Component scaling support
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 17 Jul 2011 21:03:07 +0000 (21:03 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 17 Jul 2011 21:03:07 +0000 (21:03 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@135 180e2498-e6e9-4542-8430-84ac67f01cd8

22 files changed:
ChangeLog
l10n/messages.properties
src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
src/net/sf/openrocket/gui/adaptors/DoubleModel.java
src/net/sf/openrocket/gui/components/BasicTree.java [new file with mode: 0644]
src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
src/net/sf/openrocket/gui/dialogs/ScaleDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java [new file with mode: 0644]
src/net/sf/openrocket/gui/main/BasicFrame.java
src/net/sf/openrocket/gui/main/ComponentIcons.java
src/net/sf/openrocket/gui/main/ExceptionHandler.java
src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java
src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java
src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java
src/net/sf/openrocket/rocketcomponent/BodyTube.java
src/net/sf/openrocket/rocketcomponent/LaunchLug.java
src/net/sf/openrocket/rocketcomponent/RingComponent.java
src/net/sf/openrocket/util/Reflection.java
svn-commit.tmp [deleted file]
test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java

index 074e24f8c83d7a62ba665f15651a1507f04e3607..cd9cd697bdbceeea150ea03383c7f5fe8477dc40 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-07-17  Sampo Niskanen
+
+       * Component scaling support
+
 2011-07-14  Sampo Niskanen
 
        * [BUG] Body tube auto-radius not enabled
index 7f44c7220758bf4ec2f16252195f62e4129644eb..d44c1f6925650a03ea51373362305c6f01d156e4 100644 (file)
@@ -56,14 +56,6 @@ BasicFrame.SimpleFileFilter3 = RockSim designs (*.rkt)
 BasicFrame.tab.Rocketdesign = Rocket design
 BasicFrame.tab.Flightsim = Flight simulations
 BasicFrame.title.Addnewcomp = Add new component
-BasicFrame.item.Openrocketdesign = Open a rocket design
-BasicFrame.item.Openexamplerocketdesign = Open an example rocket design
-BasicFrame.item.SavecurRocketdesign = Save the current rocket design
-BasicFrame.item.SavecurRocketdesnewfile = Save the current rocket design to a new file
-BasicFrame.item.Printpart = Print parts list and fin template
-BasicFrame.item.Closedesign = Close the current rocket design
-BasicFrame.item.Quitprogram = Quit the program
-BasicFrame.menu.Rocketedt = Rocket editing
 BasicFrame.dlg.lbl1 = Design '
 BasicFrame.dlg.lbl2 = ' has not been saved.
 BasicFrame.dlg.lbl3 = Do you want to save it?
@@ -652,6 +644,12 @@ FreeformFinSetCfg.lbl.Posrelativeto = Position relative to:
 FreeformFinSetCfg.lbl.plus = plus
 FreeformFinSetCfg.lbl.FincrossSection = Fin cross section:
 FreeformFinSetCfg.lbl.Thickness = Thickness:
+! doubleClick1 + 2 form the message "Double-click to edit", split approximately at the middle
+FreeformFinSetConfig.lbl.doubleClick1 = Double-click
+FreeformFinSetConfig.lbl.doubleClick2 = to edit
+FreeformFinSetConfig.lbl.clickDrag = Click+drag: Add and move points
+FreeformFinSetConfig.lbl.ctrlClick = Ctrl+click: Remove point
+
 
 !InnerTubeConfig
 InnerTubeCfg.tab.Motor = Motor
@@ -899,33 +897,57 @@ PlotDialog.lbl.Chart = Click+drag down+right to zoom in, up+left to zoom out
 
 ! "main" prefix is used for the main application dialog
 
+# FIXME: Rename the description keys 
+
 main.menu.file = File
+main.menu.file.desc = File-handling related tasks
 main.menu.file.new = New
+main.menu.file.new.desc = Create a new rocket design
 main.menu.file.open = Open...
+BasicFrame.item.Openrocketdesign = Open a rocket design
 main.menu.file.openExample = Open example...
+BasicFrame.item.Openexamplerocketdesign = Open an example rocket design
 main.menu.file.save = Save
+BasicFrame.item.SavecurRocketdesign = Save the current rocket design
 main.menu.file.saveAs = Save as...
+BasicFrame.item.SavecurRocketdesnewfile = Save the current rocket design to a new file
 main.menu.file.print = Print / Export PDF...
+main.menu.file.print.desc = Print or save as PDF the parts list and fin templates
 main.menu.file.close = Close
+BasicFrame.item.Closedesign = Close the current rocket design
 main.menu.file.quit = Quit
+BasicFrame.item.Quitprogram = Quit the program
 
 main.menu.edit = Edit
+BasicFrame.menu.Rocketedt = Rocket editing
 main.menu.edit.undo = Undo
+main.menu.edit.undo.desc = Undo the previous operation
 main.menu.edit.redo = Redo
+main.menu.edit.redo.desc = Redo the previously undone operation
 main.menu.edit.cut = Cut
 main.menu.edit.copy = Copy
 main.menu.edit.paste = Paste
 main.menu.edit.delete = Delete
+main.menu.edit.resize = Scale...
+main.menu.edit.resize.desc = Scale parts of the rocket design
 main.menu.edit.preferences = Preferences
+main.menu.edit.preferences.desc = Setup the application preferences
 
 main.menu.analyze = Analyze
+main.menu.analyze.desc = Rocket analysis
 main.menu.analyze.componentAnalysis = Component analysis
+main.menu.analyze.componentAnalysis.desc = Analyze the rocket components separately
 
 main.menu.help = Help
+main.menu.help.desc = Information about OpenRocket
 main.menu.help.license = License
+main.menu.help.license.desc = OpenRocket license information
 main.menu.help.bugReport = Bug report
+main.menu.help.bugReport.desc = Information about reporting bugs in OpenRocket
 main.menu.help.debugLog = Debug log
+main.menu.help.debugLog.desc = View the OpenRocket debug log
 main.menu.help.about = About
+main.menu.help.about.desc = Copyright details about OpenRocket
 
 main.menu.debug = Debug
 main.menu.debug.whatisthismenu = What is this menu?
@@ -1209,6 +1231,23 @@ Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING = Recovery device opened while motor s
 Warning.FILE_INVALID_PARAMETER = Invalid parameter encountered, ignoring.
 
 
+! Scale dialog
+ScaleDialog.lbl.scaleRocket = Entire rocket
+ScaleDialog.lbl.scaleSubselection = Selection and all subcomponents
+ScaleDialog.lbl.scaleSelection = Only selected component
+ScaleDialog.title = Scale design
+ScaleDialog.lbl.scale = Scale:
+ScaleDialog.lbl.scale.ttip = Select whether to scale the entire design or only the selected component
+ScaleDialog.lbl.scaling = Scaling to apply:
+ScaleDialog.lbl.scaling.ttip = Resulting size, values above 100% grow and values below 100% shrink the design.
+! The scaleFrom/scaleTo pair creates a phrase "Scale from [...] to [...]"
+ScaleDialog.lbl.scaleFrom = Scale from
+ScaleDialog.lbl.scaleTo = to
+ScaleDialog.lbl.scaleFromTo.ttip = Define the scaling based on an original and resulting length.
+ScaleDialog.checkbox.scaleMass = Update explicit mass values
+ScaleDialog.checkbox.scaleMass.ttip = Scale mass component and override mass values by the cube of the scaling factor
+ScaleDialog.button.scale = Scale
+
 ! Optimization
 
 ! Modifiers
index a487e48c3c6988b76b96bb1b1b1f9b6356437757..d6cf61607c0dff5a4ed676074d443821ec0efd55 100644 (file)
@@ -206,83 +206,83 @@ class DocumentConfig {
        static {
                // RocketComponent
                setters.put("RocketComponent:name", new StringSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setName", String.class)));
+                               Reflection.findMethod(RocketComponent.class, "setName", String.class)));
                setters.put("RocketComponent:color", new ColorSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setColor", Color.class)));
+                               Reflection.findMethod(RocketComponent.class, "setColor", Color.class)));
                setters.put("RocketComponent:linestyle", new EnumSetter<LineStyle>(
-                               Reflection.findMethodStatic(RocketComponent.class, "setLineStyle", LineStyle.class),
+                               Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class),
                                LineStyle.class));
                setters.put("RocketComponent:position", new PositionSetter());
                setters.put("RocketComponent:overridemass", new OverrideSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setOverrideMass", double.class),
-                               Reflection.findMethodStatic(RocketComponent.class, "setMassOverridden", boolean.class)));
+                               Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class),
+                               Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class)));
                setters.put("RocketComponent:overridecg", new OverrideSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setOverrideCGX", double.class),
-                               Reflection.findMethodStatic(RocketComponent.class, "setCGOverridden", boolean.class)));
+                               Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class),
+                               Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class)));
                setters.put("RocketComponent:overridesubcomponents", new BooleanSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setOverrideSubcomponents", boolean.class)));
+                               Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class)));
                setters.put("RocketComponent:comment", new StringSetter(
-                               Reflection.findMethodStatic(RocketComponent.class, "setComment", String.class)));
+                               Reflection.findMethod(RocketComponent.class, "setComment", String.class)));
                
                // ExternalComponent
                setters.put("ExternalComponent:finish", new EnumSetter<Finish>(
-                               Reflection.findMethodStatic(ExternalComponent.class, "setFinish", Finish.class),
+                               Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class),
                                Finish.class));
                setters.put("ExternalComponent:material", new MaterialSetter(
-                               Reflection.findMethodStatic(ExternalComponent.class, "setMaterial", Material.class),
+                               Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class),
                                Material.Type.BULK));
                
                // BodyComponent
                setters.put("BodyComponent:length", new DoubleSetter(
-                               Reflection.findMethodStatic(BodyComponent.class, "setLength", double.class)));
+                               Reflection.findMethod(BodyComponent.class, "setLength", double.class)));
                
                // SymmetricComponent
                setters.put("SymmetricComponent:thickness", new DoubleSetter(
-                               Reflection.findMethodStatic(SymmetricComponent.class, "setThickness", double.class),
+                               Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class),
                                "filled",
-                               Reflection.findMethodStatic(SymmetricComponent.class, "setFilled", boolean.class)));
+                               Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class)));
                
                // BodyTube
                setters.put("BodyTube:radius", new DoubleSetter(
-                               Reflection.findMethodStatic(BodyTube.class, "setOuterRadius", double.class),
+                               Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(BodyTube.class, "setOuterRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class)));
                
                // Transition
                setters.put("Transition:shape", new EnumSetter<Transition.Shape>(
-                               Reflection.findMethodStatic(Transition.class, "setType", Transition.Shape.class),
+                               Reflection.findMethod(Transition.class, "setType", Transition.Shape.class),
                                Transition.Shape.class));
                setters.put("Transition:shapeclipped", new BooleanSetter(
-                               Reflection.findMethodStatic(Transition.class, "setClipped", boolean.class)));
+                               Reflection.findMethod(Transition.class, "setClipped", boolean.class)));
                setters.put("Transition:shapeparameter", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setShapeParameter", double.class)));
+                               Reflection.findMethod(Transition.class, "setShapeParameter", double.class)));
                
                setters.put("Transition:foreradius", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setForeRadius", double.class),
+                               Reflection.findMethod(Transition.class, "setForeRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(Transition.class, "setForeRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class)));
                setters.put("Transition:aftradius", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setAftRadius", double.class),
+                               Reflection.findMethod(Transition.class, "setAftRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(Transition.class, "setAftRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class)));
                
                setters.put("Transition:foreshoulderradius", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setForeShoulderRadius", double.class)));
+                               Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class)));
                setters.put("Transition:foreshoulderlength", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setForeShoulderLength", double.class)));
+                               Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class)));
                setters.put("Transition:foreshoulderthickness", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setForeShoulderThickness", double.class)));
+                               Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class)));
                setters.put("Transition:foreshouldercapped", new BooleanSetter(
-                               Reflection.findMethodStatic(Transition.class, "setForeShoulderCapped", boolean.class)));
+                               Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class)));
                
                setters.put("Transition:aftshoulderradius", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setAftShoulderRadius", double.class)));
+                               Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class)));
                setters.put("Transition:aftshoulderlength", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setAftShoulderLength", double.class)));
+                               Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class)));
                setters.put("Transition:aftshoulderthickness", new DoubleSetter(
-                               Reflection.findMethodStatic(Transition.class, "setAftShoulderThickness", double.class)));
+                               Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class)));
                setters.put("Transition:aftshouldercapped", new BooleanSetter(
-                               Reflection.findMethodStatic(Transition.class, "setAftShoulderCapped", boolean.class)));
+                               Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class)));
                
                // NoseCone - disable disallowed elements
                setters.put("NoseCone:foreradius", null);
@@ -293,180 +293,180 @@ class DocumentConfig {
                
                // FinSet
                setters.put("FinSet:fincount", new IntSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setFinCount", int.class)));
+                               Reflection.findMethod(FinSet.class, "setFinCount", int.class)));
                setters.put("FinSet:rotation", new DoubleSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0));
+                               Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0));
                setters.put("FinSet:thickness", new DoubleSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setThickness", double.class)));
+                               Reflection.findMethod(FinSet.class, "setThickness", double.class)));
                setters.put("FinSet:crosssection", new EnumSetter<FinSet.CrossSection>(
-                               Reflection.findMethodStatic(FinSet.class, "setCrossSection", FinSet.CrossSection.class),
+                               Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class),
                                FinSet.CrossSection.class));
                setters.put("FinSet:cant", new DoubleSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0));
+                               Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0));
                setters.put("FinSet:tabheight", new DoubleSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setTabHeight", double.class)));
+                               Reflection.findMethod(FinSet.class, "setTabHeight", double.class)));
                setters.put("FinSet:tablength", new DoubleSetter(
-                               Reflection.findMethodStatic(FinSet.class, "setTabLength", double.class)));
+                               Reflection.findMethod(FinSet.class, "setTabLength", double.class)));
                setters.put("FinSet:tabposition", new FinTabPositionSetter());
                
                // TrapezoidFinSet
                setters.put("TrapezoidFinSet:rootchord", new DoubleSetter(
-                               Reflection.findMethodStatic(TrapezoidFinSet.class, "setRootChord", double.class)));
+                               Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class)));
                setters.put("TrapezoidFinSet:tipchord", new DoubleSetter(
-                               Reflection.findMethodStatic(TrapezoidFinSet.class, "setTipChord", double.class)));
+                               Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class)));
                setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter(
-                               Reflection.findMethodStatic(TrapezoidFinSet.class, "setSweep", double.class)));
+                               Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class)));
                setters.put("TrapezoidFinSet:height", new DoubleSetter(
-                               Reflection.findMethodStatic(TrapezoidFinSet.class, "setHeight", double.class)));
+                               Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class)));
                
                // EllipticalFinSet
                setters.put("EllipticalFinSet:rootchord", new DoubleSetter(
-                               Reflection.findMethodStatic(EllipticalFinSet.class, "setLength", double.class)));
+                               Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class)));
                setters.put("EllipticalFinSet:height", new DoubleSetter(
-                               Reflection.findMethodStatic(EllipticalFinSet.class, "setHeight", double.class)));
+                               Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class)));
                
                // FreeformFinSet points handled as a special handler
                
                // LaunchLug
                setters.put("LaunchLug:radius", new DoubleSetter(
-                               Reflection.findMethodStatic(LaunchLug.class, "setOuterRadius", double.class)));
+                               Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class)));
                setters.put("LaunchLug:length", new DoubleSetter(
-                               Reflection.findMethodStatic(LaunchLug.class, "setLength", double.class)));
+                               Reflection.findMethod(LaunchLug.class, "setLength", double.class)));
                setters.put("LaunchLug:thickness", new DoubleSetter(
-                               Reflection.findMethodStatic(LaunchLug.class, "setThickness", double.class)));
+                               Reflection.findMethod(LaunchLug.class, "setThickness", double.class)));
                setters.put("LaunchLug:radialdirection", new DoubleSetter(
-                               Reflection.findMethodStatic(LaunchLug.class, "setRadialDirection", double.class),
+                               Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class),
                                Math.PI / 180.0));
                
                // InternalComponent - nothing
                
                // StructuralComponent
                setters.put("StructuralComponent:material", new MaterialSetter(
-                               Reflection.findMethodStatic(StructuralComponent.class, "setMaterial", Material.class),
+                               Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class),
                                Material.Type.BULK));
                
                // RingComponent
                setters.put("RingComponent:length", new DoubleSetter(
-                               Reflection.findMethodStatic(RingComponent.class, "setLength", double.class)));
+                               Reflection.findMethod(RingComponent.class, "setLength", double.class)));
                setters.put("RingComponent:radialposition", new DoubleSetter(
-                               Reflection.findMethodStatic(RingComponent.class, "setRadialPosition", double.class)));
+                               Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class)));
                setters.put("RingComponent:radialdirection", new DoubleSetter(
-                               Reflection.findMethodStatic(RingComponent.class, "setRadialDirection", double.class),
+                               Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class),
                                Math.PI / 180.0));
                
                // ThicknessRingComponent - radius on separate components due to differing automatics
                setters.put("ThicknessRingComponent:thickness", new DoubleSetter(
-                               Reflection.findMethodStatic(ThicknessRingComponent.class, "setThickness", double.class)));
+                               Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class)));
                
                // EngineBlock
                setters.put("EngineBlock:outerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(EngineBlock.class, "setOuterRadius", double.class),
+                               Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class)));
                
                // TubeCoupler
                setters.put("TubeCoupler:outerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadius", double.class),
+                               Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class)));
                
                // InnerTube
                setters.put("InnerTube:outerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(InnerTube.class, "setOuterRadius", double.class)));
+                               Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class)));
                setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter());
                setters.put("InnerTube:clusterscale", new DoubleSetter(
-                               Reflection.findMethodStatic(InnerTube.class, "setClusterScale", double.class)));
+                               Reflection.findMethod(InnerTube.class, "setClusterScale", double.class)));
                setters.put("InnerTube:clusterrotation", new DoubleSetter(
-                               Reflection.findMethodStatic(InnerTube.class, "setClusterRotation", double.class),
+                               Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class),
                                Math.PI / 180.0));
                
                // RadiusRingComponent
                
                // Bulkhead
                setters.put("RadiusRingComponent:innerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(RadiusRingComponent.class, "setInnerRadius", double.class)));
+                               Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class)));
                setters.put("Bulkhead:outerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(Bulkhead.class, "setOuterRadius", double.class),
+                               Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class)));
                
                // CenteringRing
                setters.put("CenteringRing:innerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(CenteringRing.class, "setInnerRadius", double.class),
+                               Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class)));
                setters.put("CenteringRing:outerradius", new DoubleSetter(
-                               Reflection.findMethodStatic(CenteringRing.class, "setOuterRadius", double.class),
+                               Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class),
                                "auto",
-                               Reflection.findMethodStatic(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class)));
+                               Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class)));
                
 
                // MassObject
                setters.put("MassObject:packedlength", new DoubleSetter(
-                               Reflection.findMethodStatic(MassObject.class, "setLength", double.class)));
+                               Reflection.findMethod(MassObject.class, "setLength", double.class)));
                setters.put("MassObject:packedradius", new DoubleSetter(
-                               Reflection.findMethodStatic(MassObject.class, "setRadius", double.class)));
+                               Reflection.findMethod(MassObject.class, "setRadius", double.class)));
                setters.put("MassObject:radialposition", new DoubleSetter(
-                               Reflection.findMethodStatic(MassObject.class, "setRadialPosition", double.class)));
+                               Reflection.findMethod(MassObject.class, "setRadialPosition", double.class)));
                setters.put("MassObject:radialdirection", new DoubleSetter(
-                               Reflection.findMethodStatic(MassObject.class, "setRadialDirection", double.class),
+                               Reflection.findMethod(MassObject.class, "setRadialDirection", double.class),
                                Math.PI / 180.0));
                
                // MassComponent
                setters.put("MassComponent:mass", new DoubleSetter(
-                               Reflection.findMethodStatic(MassComponent.class, "setComponentMass", double.class)));
+                               Reflection.findMethod(MassComponent.class, "setComponentMass", double.class)));
                
                // ShockCord
                setters.put("ShockCord:cordlength", new DoubleSetter(
-                               Reflection.findMethodStatic(ShockCord.class, "setCordLength", double.class)));
+                               Reflection.findMethod(ShockCord.class, "setCordLength", double.class)));
                setters.put("ShockCord:material", new MaterialSetter(
-                               Reflection.findMethodStatic(ShockCord.class, "setMaterial", Material.class),
+                               Reflection.findMethod(ShockCord.class, "setMaterial", Material.class),
                                Material.Type.LINE));
                
                // RecoveryDevice
                setters.put("RecoveryDevice:cd", new DoubleSetter(
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setCD", double.class),
+                               Reflection.findMethod(RecoveryDevice.class, "setCD", double.class),
                                "auto",
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setCDAutomatic", boolean.class)));
+                               Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class)));
                setters.put("RecoveryDevice:deployevent", new EnumSetter<RecoveryDevice.DeployEvent>(
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class),
+                               Reflection.findMethod(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class),
                                RecoveryDevice.DeployEvent.class));
                setters.put("RecoveryDevice:deployaltitude", new DoubleSetter(
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setDeployAltitude", double.class)));
+                               Reflection.findMethod(RecoveryDevice.class, "setDeployAltitude", double.class)));
                setters.put("RecoveryDevice:deploydelay", new DoubleSetter(
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setDeployDelay", double.class)));
+                               Reflection.findMethod(RecoveryDevice.class, "setDeployDelay", double.class)));
                setters.put("RecoveryDevice:material", new MaterialSetter(
-                               Reflection.findMethodStatic(RecoveryDevice.class, "setMaterial", Material.class),
+                               Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class),
                                Material.Type.SURFACE));
                
                // Parachute
                setters.put("Parachute:diameter", new DoubleSetter(
-                               Reflection.findMethodStatic(Parachute.class, "setDiameter", double.class)));
+                               Reflection.findMethod(Parachute.class, "setDiameter", double.class)));
                setters.put("Parachute:linecount", new IntSetter(
-                               Reflection.findMethodStatic(Parachute.class, "setLineCount", int.class)));
+                               Reflection.findMethod(Parachute.class, "setLineCount", int.class)));
                setters.put("Parachute:linelength", new DoubleSetter(
-                               Reflection.findMethodStatic(Parachute.class, "setLineLength", double.class)));
+                               Reflection.findMethod(Parachute.class, "setLineLength", double.class)));
                setters.put("Parachute:linematerial", new MaterialSetter(
-                               Reflection.findMethodStatic(Parachute.class, "setLineMaterial", Material.class),
+                               Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class),
                                Material.Type.LINE));
                
                // Streamer
                setters.put("Streamer:striplength", new DoubleSetter(
-                               Reflection.findMethodStatic(Streamer.class, "setStripLength", double.class)));
+                               Reflection.findMethod(Streamer.class, "setStripLength", double.class)));
                setters.put("Streamer:stripwidth", new DoubleSetter(
-                               Reflection.findMethodStatic(Streamer.class, "setStripWidth", double.class)));
+                               Reflection.findMethod(Streamer.class, "setStripWidth", double.class)));
                
                // Rocket
                // <motorconfiguration> handled by separate handler
                setters.put("Rocket:referencetype", new EnumSetter<ReferenceType>(
-                               Reflection.findMethodStatic(Rocket.class, "setReferenceType", ReferenceType.class),
+                               Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class),
                                ReferenceType.class));
                setters.put("Rocket:customreference", new DoubleSetter(
-                               Reflection.findMethodStatic(Rocket.class, "setCustomReferenceLength", double.class)));
+                               Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class)));
                setters.put("Rocket:designer", new StringSetter(
-                               Reflection.findMethodStatic(Rocket.class, "setDesigner", String.class)));
+                               Reflection.findMethod(Rocket.class, "setDesigner", String.class)));
                setters.put("Rocket:revision", new StringSetter(
-                               Reflection.findMethodStatic(Rocket.class, "setRevision", String.class)));
+                               Reflection.findMethod(Rocket.class, "setRevision", String.class)));
        }
        
        
@@ -1993,7 +1993,7 @@ class PositionSetter implements Setter {
 class FinTabPositionSetter extends DoubleSetter {
        
        public FinTabPositionSetter() {
-               super(Reflection.findMethodStatic(FinSet.class, "setTabShift", double.class));
+               super(Reflection.findMethod(FinSet.class, "setTabShift", double.class));
        }
        
        @Override
index 7b3783a03cea624fb3ddc0bdc17b1e3b50701e1f..ce81e1076ff4b3f31b8656cbdf7a25d200fb1181 100644 (file)
@@ -528,18 +528,44 @@ public class DoubleModel implements ChangeListener, ChangeSource, Invalidatable
        private Invalidator invalidator = new Invalidator(this);
        
        
+       /**
+        * Generate a DoubleModel that contains an internal double value.
+        * 
+        * @param value         the initial value.
+        */
        public DoubleModel(double value) {
                this(value, UnitGroup.UNITS_NONE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
        
+       /**
+        * Generate a DoubleModel that contains an internal double value.
+        * 
+        * @param value         the initial value.
+        * @param unit          the unit for the value.
+        */
        public DoubleModel(double value, UnitGroup unit) {
                this(value, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
        
+       /**
+        * Generate a DoubleModel that contains an internal double value.
+        * 
+        * @param value         the initial value.
+        * @param unit          the unit for the value.
+        * @param min           minimum value.
+        */
        public DoubleModel(double value, UnitGroup unit, double min) {
                this(value, unit, min, Double.POSITIVE_INFINITY);
        }
        
+       /**
+        * Generate a DoubleModel that contains an internal double value.
+        * 
+        * @param value         the initial value.
+        * @param unit          the unit for the value.
+        * @param min           minimum value.
+        * @param max           maximum value.
+        */
        public DoubleModel(double value, UnitGroup unit, double min, double max) {
                this.lastValue = value;
                this.minValue = min;
diff --git a/src/net/sf/openrocket/gui/components/BasicTree.java b/src/net/sf/openrocket/gui/components/BasicTree.java
new file mode 100644 (file)
index 0000000..6ec8804
--- /dev/null
@@ -0,0 +1,145 @@
+package net.sf.openrocket.gui.components;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+public class BasicTree extends JTree {
+       
+
+       public BasicTree() {
+               super();
+               setDefaultOptions();
+       }
+       
+       public BasicTree(TreeNode node) {
+               super(node);
+               setDefaultOptions();
+       }
+       
+       
+       private void setDefaultOptions() {
+               this.setToggleClickCount(0);
+               
+               javax.swing.plaf.basic.BasicTreeUI plainUI = new javax.swing.plaf.basic.BasicTreeUI();
+               this.setUI(plainUI);
+               plainUI.setExpandedIcon(TreeIcon.MINUS);
+               plainUI.setCollapsedIcon(TreeIcon.PLUS);
+               plainUI.setLeftChildIndent(15);
+               
+
+               this.setBackground(Color.WHITE);
+               this.setShowsRootHandles(false);
+       }
+       
+       
+       /**
+        * Expand the entire tree structure.  All nodes will be visible after the call.
+        */
+       public void expandTree() {
+               for (int i = 0; i < getRowCount(); i++)
+                       expandRow(i);
+       }
+       
+       
+
+
+       @Override
+       public void treeDidChange() {
+               super.treeDidChange();
+               /*
+                * Expand the childless nodes to prevent leaf nodes from looking expandable.
+                */
+               expandChildlessNodes();
+       }
+       
+       /**
+        * Expand all nodes in the tree that are visible and have no children.  This can be used
+        * to avoid the situation where a non-leaf node is marked as being expandable, but when
+        * expanding it it has no children.
+        */
+       private void expandChildlessNodes() {
+               TreeModel model = this.getModel();
+               if (model == null) {
+                       return;
+               }
+               Object root = model.getRoot();
+               expandChildlessNodes(model, new TreePath(root));
+       }
+       
+       private void expandChildlessNodes(TreeModel model, TreePath path) {
+               Object object = path.getLastPathComponent();
+               if (this.isVisible(path)) {
+                       int count = model.getChildCount(object);
+                       if (count == 0) {
+                               this.expandPath(path);
+                       }
+                       for (int i = 0; i < count; i++) {
+                               expandChildlessNodes(model, path.pathByAddingChild(model.getChild(object, i)));
+                       }
+               }
+       }
+       
+       
+
+       /**
+        * Plain-looking tree expand/collapse icons.
+        */
+       private static class TreeIcon implements Icon {
+               public static final Icon PLUS = new TreeIcon(true);
+               public static final Icon MINUS = new TreeIcon(false);
+               
+               // Implementation:
+               
+               private final static int width = 9;
+               private final static int height = 9;
+               private final static BasicStroke stroke = new BasicStroke(2);
+               private boolean plus;
+               
+               private TreeIcon(boolean plus) {
+                       this.plus = plus;
+               }
+               
+               @Override
+               public void paintIcon(Component c, Graphics g, int x, int y) {
+                       Graphics2D g2 = (Graphics2D) g.create();
+                       
+                       // Background
+                       g2.setColor(Color.WHITE);
+                       g2.fillRect(x, y, width, height);
+                       
+                       // Border
+                       g2.setColor(Color.DARK_GRAY);
+                       g2.drawRect(x, y, width, height);
+                       
+                       // Horizontal stroke
+                       g2.setStroke(stroke);
+                       g2.drawLine(x + 3, y + (height + 1) / 2, x + width - 2, y + (height + 1) / 2);
+                       
+                       // Vertical stroke
+                       if (plus) {
+                               g2.drawLine(x + (width + 1) / 2, y + 3, x + (width + 1) / 2, y + height - 2);
+                       }
+                       
+                       g2.dispose();
+               }
+               
+               @Override
+               public int getIconWidth() {
+                       return width;
+               }
+               
+               @Override
+               public int getIconHeight() {
+                       return height;
+               }
+       }
+}
index 87be5fb40592fd06546250371e255c7596b410f5..06c877c7fa94810f31866d711b5bdce19777d78c 100644 (file)
@@ -28,6 +28,7 @@ import net.sf.openrocket.gui.scalefigure.FinPointFigure;
 import net.sf.openrocket.gui.scalefigure.ScaleScrollPane;
 import net.sf.openrocket.gui.scalefigure.ScaleSelector;
 import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.material.Material;
 import net.sf.openrocket.rocketcomponent.FinSet;
 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
@@ -38,24 +39,26 @@ import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Coordinate;
 
 public class FreeformFinSetConfig extends FinSetConfig {
-
+       
+       private static final LogHelper log = Application.getLogger();
+       private static final Translator trans = Application.getTranslator();
+       
        private final FreeformFinSet finset;
        private JTable table = null;
        private FinPointTableModel tableModel = null;
        
        private FinPointFigure figure = null;
-       private static final Translator trans = Application.getTranslator();
-
+       
        
        public FreeformFinSetConfig(RocketComponent component) {
                super(component);
-               this.finset = (FreeformFinSet)component;
-
+               this.finset = (FreeformFinSet) component;
+               
                //// General and General properties
-               tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.General"), null, generalPane(), 
+               tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.General"), null, generalPane(),
                                trans.get("FreeformFinSetCfg.tab.ttip.General"), 0);
                //// Shape and Fin shape
-               tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.Shape"), null, shapePane(), 
+               tabbedPane.insertTab(trans.get("FreeformFinSetCfg.tab.Shape"), null, shapePane(),
                                trans.get("FreeformFinSetCfg.tab.ttip.Finshape"), 1);
                tabbedPane.setSelectedIndex(0);
                
@@ -63,43 +66,43 @@ public class FreeformFinSetConfig extends FinSetConfig {
        }
        
        
-       
-       private JPanel generalPane() {
 
+       private JPanel generalPane() {
+               
                DoubleModel m;
                JSpinner spin;
                JComboBox combo;
                
                JPanel mainPanel = new JPanel(new MigLayout("fill"));
                
-               JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel","[][65lp::][30lp::]",""));
-               
-               
+               JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel", "[][65lp::][30lp::]", ""));
                
+
+
                ////  Number of fins:
                panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Numberoffins")));
                
-               IntegerModel im = new IntegerModel(component,"FinCount",1,8);
+               IntegerModel im = new IntegerModel(component, "FinCount", 1, 8);
                
                spin = new JSpinner(im.getSpinnerModel());
                spin.setEditor(new SpinnerEditor(spin));
-               panel.add(spin,"growx, wrap");
-               
+               panel.add(spin, "growx, wrap");
                
+
                ////  Base rotation
                panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Finrotation")));
                
-               m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE,-Math.PI,Math.PI);
+               m = new DoubleModel(component, "BaseRotation", UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI);
                
                spin = new JSpinner(m.getSpinnerModel());
                spin.setEditor(new SpinnerEditor(spin));
-               panel.add(spin,"growx");
-               
-               panel.add(new UnitSelector(m),"growx");
-               panel.add(new BasicSlider(m.getSliderModel(-Math.PI,Math.PI)),"w 100lp, wrap");
+               panel.add(spin, "growx");
                
+               panel.add(new UnitSelector(m), "growx");
+               panel.add(new BasicSlider(m.getSliderModel(-Math.PI, Math.PI)), "w 100lp, wrap");
                
 
+
                ////  Fin cant
                JLabel label = new JLabel(trans.get("FreeformFinSetCfg.lbl.Fincant"));
                //// The angle that the fins are canted with respect to the rocket body.
@@ -107,135 +110,135 @@ public class FreeformFinSetConfig extends FinSetConfig {
                panel.add(label);
                
                m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
-                               -FinSet.MAX_CANT,FinSet.MAX_CANT);
+                               -FinSet.MAX_CANT, FinSet.MAX_CANT);
                
                spin = new JSpinner(m.getSpinnerModel());
                spin.setEditor(new SpinnerEditor(spin));
-               panel.add(spin,"growx");
+               panel.add(spin, "growx");
                
-               panel.add(new UnitSelector(m),"growx");
-               panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT,FinSet.MAX_CANT)),
+               panel.add(new UnitSelector(m), "growx");
+               panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)),
                                "w 100lp, wrap 40lp");
                
-               
-               
+
+
                ////  Position
                //// Position relative to:
-               panel.add(new JLabel("FreeformFinSetCfg.lbl.Posrelativeto"));
-
+               panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Posrelativeto")));
+               
                combo = new JComboBox(
                                new EnumModel<RocketComponent.Position>(component, "RelativePosition",
                                                new RocketComponent.Position[] {
-                                               RocketComponent.Position.TOP,
-                                               RocketComponent.Position.MIDDLE,
-                                               RocketComponent.Position.BOTTOM,
-                                               RocketComponent.Position.ABSOLUTE
+                                                               RocketComponent.Position.TOP,
+                                                               RocketComponent.Position.MIDDLE,
+                                                               RocketComponent.Position.BOTTOM,
+                                                               RocketComponent.Position.ABSOLUTE
                                }));
-               panel.add(combo,"spanx 3, growx, wrap");
-                               //// plus
-               panel.add(new JLabel("FreeformFinSetCfg.lbl.plus"),"right");
-
-               m = new DoubleModel(component,"PositionValue",UnitGroup.UNITS_LENGTH);
+               panel.add(combo, "spanx 3, growx, wrap");
+               //// plus
+               panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.plus")), "right");
+               
+               m = new DoubleModel(component, "PositionValue", UnitGroup.UNITS_LENGTH);
                spin = new JSpinner(m.getSpinnerModel());
                spin.setEditor(new SpinnerEditor(spin));
-               panel.add(spin,"growx");
+               panel.add(spin, "growx");
                
-               panel.add(new UnitSelector(m),"growx");
+               panel.add(new UnitSelector(m), "growx");
                panel.add(new BasicSlider(m.getSliderModel(
                                new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
                                new DoubleModel(component.getParent(), "Length"))),
                                "w 100lp, wrap");
-
-               
-               
                
 
+
+
+
                mainPanel.add(panel, "aligny 20%");
                mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
                
+
+               panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
                
-               panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
-               
-               
-               
-               
+
+
+
                ////  Cross section
                //// Fin cross section:
-               panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")),"span, split");
+               panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")), "span, split");
                combo = new JComboBox(
-                               new EnumModel<FinSet.CrossSection>(component,"CrossSection"));
-               panel.add(combo,"growx, wrap unrel");
+                               new EnumModel<FinSet.CrossSection>(component, "CrossSection"));
+               panel.add(combo, "growx, wrap unrel");
                
 
                ////  Thickness:
                panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Thickness")));
                
-               m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
+               m = new DoubleModel(component, "Thickness", UnitGroup.UNITS_LENGTH, 0);
                
                spin = new JSpinner(m.getSpinnerModel());
                spin.setEditor(new SpinnerEditor(spin));
-               panel.add(spin,"growx");
+               panel.add(spin, "growx");
                
-               panel.add(new UnitSelector(m),"growx");
-               panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
+               panel.add(new UnitSelector(m), "growx");
+               panel.add(new BasicSlider(m.getSliderModel(0, 0.01)), "w 100lp, wrap 30lp");
                
 
                //// Material
                materialPanel(panel, Material.Type.BULK);
                
-               
-               
+
+
                mainPanel.add(panel, "aligny 20%");
                
                return mainPanel;
        }
        
        
-       
+
        private JPanel shapePane() {
                JPanel panel = new JPanel(new MigLayout("fill"));
                
-               
+
                // Create the figure
                figure = new FinPointFigure(finset);
                ScaleScrollPane figurePane = new FinPointScrollPane();
                figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
                figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
-
+               
                // Create the table
                tableModel = new FinPointTableModel();
                table = new JTable(tableModel);
                table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               for (int i=0; i < Columns.values().length; i++) {
+               for (int i = 0; i < Columns.values().length; i++) {
                        table.getColumnModel().getColumn(i).
-                               setPreferredWidth(Columns.values()[i].getWidth());
+                                       setPreferredWidth(Columns.values()[i].getWidth());
                }
                JScrollPane tablePane = new JScrollPane(table);
                
+
+               //              panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
+               //              panel.add(new JLabel("    View:"), "wrap, aligny bottom");
                
-//             panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
-//             panel.add(new JLabel("    View:"), "wrap, aligny bottom");
-               
-               
-               panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:");
-               panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
-               
-               panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
+
+               panel.add(tablePane, "growy, width 100lp:100lp:, height 100lp:250lp:");
+               panel.add(figurePane, "gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
                
-               panel.add(new ScaleSelector(figurePane),"spany 2");
-               panel.add(new StyledLabel("Click+drag: Add and move points   " +
-                               "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
+               panel.add(new StyledLabel(trans.get("lbl.doubleClick1"), -2), "alignx 50%");
                
+               panel.add(new ScaleSelector(figurePane), "spany 2");
+               panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.clickDrag") + "   " +
+                               trans.get("FreeformFinSetConfig.lbl.ctrlClick"), -2), "spany 2, right, wrap");
                
-               panel.add(new StyledLabel("to edit", -2), "alignx 50%");
+
+               panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.doubleClick2"), -2), "alignx 50%");
                
                return panel;
        }
        
        
-       
-       
-       
+
+
+
        @Override
        public void updateFields() {
                super.updateFields();
@@ -249,20 +252,20 @@ public class FreeformFinSetConfig extends FinSetConfig {
        }
        
        
-       
-       
+
+
        private class FinPointScrollPane extends ScaleScrollPane {
-               private static final int ANY_MASK = 
-                       (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK | 
-                                       MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK | 
+               private static final int ANY_MASK =
+                               (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK |
+                                               MouseEvent.META_DOWN_MASK | MouseEvent.CTRL_DOWN_MASK |
                                        MouseEvent.SHIFT_DOWN_MASK);
                
                private int dragIndex = -1;
                
                public FinPointScrollPane() {
-                       super(figure, false);   // Disallow fitting as it's buggy
+                       super(figure, false); // Disallow fitting as it's buggy
                }
-
+               
                @Override
                public void mousePressed(MouseEvent event) {
                        int mods = event.getModifiersEx();
@@ -284,7 +287,8 @@ public class FreeformFinSetConfig extends FinSetConfig {
                                finset.addPoint(index);
                                try {
                                        finset.setPoint(index, point.x, point.y);
-                               } catch (IllegalFinPointException ignore) { }
+                               } catch (IllegalFinPointException ignore) {
+                               }
                                dragIndex = index;
                                
                                return;
@@ -293,13 +297,13 @@ public class FreeformFinSetConfig extends FinSetConfig {
                        super.mousePressed(event);
                        return;
                }
-
+               
                
                @Override
                public void mouseDragged(MouseEvent event) {
                        int mods = event.getModifiersEx();
                        if (dragIndex < 0 ||
-                                       (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) != 
+                                       (mods & (ANY_MASK | MouseEvent.BUTTON1_DOWN_MASK)) !=
                                                MouseEvent.BUTTON1_DOWN_MASK) {
                                super.mouseDragged(event);
                                return;
@@ -309,7 +313,8 @@ public class FreeformFinSetConfig extends FinSetConfig {
                        try {
                                finset.setPoint(dragIndex, point.x, point.y);
                        } catch (IllegalFinPointException ignore) {
-                               System.out.println("IAE:"+ignore);
+                               log.debug("Ignoring IllegalFinPointException while dragging, dragIndex=" + dragIndex +
+                                               " x=" + point.x + " y=" + point.y);
                        }
                }
                
@@ -334,7 +339,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                                super.mouseClicked(event);
                                return;
                        }
-
+                       
                        try {
                                finset.removePoint(index);
                        } catch (IllegalFinPointException ignore) {
@@ -369,54 +374,58 @@ public class FreeformFinSetConfig extends FinSetConfig {
                        return figure.convertPoint(x, y);
                }
                
-               
+
        }
        
        
 
-       
-       
+
+
        private enum Columns {
-//             NUMBER {
-//                     @Override
-//                     public String toString() {
-//                             return "#";
-//                     }
-//                     @Override
-//                     public String getValue(FreeformFinSet finset, int row) {
-//                             return "" + (row+1) + ".";
-//                     }
-//                     @Override
-//                     public int getWidth() {
-//                             return 10;
-//                     }
-//             }, 
+               //              NUMBER {
+               //                      @Override
+               //                      public String toString() {
+               //                              return "#";
+               //                      }
+               //                      @Override
+               //                      public String getValue(FreeformFinSet finset, int row) {
+               //                              return "" + (row+1) + ".";
+               //                      }
+               //                      @Override
+               //                      public int getWidth() {
+               //                              return 10;
+               //                      }
+               //              }, 
                X {
                        @Override
                        public String toString() {
                                return "X / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
                        }
+                       
                        @Override
                        public String getValue(FreeformFinSet finset, int row) {
                                return UnitGroup.UNITS_LENGTH.getDefaultUnit()
-                                       .toString(finset.getFinPoints()[row].x);
+                                               .toString(finset.getFinPoints()[row].x);
                        }
-               }, 
+               },
                Y {
                        @Override
                        public String toString() {
                                return "Y / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toString();
                        }
+                       
                        @Override
                        public String getValue(FreeformFinSet finset, int row) {
                                return UnitGroup.UNITS_LENGTH.getDefaultUnit()
-                                       .toString(finset.getFinPoints()[row].y);
+                                               .toString(finset.getFinPoints()[row].y);
                        }
                };
                
                public abstract String getValue(FreeformFinSet finset, int row);
+               
                @Override
                public abstract String toString();
+               
                public int getWidth() {
                        return 20;
                }
@@ -428,12 +437,12 @@ public class FreeformFinSetConfig extends FinSetConfig {
                public int getColumnCount() {
                        return Columns.values().length;
                }
-
+               
                @Override
                public int getRowCount() {
                        return finset.getPointCount();
                }
-
+               
                @Override
                public Object getValueAt(int rowIndex, int columnIndex) {
                        return Columns.values()[columnIndex].getValue(finset, rowIndex);
@@ -446,7 +455,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                
                @Override
                public boolean isCellEditable(int rowIndex, int columnIndex) {
-                       if (rowIndex == 0 || rowIndex == getRowCount()-1) {
+                       if (rowIndex == 0 || rowIndex == getRowCount() - 1) {
                                return (columnIndex == Columns.X.ordinal());
                        }
                        
@@ -460,11 +469,11 @@ public class FreeformFinSetConfig extends FinSetConfig {
                        
                        if (rowIndex < 0 || rowIndex >= finset.getFinPoints().length ||
                                        columnIndex < 0 || columnIndex >= Columns.values().length) {
-                               throw new IllegalArgumentException("Index out of bounds, row="+rowIndex+
-                                               " column="+columnIndex+" fin point count="+finset.getFinPoints().length);
+                               throw new IllegalArgumentException("Index out of bounds, row=" + rowIndex +
+                                               " column=" + columnIndex + " fin point count=" + finset.getFinPoints().length);
                        }
                        
-                       String str = (String)o;
+                       String str = (String) o;
                        try {
                                
                                double value = UnitGroup.UNITS_LENGTH.fromString(str);
@@ -475,7 +484,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                                        c = c.setY(value);
                                
                                finset.setPoint(rowIndex, c.x, c.y);
-                       
+                               
                        } catch (NumberFormatException ignore) {
                        } catch (IllegalFinPointException ignore) {
                        }
diff --git a/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
new file mode 100644 (file)
index 0000000..d53a1a5
--- /dev/null
@@ -0,0 +1,609 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.SpinnerEditor;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.BodyComponent;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.MassComponent;
+import net.sf.openrocket.rocketcomponent.MassObject;
+import net.sf.openrocket.rocketcomponent.Parachute;
+import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
+import net.sf.openrocket.rocketcomponent.RingComponent;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.ShockCord;
+import net.sf.openrocket.rocketcomponent.Streamer;
+import net.sf.openrocket.rocketcomponent.SymmetricComponent;
+import net.sf.openrocket.rocketcomponent.ThicknessRingComponent;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.Reflection;
+import net.sf.openrocket.util.Reflection.Method;
+
+/**
+ * Dialog that allows scaling the rocket design.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ScaleDialog extends JDialog {
+       
+       private static final LogHelper log = Application.getLogger();
+       private static final Translator trans = Application.getTranslator();
+       
+
+       /*
+        * Scaler implementations
+        * 
+        * Each scaled value (except override cg/mass) is defined using a Scaler instance.
+        */
+       private static final Map<Class<? extends RocketComponent>, List<Scaler>> SCALERS =
+                       new HashMap<Class<? extends RocketComponent>, List<Scaler>>();
+       static {
+               List<Scaler> list;
+               
+               // RocketComponent
+               addScaler(RocketComponent.class, "PositionValue");
+               SCALERS.get(RocketComponent.class).add(new OverrideScaler());
+               
+               // BodyComponent
+               addScaler(BodyComponent.class, "Length");
+               
+               // SymmetricComponent
+               addScaler(SymmetricComponent.class, "Thickness", "isFilled");
+               
+               // Transition + Nose cone
+               addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic");
+               addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic");
+               addScaler(Transition.class, "ForeShoulderRadius");
+               addScaler(Transition.class, "ForeShoulderThickness");
+               addScaler(Transition.class, "ForeShoulderLength");
+               addScaler(Transition.class, "AftShoulderRadius");
+               addScaler(Transition.class, "AftShoulderThickness");
+               addScaler(Transition.class, "AftShoulderLength");
+               
+               // Body tube
+               addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic");
+               addScaler(BodyTube.class, "MotorOverhang");
+               
+               // Launch lug
+               addScaler(LaunchLug.class, "OuterRadius");
+               addScaler(LaunchLug.class, "Thickness");
+               addScaler(LaunchLug.class, "Length");
+               
+               // FinSet
+               addScaler(FinSet.class, "Thickness");
+               addScaler(FinSet.class, "TabHeight");
+               addScaler(FinSet.class, "TabLength");
+               addScaler(FinSet.class, "TabShift");
+               
+               // TrapezoidFinSet
+               addScaler(TrapezoidFinSet.class, "Sweep");
+               addScaler(TrapezoidFinSet.class, "RootChord");
+               addScaler(TrapezoidFinSet.class, "TipChord");
+               addScaler(TrapezoidFinSet.class, "Height");
+               
+               // EllipticalFinSet
+               addScaler(EllipticalFinSet.class, "Length");
+               addScaler(EllipticalFinSet.class, "Height");
+               
+               // FreeformFinSet
+               list = new ArrayList<ScaleDialog.Scaler>(1);
+               list.add(new FreeformFinSetScaler());
+               SCALERS.put(FreeformFinSet.class, list);
+               
+               // MassObject
+               addScaler(MassObject.class, "Length");
+               addScaler(MassObject.class, "Radius");
+               addScaler(MassObject.class, "RadialPosition");
+               
+               // MassComponent
+               list = new ArrayList<ScaleDialog.Scaler>(1);
+               list.add(new MassComponentScaler());
+               SCALERS.put(MassComponent.class, list);
+               
+               // Parachute
+               addScaler(Parachute.class, "Diameter");
+               addScaler(Parachute.class, "LineLength");
+               
+               // Streamer
+               addScaler(Streamer.class, "StripLength");
+               addScaler(Streamer.class, "StripWidth");
+               
+               // ShockCord
+               addScaler(ShockCord.class, "CordLength");
+               
+               // RingComponent
+               addScaler(RingComponent.class, "Length");
+               addScaler(RingComponent.class, "RadialPosition");
+               
+               // ThicknessRingComponent
+               addScaler(ThicknessRingComponent.class, "OuterRadius", "isOuterRadiusAutomatic");
+               addScaler(ThicknessRingComponent.class, "Thickness");
+               
+               // InnerTube
+               addScaler(InnerTube.class, "MotorOverhang");
+               
+               // RadiusRingComponent
+               addScaler(RadiusRingComponent.class, "OuterRadius", "isOuterRadiusAutomatic");
+               addScaler(RadiusRingComponent.class, "InnerRadius", "isInnerRadiusAutomatic");
+       }
+       
+       private static void addScaler(Class<? extends RocketComponent> componentClass, String methodName) {
+               addScaler(componentClass, methodName, null);
+       }
+       
+       private static void addScaler(Class<? extends RocketComponent> componentClass, String methodName, String autoMethodName) {
+               List<Scaler> list = SCALERS.get(componentClass);
+               if (list == null) {
+                       list = new ArrayList<ScaleDialog.Scaler>();
+                       SCALERS.put(componentClass, list);
+               }
+               list.add(new GeneralScaler(componentClass, methodName, autoMethodName));
+       }
+       
+       
+
+
+
+       private static final double DEFAULT_INITIAL_SIZE = 0.1; // meters
+       private static final double SCALE_MIN = 0.01;
+       private static final double SCALE_MAX = 100.0;
+       
+       private static final String SCALE_ROCKET = trans.get("lbl.scaleRocket");
+       private static final String SCALE_SUBSELECTION = trans.get("lbl.scaleSubselection");
+       private static final String SCALE_SELECTION = trans.get("lbl.scaleSelection");
+       
+
+
+
+       private final DoubleModel multiplier = new DoubleModel(1.0, UnitGroup.UNITS_RELATIVE, SCALE_MIN, SCALE_MAX);
+       private final DoubleModel fromField = new DoubleModel(0, UnitGroup.UNITS_LENGTH, 0);
+       private final DoubleModel toField = new DoubleModel(0, UnitGroup.UNITS_LENGTH, 0);
+       
+       private final OpenRocketDocument document;
+       private final RocketComponent selection;
+       
+       private final JComboBox selectionOption;
+       private final JCheckBox scaleMassValues;
+       
+       private boolean changing = false;
+       
+       // FIXME: Localize
+       
+       /**
+        * Sole constructor.
+        * 
+        * @param document              the document to modify.
+        * @param selection             the currently selected component (or <code>null</code> if none selected).
+        * @param parent                the parent window.
+        */
+       public ScaleDialog(OpenRocketDocument document, RocketComponent selection, Window parent) {
+               super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
+               
+               this.document = document;
+               this.selection = selection;
+               
+               // Generate options for scaling
+               List<String> options = new ArrayList<String>();
+               options.add(SCALE_ROCKET);
+               if (selection != null && selection.getChildCount() > 0) {
+                       options.add(SCALE_SUBSELECTION);
+               }
+               if (selection != null) {
+                       options.add(SCALE_SELECTION);
+               }
+               
+
+               /*
+                * Select initial size for "from" field.
+                * 
+                * If a component is selected, either its diameter (for SymmetricComponents) or length is selected.
+                * Otherwise the maximum body diameter is selected.  As a fallback DEFAULT_INITIAL_SIZE is used.
+                */
+               // 
+               double initialSize = 0;
+               if (selection != null) {
+                       if (selection instanceof SymmetricComponent) {
+                               SymmetricComponent s = (SymmetricComponent) selection;
+                               initialSize = s.getForeRadius() * 2;
+                               initialSize = MathUtil.max(initialSize, s.getAftRadius() * 2);
+                       } else {
+                               initialSize = selection.getLength();
+                       }
+               } else {
+                       for (RocketComponent c : document.getRocket()) {
+                               if (c instanceof SymmetricComponent) {
+                                       SymmetricComponent s = (SymmetricComponent) c;
+                                       initialSize = s.getForeRadius() * 2;
+                                       initialSize = MathUtil.max(initialSize, s.getAftRadius() * 2);
+                               }
+                       }
+               }
+               if (initialSize < 0.001) {
+                       Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
+                       initialSize = unit.fromUnit(unit.round(unit.toUnit(DEFAULT_INITIAL_SIZE)));
+               }
+               
+               fromField.setValue(initialSize);
+               toField.setValue(initialSize);
+               
+
+               // Add actions to the values
+               multiplier.addChangeListener(new ChangeListener() {
+                       @Override
+                       public void stateChanged(ChangeEvent e) {
+                               if (!changing) {
+                                       changing = true;
+                                       updateToField();
+                                       changing = false;
+                               }
+                       }
+               });
+               fromField.addChangeListener(new ChangeListener() {
+                       @Override
+                       public void stateChanged(ChangeEvent e) {
+                               if (!changing) {
+                                       changing = true;
+                                       updateToField();
+                                       changing = false;
+                               }
+                       }
+               });
+               toField.addChangeListener(new ChangeListener() {
+                       @Override
+                       public void stateChanged(ChangeEvent e) {
+                               if (!changing) {
+                                       changing = true;
+                                       updateMultiplier();
+                                       changing = false;
+                               }
+                       }
+               });
+               
+
+
+               String tip;
+               JPanel panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", ""));
+               this.add(panel);
+               
+
+               // Scaling selection
+               tip = trans.get("lbl.scale.ttip");
+               JLabel label = new JLabel(trans.get("lbl.scale"));
+               label.setToolTipText(tip);
+               panel.add(label, "span, split, gapright unrel");
+               
+               selectionOption = new JComboBox(options.toArray());
+               selectionOption.setEditable(false);
+               selectionOption.setToolTipText(tip);
+               panel.add(selectionOption, "growx, wrap para*2");
+               
+
+               // Scale multiplier
+               tip = trans.get("lbl.scaling.ttip");
+               label = new JLabel(trans.get("lbl.scaling"));
+               label.setToolTipText(tip);
+               panel.add(label, "gapright unrel");
+               
+
+               JSpinner spin = new JSpinner(multiplier.getSpinnerModel());
+               spin.setEditor(new SpinnerEditor(spin));
+               spin.setToolTipText(tip);
+               panel.add(spin, "w :30lp:65lp");
+               
+               UnitSelector unit = new UnitSelector(multiplier);
+               unit.setToolTipText(tip);
+               panel.add(unit, "w 30lp");
+               BasicSlider slider = new BasicSlider(multiplier.getSliderModel(0.25, 1.0, 4.0));
+               slider.setToolTipText(tip);
+               panel.add(slider, "w 100lp, growx, wrap para");
+               
+
+               // Scale from ... to ...
+               tip = trans.get("lbl.scaleFromTo.ttip");
+               label = new JLabel(trans.get("lbl.scaleFrom"));
+               label.setToolTipText(tip);
+               panel.add(label, "gapright unrel, right");
+               
+               spin = new JSpinner(fromField.getSpinnerModel());
+               spin.setEditor(new SpinnerEditor(spin));
+               spin.setToolTipText(tip);
+               panel.add(spin, "span, split, w :30lp:65lp");
+               
+               unit = new UnitSelector(fromField);
+               unit.setToolTipText(tip);
+               panel.add(unit, "w 30lp");
+               
+               label = new JLabel(trans.get("lbl.scaleTo"));
+               label.setToolTipText(tip);
+               panel.add(label, "gap unrel");
+               
+               spin = new JSpinner(toField.getSpinnerModel());
+               spin.setEditor(new SpinnerEditor(spin));
+               spin.setToolTipText(tip);
+               panel.add(spin, "w :30lp:65lp");
+               
+               unit = new UnitSelector(toField);
+               unit.setToolTipText(tip);
+               panel.add(unit, "w 30lp, wrap para*2");
+               
+
+               // Scale override
+               scaleMassValues = new JCheckBox(trans.get("checkbox.scaleMass"));
+               scaleMassValues.setToolTipText(trans.get("checkbox.scaleMass.ttip"));
+               scaleMassValues.setSelected(true);
+               boolean overridden = false;
+               for (RocketComponent c : document.getRocket()) {
+                       if (c instanceof MassComponent || c.isMassOverridden()) {
+                               overridden = true;
+                               break;
+                       }
+               }
+               scaleMassValues.setEnabled(overridden);
+               panel.add(scaleMassValues, "span, wrap para*3");
+               
+
+               // Buttons
+               
+               JButton scale = new JButton(trans.get("button.scale"));
+               scale.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               doScale();
+                               ScaleDialog.this.setVisible(false);
+                       }
+               });
+               panel.add(scale, "span, split, right, gap para");
+               
+               JButton cancel = new JButton(trans.get("button.cancel"));
+               cancel.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               ScaleDialog.this.setVisible(false);
+                       }
+               });
+               panel.add(cancel, "right, gap para");
+               
+
+
+               GUIUtil.setDisposableDialogOptions(this, scale);
+       }
+       
+       
+
+       private void doScale() {
+               double mul = multiplier.getValue();
+               if (!(SCALE_MIN <= mul && mul <= SCALE_MAX)) {
+                       ExceptionHandler.handleErrorCondition("Illegal multiplier value, mul=" + mul);
+                       return;
+               }
+               
+               if (MathUtil.equals(mul, 1.0)) {
+                       // Nothing to do
+                       log.user("Scaling by value 1.0 - nothing to do");
+                       return;
+               }
+               
+               boolean scaleMass = scaleMassValues.isSelected();
+               
+               Object item = selectionOption.getSelectedItem();
+               log.user("Scaling design by factor " + mul + ", option=" + item);
+               if (SCALE_ROCKET.equals(item)) {
+                       
+                       // Scale the entire rocket design
+                       try {
+                               document.startUndo("Scale rocket");
+                               for (RocketComponent c : document.getRocket()) {
+                                       scale(c, mul, scaleMass);
+                               }
+                       } finally {
+                               document.stopUndo();
+                       }
+                       
+               } else if (SCALE_SUBSELECTION.equals(item)) {
+                       
+                       // Scale component and subcomponents
+                       try {
+                               document.startUndo("Scale components");
+                               for (RocketComponent c : selection) {
+                                       scale(c, mul, scaleMass);
+                               }
+                       } finally {
+                               document.stopUndo();
+                       }
+                       
+               } else if (SCALE_SELECTION.equals(item)) {
+                       
+                       // Scale only the selected component
+                       try {
+                               document.startUndo("Scale component");
+                               scale(selection, mul, scaleMass);
+                       } finally {
+                               document.stopUndo();
+                       }
+                       
+               } else {
+                       throw new BugException("Unknown item selected, item=" + item);
+               }
+       }
+       
+       
+       /**
+        * Perform scaling on a single component.
+        */
+       private void scale(RocketComponent component, double mul, boolean scaleMass) {
+               
+               Class<?> clazz = component.getClass();
+               while (clazz != null) {
+                       List<Scaler> list = SCALERS.get(clazz);
+                       if (list != null) {
+                               for (Scaler s : list) {
+                                       s.scale(component, mul, scaleMass);
+                               }
+                       }
+                       
+                       clazz = clazz.getSuperclass();
+               }
+       }
+       
+       
+       private void updateToField() {
+               double mul = multiplier.getValue();
+               double from = fromField.getValue();
+               double to = from * mul;
+               toField.setValue(to);
+       }
+       
+       private void updateMultiplier() {
+               double from = fromField.getValue();
+               double to = toField.getValue();
+               double mul = to / from;
+               
+               if (!MathUtil.equals(from, 0)) {
+                       mul = MathUtil.clamp(mul, SCALE_MIN, SCALE_MAX);
+                       multiplier.setValue(mul);
+               }
+               updateToField();
+       }
+       
+       
+
+       /**
+        * Interface for scaling a specific component/value.
+        */
+       private interface Scaler {
+               public void scale(RocketComponent c, double multiplier, boolean scaleMass);
+       }
+       
+       /**
+        * General scaler implementation that uses reflection to get/set a specific value.
+        */
+       private static class GeneralScaler implements Scaler {
+               
+               private final Method getter;
+               private final Method setter;
+               private final Method autoMethod;
+               
+               public GeneralScaler(Class<? extends RocketComponent> componentClass, String methodName, String autoMethodName) {
+                       
+                       getter = Reflection.findMethod(componentClass, "get" + methodName);
+                       setter = Reflection.findMethod(componentClass, "set" + methodName, double.class);
+                       if (autoMethodName != null) {
+                               autoMethod = Reflection.findMethod(componentClass, autoMethodName);
+                       } else {
+                               autoMethod = null;
+                       }
+                       
+               }
+               
+               @Override
+               public void scale(RocketComponent c, double multiplier, boolean scaleMass) {
+                       
+                       // Do not scale if set to automatic
+                       if (autoMethod != null) {
+                               boolean auto = (Boolean) autoMethod.invoke(c);
+                               if (auto) {
+                                       return;
+                               }
+                       }
+                       
+                       // Scale value
+                       double value = (Double) getter.invoke(c);
+                       value = value * multiplier;
+                       setter.invoke(c, value);
+               }
+               
+       }
+       
+       
+       private static class OverrideScaler implements Scaler {
+               
+               @Override
+               public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+                       
+                       if (component.isCGOverridden()) {
+                               double cgx = component.getOverrideCGX();
+                               cgx = cgx * multiplier;
+                               component.setOverrideCGX(cgx);
+                       }
+                       
+                       if (scaleMass && component.isMassOverridden()) {
+                               double mass = component.getOverrideMass();
+                               mass = mass * MathUtil.pow3(multiplier);
+                               component.setOverrideMass(mass);
+                       }
+               }
+               
+       }
+       
+       private static class MassComponentScaler implements Scaler {
+               
+               @Override
+               public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+                       if (scaleMass) {
+                               MassComponent c = (MassComponent) component;
+                               double mass = c.getComponentMass();
+                               mass = mass * MathUtil.pow3(multiplier);
+                               c.setComponentMass(mass);
+                       }
+               }
+               
+       }
+       
+       private static class FreeformFinSetScaler implements Scaler {
+               
+               @Override
+               public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+                       FreeformFinSet finset = (FreeformFinSet) component;
+                       Coordinate[] points = finset.getFinPoints();
+                       for (int i = 0; i < points.length; i++) {
+                               points[i] = points[i].multiply(multiplier);
+                       }
+                       try {
+                               finset.setPoints(points);
+                       } catch (IllegalFinPointException e) {
+                               throw new BugException("Failed to set points after scaling, original=" + Arrays.toString(finset.getFinPoints()) + " scaled=" + Arrays.toString(points), e);
+                       }
+               }
+               
+       }
+       
+}
index 1253a01180e47f797ee30d8f163d72a4ef201f98..a68412e175b051cbacb85ec876adce0c1a9d35f9 100644 (file)
@@ -7,44 +7,60 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.ServiceLoader;
 
 import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
 
+import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.document.OpenRocketDocument;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
 import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
-import net.sf.openrocket.optimization.services.OptimizableParameterService;
-import net.sf.openrocket.optimization.services.SimulationModifierService;
+import net.sf.openrocket.optimization.services.OptimizationServiceHelper;
+import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.GUIUtil;
 
 public class GeneralOptimizationDialog extends JDialog {
+       private static final Translator trans = Application.getTranslator();
        
        private final List<OptimizableParameter> optimizationParameters = new ArrayList<OptimizableParameter>();
        private final Map<Object, List<SimulationModifier>> simulationModifiers =
                        new HashMap<Object, List<SimulationModifier>>();
-       private static final Translator trans = Application.getTranslator();
-
+       
 
-       private final OpenRocketDocument document;
+       private final OpenRocketDocument baseDocument;
+       private final Rocket rocketCopy;
+       
        
        public GeneralOptimizationDialog(OpenRocketDocument document, Window parent) {
-               this.document = document;
+               super(parent, "Rocket optimization");
+               
+               this.baseDocument = document;
+               this.rocketCopy = document.getRocket().copyWithOriginalID();
                
                loadOptimizationParameters();
                loadSimulationModifiers();
+               
+
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+
+               JTree tree = new SimulationModifierTree(rocketCopy, simulationModifiers);
+               JScrollPane scroll = new JScrollPane(tree);
+               panel.add(scroll, "width 300lp, height 300lp");
+               
+
+               this.add(panel);
+               GUIUtil.setDisposableDialogOptions(this, null);
        }
        
        
        private void loadOptimizationParameters() {
-               ServiceLoader<OptimizableParameterService> loader =
-                               ServiceLoader.load(OptimizableParameterService.class);
-               
-               for (OptimizableParameterService g : loader) {
-                       optimizationParameters.addAll(g.getParameters(document));
-               }
+               optimizationParameters.addAll(OptimizationServiceHelper.getOptimizableParameters(baseDocument));
                
                if (optimizationParameters.isEmpty()) {
                        throw new BugException("No rocket optimization parameters found, distribution built wrong.");
@@ -60,18 +76,15 @@ public class GeneralOptimizationDialog extends JDialog {
        
        
        private void loadSimulationModifiers() {
-               ServiceLoader<SimulationModifierService> loader = ServiceLoader.load(SimulationModifierService.class);
                
-               for (SimulationModifierService g : loader) {
-                       for (SimulationModifier m : g.getModifiers(document)) {
-                               Object key = m.getRelatedObject();
-                               List<SimulationModifier> list = simulationModifiers.get(key);
-                               if (list == null) {
-                                       list = new ArrayList<SimulationModifier>();
-                                       simulationModifiers.put(key, list);
-                               }
-                               list.add(m);
+               for (SimulationModifier m : OptimizationServiceHelper.getSimulationModifiers(baseDocument)) {
+                       Object key = m.getRelatedObject();
+                       List<SimulationModifier> list = simulationModifiers.get(key);
+                       if (list == null) {
+                               list = new ArrayList<SimulationModifier>();
+                               simulationModifiers.put(key, list);
                        }
+                       list.add(m);
                }
                
                for (Object key : simulationModifiers.keySet()) {
@@ -86,4 +99,6 @@ public class GeneralOptimizationDialog extends JDialog {
                
        }
        
+
+
 }
diff --git a/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java b/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java
new file mode 100644 (file)
index 0000000..73296bb
--- /dev/null
@@ -0,0 +1,159 @@
+package net.sf.openrocket.gui.dialogs.optimization;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import net.sf.openrocket.gui.components.BasicTree;
+import net.sf.openrocket.gui.main.ComponentIcons;
+import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+/**
+ * A tree that displays the simulation modifiers in a tree structure.
+ * <p>
+ * All nodes in the model are instances of DefaultMutableTreeNode.  The user objects
+ * within are either of type RocketComponent, String or SimulationModifier.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SimulationModifierTree extends BasicTree {
+       
+       /**
+        * Sole constructor.
+        * 
+        * @param rocket                                the rocket.
+        * @param simulationModifiers   the simulation modifiers, ordered and mapped by components
+        */
+       public SimulationModifierTree(Rocket rocket, Map<Object, List<SimulationModifier>> simulationModifiers) {
+               super(createModifierTree(rocket, simulationModifiers));
+               
+               this.setCellRenderer(new ComponentModifierTreeRenderer());
+               expandComponents();
+       }
+       
+       
+
+       private static DefaultMutableTreeNode createModifierTree(Rocket rocket,
+                       Map<Object, List<SimulationModifier>> simulationModifiers) {
+               
+               DefaultMutableTreeNode baseNode = new DefaultMutableTreeNode(rocket);
+               populateTree(baseNode, rocket, simulationModifiers);
+               
+               return baseNode;
+       }
+       
+       
+       private static void populateTree(DefaultMutableTreeNode node, RocketComponent component,
+                       Map<Object, List<SimulationModifier>> simulationModifiers) {
+               
+               // Add modifiers (if any)
+               List<SimulationModifier> modifiers = simulationModifiers.get(component);
+               if (modifiers != null) {
+                       DefaultMutableTreeNode modifierNode;
+                       
+                       if (component.getChildCount() > 0) {
+                               modifierNode = new DefaultMutableTreeNode("Optimization parameters");
+                               node.add(modifierNode);
+                       } else {
+                               modifierNode = node;
+                       }
+                       
+                       for (SimulationModifier m : modifiers) {
+                               modifierNode.add(new DefaultMutableTreeNode(m));
+                       }
+               }
+               
+               // Add child components
+               for (RocketComponent c : component.getChildren()) {
+                       DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c);
+                       node.add(newNode);
+                       populateTree(newNode, c, simulationModifiers);
+               }
+               
+       }
+       
+       
+       @SuppressWarnings("rawtypes")
+       private void expandComponents() {
+               DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) this.getModel().getRoot();
+               
+               Enumeration enumeration = baseNode.breadthFirstEnumeration();
+               
+               while (enumeration.hasMoreElements()) {
+                       DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
+                       Object object = node.getUserObject();
+                       if (object instanceof RocketComponent) {
+                               this.makeVisible(new TreePath(node.getPath()));
+                       }
+               }
+       }
+       
+       
+
+
+       public class ComponentModifierTreeRenderer extends DefaultTreeCellRenderer {
+               private Font componentFont;
+               private Font stringFont;
+               private Font modifierFont;
+               
+               @Override
+               public Component getTreeCellRendererComponent(
+                               JTree tree,
+                               Object value,
+                               boolean sel,
+                               boolean expanded,
+                               boolean leaf,
+                               int row,
+                               boolean hasFocus2) {
+                       
+                       super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus2);
+                       
+                       if (componentFont == null) {
+                               makeFonts();
+                       }
+                       
+
+                       // Customize based on line type
+                       
+                       Object object = ((DefaultMutableTreeNode) value).getUserObject();
+                       
+                       // Set icon (for rocket components, null for others)
+                       setIcon(ComponentIcons.getSmallIcon(object.getClass()));
+                       
+
+                       // Set text color/style
+                       if (object instanceof RocketComponent) {
+                               setForeground(Color.GRAY);
+                               setFont(componentFont);
+                       } else if (object instanceof String) {
+                               setForeground(Color.GRAY);
+                               setFont(stringFont);
+                       } else if (object instanceof SimulationModifier) {
+                               setForeground(Color.BLACK);
+                               setFont(modifierFont);
+                               setText(((SimulationModifier) object).getName());
+                       }
+                       
+                       return this;
+               }
+               
+               private void makeFonts() {
+                       Font font = getFont();
+                       componentFont = font.deriveFont(Font.ITALIC);
+                       stringFont = font;
+                       modifierFont = font.deriveFont(Font.BOLD);
+               }
+               
+       }
+       
+}
index fe662a0a490abf6412df0c0d60173937216be7dd..b6b74380506ea80044145020935da199905dc4fb 100644 (file)
@@ -75,8 +75,10 @@ import net.sf.openrocket.gui.dialogs.ExampleDesignDialog;
 import net.sf.openrocket.gui.dialogs.LicenseDialog;
 import net.sf.openrocket.gui.dialogs.MotorDatabaseLoadingDialog;
 import net.sf.openrocket.gui.dialogs.PrintDialog;
+import net.sf.openrocket.gui.dialogs.ScaleDialog;
 import net.sf.openrocket.gui.dialogs.SwingWorkerDialog;
 import net.sf.openrocket.gui.dialogs.WarningDialog;
+import net.sf.openrocket.gui.dialogs.optimization.GeneralOptimizationDialog;
 import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog;
 import net.sf.openrocket.gui.main.componenttree.ComponentTree;
 import net.sf.openrocket.gui.optimization.OptimizationTestDialog;
@@ -395,6 +397,19 @@ public class BasicFrame extends JFrame {
        
        
 
+       /**
+        * Return the currently selected rocket component, or <code>null</code> if none selected.
+        */
+       private RocketComponent getSelectedComponent() {
+               TreePath path = componentSelectionModel.getSelectionPath();
+               if (path == null)
+                       return null;
+               tree.scrollPathToVisible(path);
+               
+               return (RocketComponent) path.getLastPathComponent();
+       }
+       
+       
        /**
         * Creates the menu for the window.
         */
@@ -407,7 +422,7 @@ public class BasicFrame extends JFrame {
                menu = new JMenu(trans.get("main.menu.file"));
                menu.setMnemonic(KeyEvent.VK_F);
                //// File-handling related tasks
-               menu.getAccessibleContext().setAccessibleDescription("File-handling related tasks");
+               menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.desc"));
                menubar.add(menu);
                
                //// New 
@@ -415,7 +430,7 @@ public class BasicFrame extends JFrame {
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
                item.setMnemonic(KeyEvent.VK_N);
                //// Create a new rocket design
-               item.getAccessibleContext().setAccessibleDescription("Create a new rocket design");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.new.desc"));
                item.setIcon(Icons.FILE_NEW);
                item.addActionListener(new ActionListener() {
                        @Override
@@ -504,7 +519,7 @@ public class BasicFrame extends JFrame {
                item = new JMenuItem(trans.get("main.menu.file.print"), KeyEvent.VK_P);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.CTRL_MASK));
                //// Print parts list and fin template
-               item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Printpart"));
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.print.desc"));
                item.setIcon(Icons.FILE_PRINT);
                item.addActionListener(new ActionListener() {
                        @Override
@@ -565,7 +580,7 @@ public class BasicFrame extends JFrame {
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
                item.setMnemonic(KeyEvent.VK_U);
                //// Undo the previous operation
-               item.getAccessibleContext().setAccessibleDescription("Undo the previous operation");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc"));
                
                menu.add(item);
                
@@ -574,8 +589,7 @@ public class BasicFrame extends JFrame {
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
                item.setMnemonic(KeyEvent.VK_R);
                //// Redo the previously undone operation
-               item.getAccessibleContext().setAccessibleDescription("Redo the previously undone " +
-                               "operation");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.redo.desc"));
                menu.add(item);
                
                menu.addSeparator();
@@ -595,12 +609,31 @@ public class BasicFrame extends JFrame {
                
                menu.addSeparator();
                
+
+
+               item = new JMenuItem(trans.get("main.menu.edit.resize"));
+               // FIXME: Icon
+               //item.setIcon(Icons.PREFERENCES);
+               //// Setup the application preferences
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc"));
+               item.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               log.user("Scale... selected");
+                               ScaleDialog dialog = new ScaleDialog(document, getSelectedComponent(), BasicFrame.this);
+                               dialog.setVisible(true);
+                               dialog.dispose();
+                       }
+               });
+               menu.add(item);
+               
+
+
                //// Preferences
                item = new JMenuItem(trans.get("main.menu.edit.preferences"));
                item.setIcon(Icons.PREFERENCES);
                //// Setup the application preferences
-               item.getAccessibleContext().setAccessibleDescription("Setup the application " +
-                               "preferences");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.preferences.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -617,14 +650,13 @@ public class BasicFrame extends JFrame {
                menu = new JMenu(trans.get("main.menu.analyze"));
                menu.setMnemonic(KeyEvent.VK_A);
                //// Analyzing the rocket
-               menu.getAccessibleContext().setAccessibleDescription("Analyzing the rocket");
+               menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.desc"));
                menubar.add(menu);
                
                //// Component analysis
                item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C);
                //// Analyze the rocket components separately
-               item.getAccessibleContext().setAccessibleDescription("Analyze the rocket components " +
-                               "separately");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -648,14 +680,14 @@ public class BasicFrame extends JFrame {
                menu = new JMenu(trans.get("main.menu.help"));
                menu.setMnemonic(KeyEvent.VK_H);
                //// Information about OpenRocket
-               menu.getAccessibleContext().setAccessibleDescription("Information about OpenRocket");
+               menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc"));
                menubar.add(menu);
                
 
                //// License
                item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L);
                //// OpenRocket license information
-               item.getAccessibleContext().setAccessibleDescription("OpenRocket license information");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -670,8 +702,7 @@ public class BasicFrame extends JFrame {
                //// Bug report
                item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B);
                //// Information about reporting bugs in OpenRocket
-               item.getAccessibleContext().setAccessibleDescription("Information about reporting " +
-                               "bugs in OpenRocket");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.bugReport.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -684,7 +715,7 @@ public class BasicFrame extends JFrame {
                //// Debug log
                item = new JMenuItem(trans.get("main.menu.help.debugLog"));
                //// View the OpenRocket debug log
-               item.getAccessibleContext().setAccessibleDescription("View the OpenRocket debug log");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.debugLog.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -699,7 +730,7 @@ public class BasicFrame extends JFrame {
                //// About
                item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A);
                //// About OpenRocket
-               item.getAccessibleContext().setAccessibleDescription("About OpenRocket");
+               item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.about.desc"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -713,11 +744,14 @@ public class BasicFrame extends JFrame {
                this.setJMenuBar(menubar);
        }
        
-       
        private JMenu makeDebugMenu() {
                JMenu menu;
                JMenuItem item;
                
+               /*
+                * This menu is intentionally left untranslated.
+                */
+
                ////  Debug menu
                menu = new JMenu("Debug");
                //// OpenRocket debugging tasks
@@ -936,6 +970,16 @@ public class BasicFrame extends JFrame {
                menu.add(item);
                
 
+               item = new JMenuItem("General optimization test");
+               item.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               new GeneralOptimizationDialog(document, BasicFrame.this).setVisible(true);
+                       }
+               });
+               menu.add(item);
+               
+
                item = new JMenuItem("Optimization test");
                item.addActionListener(new ActionListener() {
                        @Override
index 822577189707228812bff1f3d47e3b36e94b3548..cd71c2a625b72f34bfbfb6beb9a5cc6808fddeff 100644 (file)
@@ -31,18 +31,18 @@ import net.sf.openrocket.startup.Application;
 
 public class ComponentIcons {
        private static final Translator trans = Application.getTranslator();
-
+       
        private static final String ICON_DIRECTORY = "pix/componenticons/";
        private static final String SMALL_SUFFIX = "-small.png";
        private static final String LARGE_SUFFIX = "-large.png";
        
-       private static final HashMap<Class<?>,ImageIcon> SMALL_ICONS = 
-               new HashMap<Class<?>,ImageIcon>();
-       private static final HashMap<Class<?>,ImageIcon> LARGE_ICONS = 
-               new HashMap<Class<?>,ImageIcon>();
-       private static final HashMap<Class<?>,ImageIcon> DISABLED_ICONS = 
-               new HashMap<Class<?>,ImageIcon>();
-
+       private static final HashMap<Class<?>, ImageIcon> SMALL_ICONS =
+                       new HashMap<Class<?>, ImageIcon>();
+       private static final HashMap<Class<?>, ImageIcon> LARGE_ICONS =
+                       new HashMap<Class<?>, ImageIcon>();
+       private static final HashMap<Class<?>, ImageIcon> DISABLED_ICONS =
+                       new HashMap<Class<?>, ImageIcon>();
+       
        static {
                //// Nose cone
                load("nosecone", trans.get("ComponentIcons.Nosecone"), NoseCone.class);
@@ -88,24 +88,43 @@ public class ComponentIcons {
        }
        
        
-       
+       /**
+        * Return the small icon for a component type.
+        * 
+        * @param c             the component class.
+        * @return              the icon, or <code>null</code> if none available.
+        */
        public static Icon getSmallIcon(Class<?> c) {
                return SMALL_ICONS.get(c);
        }
+       
+       /**
+        * Return the large icon for a component type.
+        * 
+        * @param c             the component class.
+        * @return              the icon, or <code>null</code> if none available.
+        */
        public static Icon getLargeIcon(Class<?> c) {
                return LARGE_ICONS.get(c);
        }
+       
+       /**
+        * Return the large disabled icon for a component type.
+        * 
+        * @param c             the component class.
+        * @return              the icon, or <code>null</code> if none available.
+        */
        public static Icon getLargeDisabledIcon(Class<?> c) {
                return DISABLED_ICONS.get(c);
        }
        
        
-       
-       
+
+
        private static ImageIcon loadSmall(String file, String desc) {
                URL url = ClassLoader.getSystemResource(file);
-               if (url==null) {
-               ExceptionHandler.handleErrorCondition("ERROR:  Couldn't find file: " + file);
+               if (url == null) {
+                       ExceptionHandler.handleErrorCondition("ERROR:  Couldn't find file: " + file);
                        return null;
                }
                return new ImageIcon(url, desc);
@@ -116,35 +135,35 @@ public class ComponentIcons {
                ImageIcon[] icons = new ImageIcon[2];
                
                URL url = ClassLoader.getSystemResource(file);
-           if (url != null) {
-               BufferedImage bi,bi2;
-               try {
+               if (url != null) {
+                       BufferedImage bi, bi2;
+                       try {
                                bi = ImageIO.read(url);
-                               bi2 = ImageIO.read(url);   //  How the fsck can one duplicate a BufferedImage???
+                               bi2 = ImageIO.read(url); //  How the fsck can one duplicate a BufferedImage???
                        } catch (IOException e) {
-                               ExceptionHandler.handleErrorCondition("ERROR:  Couldn't read file: "+file, e);
-                       return new ImageIcon[]{null,null};
+                               ExceptionHandler.handleErrorCondition("ERROR:  Couldn't read file: " + file, e);
+                               return new ImageIcon[] { null, null };
                        }
                        
-                       icons[0] = new ImageIcon(bi,desc);
+                       icons[0] = new ImageIcon(bi, desc);
                        
                        // Create disabled icon
-                       if (false) {   // Fade using alpha 
-                               
-                               int rgb[] = bi2.getRGB(0,0,bi2.getWidth(),bi2.getHeight(),null,0,bi2.getWidth());
-                               for (int i=0; i<rgb.length; i++) {
-                                       final int alpha = (rgb[i]>>24)&0xFF;
-                                       rgb[i] = (rgb[i]&0xFFFFFF) | (alpha/3)<<24;
+                       if (false) { // Fade using alpha 
+                       
+                               int rgb[] = bi2.getRGB(0, 0, bi2.getWidth(), bi2.getHeight(), null, 0, bi2.getWidth());
+                               for (int i = 0; i < rgb.length; i++) {
+                                       final int alpha = (rgb[i] >> 24) & 0xFF;
+                                       rgb[i] = (rgb[i] & 0xFFFFFF) | (alpha / 3) << 24;
                                        
                                        //rgb[i] = (rgb[i]&0xFFFFFF) | ((rgb[i]>>1)&0x3F000000);
                                }
                                bi2.setRGB(0, 0, bi2.getWidth(), bi2.getHeight(), rgb, 0, bi2.getWidth());
-
-                       } else {   // Raster alpha
-
-                               for (int x=0; x < bi.getWidth(); x++) {
-                                       for (int y=0; y < bi.getHeight(); y++) {
-                                               if ((x+y)%2 == 0) {
+                               
+                       } else { // Raster alpha
+                       
+                               for (int x = 0; x < bi.getWidth(); x++) {
+                                       for (int y = 0; y < bi.getHeight(); y++) {
+                                               if ((x + y) % 2 == 0) {
                                                        bi2.setRGB(x, y, 0);
                                                }
                                        }
@@ -153,12 +172,12 @@ public class ComponentIcons {
                        }
                        
                        //// (disabled)
-                       icons[1] = new ImageIcon(bi2,desc + " " +trans.get("ComponentIcons.disabled"));
-               
-               return icons;
-           } else {
-               ExceptionHandler.handleErrorCondition("ERROR:  Couldn't find file: " + file);
-               return new ImageIcon[]{null,null};
-           }
+                       icons[1] = new ImageIcon(bi2, desc + " " + trans.get("ComponentIcons.disabled"));
+                       
+                       return icons;
+               } else {
+                       ExceptionHandler.handleErrorCondition("ERROR:  Couldn't find file: " + file);
+                       return new ImageIcon[] { null, null };
+               }
        }
 }
index 15e0cbf06b4a926952edf7df0b5cbc959916bb0b..a3d51de3a66131a04fa9a9a179895912f998f7a3 100644 (file)
@@ -186,7 +186,7 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
                }
                
                // Unknown Error
-               if (!(e instanceof Exception)) {
+               if (!(e instanceof Exception) && !(e instanceof LinkageError)) {
                        log.info("Showing Error dialog");
                        JOptionPane.showMessageDialog(null,
                                        new Object[] {
index ef13629a7e46626b3080a0ce9fa0ee95a231a91e..f78bfd0057a705972f1fbd3d5f7841af0c24eff9 100644 (file)
@@ -1,37 +1,17 @@
 package net.sf.openrocket.gui.main.componenttree;
 
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-
 import javax.swing.DropMode;
-import javax.swing.Icon;
-import javax.swing.JTree;
 import javax.swing.ToolTipManager;
-import javax.swing.tree.TreeModel;
-import javax.swing.tree.TreePath;
 
 import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.components.BasicTree;
 
 
-public class ComponentTree extends JTree {
+public class ComponentTree extends BasicTree {
        
        public ComponentTree(OpenRocketDocument document) {
                super();
                this.setModel(new ComponentTreeModel(document.getRocket(), this));
-               this.setToggleClickCount(0);
-               
-               javax.swing.plaf.basic.BasicTreeUI plainUI = new javax.swing.plaf.basic.BasicTreeUI();
-               this.setUI(plainUI);
-               plainUI.setExpandedIcon(TreeIcon.MINUS);
-               plainUI.setCollapsedIcon(TreeIcon.PLUS);
-               plainUI.setLeftChildIndent(15);
-               
-
-               this.setBackground(Color.WHITE);
-               this.setShowsRootHandles(false);
                
                this.setCellRenderer(new ComponentTreeRenderer());
                
@@ -47,86 +27,6 @@ public class ComponentTree extends JTree {
                
        }
        
-       
-       public void expandTree() {
-               for (int i = 0; i < getRowCount(); i++)
-                       expandRow(i);
-       }
-       
-       @Override
-       public void treeDidChange() {
-               super.treeDidChange();
-               expandChildlessNodes();
-       }
-       
-       /**
-        * Expand all nodes in the tree that are visible and have no children.  This can be used
-        * to avoid the situation where a non-leaf node is marked as being expandable, but when
-        * expanding it it has no children.
-        */
-       private void expandChildlessNodes() {
-               TreeModel model = this.getModel();
-               if (model == null) {
-                       return;
-               }
-               Object root = model.getRoot();
-               expandChildlessNodes(model, new TreePath(root));
-       }
-       
-       private void expandChildlessNodes(TreeModel model, TreePath path) {
-               Object object = path.getLastPathComponent();
-               if (this.isVisible(path)) {
-                       int count = model.getChildCount(object);
-                       if (count == 0) {
-                               this.expandPath(path);
-                       }
-                       for (int i = 0; i < count; i++) {
-                               expandChildlessNodes(model, path.pathByAddingChild(model.getChild(object, i)));
-                       }
-               }
-       }
-       
-       
 
-       private static class TreeIcon implements Icon {
-               public static final Icon PLUS = new TreeIcon(true);
-               public static final Icon MINUS = new TreeIcon(false);
-               
-               // Implementation:
-               
-               private final static int width = 9;
-               private final static int height = 9;
-               private final static BasicStroke stroke = new BasicStroke(2);
-               private boolean plus;
-               
-               private TreeIcon(boolean plus) {
-                       this.plus = plus;
-               }
-               
-               public void paintIcon(Component c, Graphics g, int x, int y) {
-                       Graphics2D g2 = (Graphics2D) g.create();
-                       
-                       g2.setColor(Color.WHITE);
-                       g2.fillRect(x, y, width, height);
-                       
-                       g2.setColor(Color.DARK_GRAY);
-                       g2.drawRect(x, y, width, height);
-                       
-                       g2.setStroke(stroke);
-                       g2.drawLine(x + 3, y + (height + 1) / 2, x + width - 2, y + (height + 1) / 2);
-                       if (plus)
-                               g2.drawLine(x + (width + 1) / 2, y + 3, x + (width + 1) / 2, y + height - 2);
-                       
-                       g2.dispose();
-               }
-               
-               public int getIconWidth() {
-                       return width;
-               }
-               
-               public int getIconHeight() {
-                       return height;
-               }
-       }
-       
+
 }
index 8cf5828285f30512f496c3902d3216e6c92d1535..fe141f6129c87fd528cae1bbfad331a6d6541636 100644 (file)
@@ -20,15 +20,6 @@ import net.sf.openrocket.util.BugException;
 
 /**
  * A TreeModel that implements viewing of the rocket tree structure.
- * This transforms the internal view (which has nested Stages) into the user-view
- * (which has parallel Stages).
- * 
- * To view with the internal structure, switch to using BareComponentTreeModel in
- * ComponentTree.java.  NOTE: This class's makeTreePath will still be used, which
- * will create illegal paths, which results in problems with selections. 
- * 
- * TODO: MEDIUM: When converting a component to another component this model given 
- * outdated information, since it uses the components themselves as the nodes.
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
@@ -104,32 +95,6 @@ public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
                        ((TreeModelListener) l[i]).treeNodesChanged(e);
        }
        
-       private void fireTreeNodesChanged() {
-               Object[] path = { root };
-               TreeModelEvent e = new TreeModelEvent(this, path);
-               Object[] l = listeners.toArray();
-               for (int i = 0; i < l.length; i++)
-                       ((TreeModelListener) l[i]).treeNodesChanged(e);
-       }
-       
-       
-       @SuppressWarnings("unused")
-       private void printStructure(TreePath p, int level) {
-               String indent = "";
-               for (int i = 0; i < level; i++)
-                       indent += "  ";
-               System.out.println(indent + p +
-                               ": isVisible:" + tree.isVisible(p) +
-                               " isCollapsed:" + tree.isCollapsed(p) +
-                               " isExpanded:" + tree.isExpanded(p));
-               Object parent = p.getLastPathComponent();
-               for (int i = 0; i < getChildCount(parent); i++) {
-                       Object child = getChild(parent, i);
-                       TreePath path = makeTreePath((RocketComponent) child);
-                       printStructure(path, level + 1);
-               }
-       }
-       
        
        private void fireTreeStructureChanged(RocketComponent source) {
                Object[] path = { root };
index a4cedc05d0eb12d3cf590a4ad0a4876b13b83b3c..4cca766b4af225d10b9218d1e989be8f4582fd94 100644 (file)
@@ -1,29 +1,24 @@
 package net.sf.openrocket.l10n;
 
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.MissingResourceException;
-import java.util.Set;
 
 import net.sf.openrocket.gui.main.ExceptionHandler;
 
 /**
  * A translator that suppresses MissingResourceExceptions and handles them gracefully.
- * For every missing key this class calls the exception handler exactly once, and
- * returns the key itself as the translation.
+ * For the first missing key this class calls the exception handler, and afterwards
+ * always returns the key for missing translations.
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public class ExceptionSuppressingTranslator implements Translator {
        
-       private static final Set<String> errors = new HashSet<String>();
-       // For unit testing:
-       static int failures = 0;
+       static boolean errorReported = false;
        
        private final Translator translator;
        
        
-
        /**
         * Sole constructor.
         * 
@@ -49,8 +44,8 @@ public class ExceptionSuppressingTranslator implements Translator {
        
 
        private static synchronized void handleError(String key, MissingResourceException e) {
-               if (errors.add(key)) {
-                       failures++;
+               if (!errorReported) {
+                       errorReported = true;
                        ExceptionHandler.handleErrorCondition("Can not find translation for '" + key + "' locale=" + Locale.getDefault(), e);
                }
        }
index 92d5286cf6cbb578e14b4ab5894024beccb0fa27..977cd1866dc5d8242a86ad4b09231705bdfa8f99 100644 (file)
@@ -26,6 +26,12 @@ public class DefaultSimulationModifierService implements SimulationModifierServi
        static {
                //addModifier("optimization.modifier.", unitGroup, multiplier, componentClass, methodName);
                
+               /*
+                * Note:  Each component type must contain only mutually exclusive variables.
+                * For example, body tube does not have inner diameter definition because it is
+                * defined by the outer diameter and thickness.
+                */
+
                addModifier("optimization.modifier.nosecone.length", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Length");
                addModifier("optimization.modifier.nosecone.diameter", UnitGroup.UNITS_LENGTH, 2.0, NoseCone.class, "AftRadius");
                addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness");
@@ -37,7 +43,6 @@ public class DefaultSimulationModifierService implements SimulationModifierServi
                
                addModifier("optimization.modifier.bodytube.length", UnitGroup.UNITS_LENGTH, 1.0, BodyTube.class, "Length");
                addModifier("optimization.modifier.bodytube.outerDiameter", UnitGroup.UNITS_LENGTH, 2.0, BodyTube.class, "OuterRadius");
-               addModifier("optimization.modifier.bodytube.innerDiameter", UnitGroup.UNITS_LENGTH, 2.0, BodyTube.class, "InnerRadius");
                addModifier("optimization.modifier.bodytube.thickness", UnitGroup.UNITS_LENGTH, 1.0, BodyTube.class, "Thickness");
                
 
index 7725bbae4b91de346429991251b8d9fc2accd04f..d3b6194eb63832b52e84278048b2cb88c97c9d68 100644 (file)
@@ -20,7 +20,7 @@ import net.sf.openrocket.util.MathUtil;
 public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial {
        private static final Translator trans = Application.getTranslator();
        
-       private double radius = 0;
+       private double outerRadius = 0;
        private boolean autoRadius = false; // Radius chosen automatically based on parent component
        
        // When changing the inner radius, thickness is modified
@@ -37,13 +37,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        public BodyTube() {
                super();
                this.length = 8 * DEFAULT_RADIUS;
-               this.radius = DEFAULT_RADIUS;
+               this.outerRadius = DEFAULT_RADIUS;
                this.autoRadius = true;
        }
        
        public BodyTube(double length, double radius) {
                super();
-               this.radius = Math.max(radius, 0);
+               this.outerRadius = Math.max(radius, 0);
                this.length = Math.max(length, 0);
        }
        
@@ -86,7 +86,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
                                r = DEFAULT_RADIUS;
                        return r;
                }
-               return radius;
+               return outerRadius;
        }
        
        
@@ -99,14 +99,14 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
         */
        @Override
        public void setOuterRadius(double radius) {
-               if ((this.radius == radius) && (autoRadius == false))
+               if ((this.outerRadius == radius) && (autoRadius == false))
                        return;
                
                this.autoRadius = false;
-               this.radius = Math.max(radius, 0);
+               this.outerRadius = Math.max(radius, 0);
                
-               if (this.thickness > this.radius)
-                       this.thickness = this.radius;
+               if (this.thickness > this.outerRadius)
+                       this.thickness = this.outerRadius;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
        
index 8a6b2751da247f3136f99fcf76714b716500884e..5614e07710c1cd0edfa1ed07818ce9e9684b181d 100644 (file)
@@ -1,28 +1,28 @@
 package net.sf.openrocket.rocketcomponent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 
-import java.util.ArrayList;
-import java.util.Collection;
-
 
 
 public class LaunchLug extends ExternalComponent implements Coaxial {
-
+       
        private static final Translator trans = Application.getTranslator();
-
+       
        private double radius;
        private double thickness;
-
+       
        private double radialDirection = 0;
-
+       
        /* These are calculated when the component is first attached to any Rocket */
        private double shiftY, shiftZ;
-
-
+       
+       
 
        public LaunchLug() {
                super(Position.MIDDLE);
@@ -30,44 +30,49 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                thickness = 0.001;
                length = 0.03;
        }
-
-
-       public double getOuterRadius () {
+       
+       
+       @Override
+       public double getOuterRadius() {
                return radius;
        }
-
-       public void setOuterRadius (double radius) {
+       
+       @Override
+       public void setOuterRadius(double radius) {
                if (MathUtil.equals(this.radius, radius))
                        return;
                this.radius = radius;
                this.thickness = Math.min(this.thickness, this.radius);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
+       
+       @Override
        public double getInnerRadius() {
                return radius - thickness;
        }
-
+       
+       @Override
        public void setInnerRadius(double innerRadius) {
                setOuterRadius(innerRadius + thickness);
        }
-
+       
+       @Override
        public double getThickness() {
                return thickness;
        }
-
+       
        public void setThickness(double thickness) {
                if (MathUtil.equals(this.thickness, thickness))
                        return;
                this.thickness = MathUtil.clamp(thickness, 0, radius);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
        public double getRadialDirection() {
                return radialDirection;
        }
-
+       
        public void setRadialDirection(double direction) {
                direction = MathUtil.reduce180(direction);
                if (MathUtil.equals(this.radialDirection, direction))
@@ -75,8 +80,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                this.radialDirection = direction;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
 
        public void setLength(double length) {
                if (MathUtil.equals(this.length, length))
@@ -84,8 +89,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                this.length = length;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
 
 
 
@@ -94,44 +99,44 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                super.setRelativePosition(position);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
        @Override
        public void setPositionValue(double value) {
                super.setPositionValue(value);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-
-
+       
+       
 
        @Override
        public Coordinate[] shiftCoordinates(Coordinate[] array) {
                array = super.shiftCoordinates(array);
-
+               
                for (int i = 0; i < array.length; i++) {
                        array[i] = array[i].add(0, shiftY, shiftZ);
                }
-
+               
                return array;
        }
-
-
+       
+       
        @Override
        public void componentChanged(ComponentChangeEvent e) {
                super.componentChanged(e);
-
+               
                /*
                 * shiftY and shiftZ must be computed here since calculating them
                 * in shiftCoordinates() would cause an infinite loop due to .toRelative
                 */
                RocketComponent body;
                double parentRadius;
-
+               
                for (body = this.getParent(); body != null; body = body.getParent()) {
                        if (body instanceof SymmetricComponent)
                                break;
                }
-
+               
                if (body == null) {
                        parentRadius = 0;
                } else {
@@ -143,21 +148,21 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                        x2 = MathUtil.clamp(x2, 0, body.getLength());
                        parentRadius = Math.max(s.getRadius(x1), s.getRadius(x2));
                }
-
+               
                shiftY = Math.cos(radialDirection) * (parentRadius + radius);
                shiftZ = Math.sin(radialDirection) * (parentRadius + radius);
-
+               
                //              System.out.println("Computed shift: y="+shiftY+" z="+shiftZ);
        }
-
-
+       
+       
 
 
        @Override
        public double getComponentVolume() {
                return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness));
        }
-
+       
        @Override
        public Collection<Coordinate> getComponentBounds() {
                ArrayList<Coordinate> set = new ArrayList<Coordinate>();
@@ -165,40 +170,39 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                addBound(set, length, radius);
                return set;
        }
-
+       
        @Override
        public Coordinate getComponentCG() {
                return new Coordinate(length / 2, 0, 0, getComponentMass());
        }
-
+       
        @Override
        public String getComponentName() {
                //// Launch lug
                return trans.get("LaunchLug.Launchlug");
        }
-
+       
        @Override
        public double getLongitudinalUnitInertia() {
                // 1/12 * (3 * (r1^2 + r2^2) + h^2)
-               return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
-                               MathUtil.pow2(getLength())) / 12;
+               return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) + MathUtil.pow2(getLength())) / 12;
        }
-
+       
        @Override
        public double getRotationalUnitInertia() {
                // 1/2 * (r1^2 + r2^2)
-               return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
+               return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius())) / 2;
        }
-
+       
        @Override
        public boolean allowsChildren() {
                return false;
        }
-
+       
        @Override
        public boolean isCompatible(Class<? extends RocketComponent> type) {
                // Allow nothing to be attached to a LaunchLug
                return false;
        }
-
+       
 }
index 233457c25e8929637e37919e680afff27c07bcee..9e2c16bc11a8dc8465decd2a70d26a9f11c3c484 100644 (file)
@@ -1,12 +1,12 @@
 package net.sf.openrocket.rocketcomponent;
 
-import net.sf.openrocket.util.Coordinate;
-import net.sf.openrocket.util.MathUtil;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.MathUtil;
+
 
 /**
  * An inner component that consists of a hollow cylindrical component.  This can be
@@ -17,70 +17,75 @@ import java.util.List;
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public abstract class RingComponent extends StructuralComponent implements Coaxial {
-
+       
        protected boolean outerRadiusAutomatic = false;
        protected boolean innerRadiusAutomatic = false;
-
+       
 
        private double radialDirection = 0;
        private double radialPosition = 0;
-
+       
        private double shiftY = 0;
        private double shiftZ = 0;
+       
+       
 
-
-
-    @Override
+       @Override
        public abstract double getOuterRadius();
-    @Override
+       
+       @Override
        public abstract void setOuterRadius(double r);
-
-    @Override
+       
+       @Override
        public abstract double getInnerRadius();
-    @Override
+       
+       @Override
        public abstract void setInnerRadius(double r);
-
-    @Override
+       
+       @Override
        public abstract double getThickness();
+       
        public abstract void setThickness(double thickness);
-
-
+       
+       
        public final boolean isOuterRadiusAutomatic() {
                return outerRadiusAutomatic;
        }
-
+       
+       // Setter is protected, subclasses may make it public
        protected void setOuterRadiusAutomatic(boolean auto) {
                if (auto == outerRadiusAutomatic)
                        return;
                outerRadiusAutomatic = auto;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
        public final boolean isInnerRadiusAutomatic() {
                return innerRadiusAutomatic;
        }
-
+       
+       // Setter is protected, subclasses may make it public
        protected void setInnerRadiusAutomatic(boolean auto) {
                if (auto == innerRadiusAutomatic)
                        return;
                innerRadiusAutomatic = auto;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
 
 
        public final void setLength(double length) {
-               double l = Math.max(length,0);
+               double l = Math.max(length, 0);
                if (this.length == l)
                        return;
-
+               
                this.length = l;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
        /**
         * Return the radial direction of displacement of the component.  Direction 0
         * is equivalent to the Y-direction.
@@ -90,7 +95,7 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
        public double getRadialDirection() {
                return radialDirection;
        }
-
+       
        /**
         * Set the radial direction of displacement of the component.  Direction 0
         * is equivalent to the Y-direction.
@@ -106,8 +111,8 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                shiftZ = radialPosition * Math.sin(radialDirection);
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
 
 
        /**
@@ -119,7 +124,7 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
        public double getRadialPosition() {
                return radialPosition;
        }
-
+       
        /**
         * Set the radial position of the component.  The position is the distance
         * of the center of the component from the center of the parent component.
@@ -135,82 +140,82 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                shiftZ = radialPosition * Math.sin(radialDirection);
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
        public double getRadialShiftY() {
                return shiftY;
        }
-
+       
        public double getRadialShiftZ() {
                return shiftZ;
        }
-
+       
        public void setRadialShift(double y, double z) {
                radialPosition = Math.hypot(y, z);
                radialDirection = Math.atan2(z, y);
-
+               
                // Re-calculate to ensure consistency
                shiftY = radialPosition * Math.cos(radialDirection);
                shiftZ = radialPosition * Math.sin(radialDirection);
-               assert(MathUtil.equals(y, shiftY));
-               assert(MathUtil.equals(z, shiftZ));
-
+               assert (MathUtil.equals(y, shiftY));
+               assert (MathUtil.equals(z, shiftZ));
+               
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-
-
+       
+       
        /**
         * Return the number of times the component is multiplied.
         */
        public int getClusterCount() {
                if (this instanceof Clusterable)
-                       return ((Clusterable)this).getClusterConfiguration().getClusterCount();
+                       return ((Clusterable) this).getClusterConfiguration().getClusterCount();
                return 1;
        }
-
-
+       
+       
        /**
         * Shift the coordinates according to the radial position and direction.
         */
        @Override
        public Coordinate[] shiftCoordinates(Coordinate[] array) {
-               for (int i=0; i < array.length; i++) {
+               for (int i = 0; i < array.length; i++) {
                        array[i] = array[i].add(0, shiftY, shiftZ);
                }
                return array;
        }
-
-
+       
+       
        @Override
        public Collection<Coordinate> getComponentBounds() {
                List<Coordinate> bounds = new ArrayList<Coordinate>();
-               addBound(bounds,0,getOuterRadius());
-               addBound(bounds,length,getOuterRadius());
+               addBound(bounds, 0, getOuterRadius());
+               addBound(bounds, length, getOuterRadius());
                return bounds;
        }
-
-
+       
+       
 
        @Override
        public Coordinate getComponentCG() {
-               return new Coordinate(length/2, 0, 0, getComponentMass());
+               return new Coordinate(length / 2, 0, 0, getComponentMass());
        }
-
+       
        @Override
        public double getComponentMass() {
                return ringMass(getOuterRadius(), getInnerRadius(), getLength(),
                                getMaterial().getDensity()) * getClusterCount();
        }
-
-
+       
+       
        @Override
        public double getLongitudinalUnitInertia() {
                return ringLongitudinalUnitInertia(getOuterRadius(), getInnerRadius(), getLength());
        }
-
+       
        @Override
        public double getRotationalUnitInertia() {
                return ringRotationalUnitInertia(getOuterRadius(), getInnerRadius());
        }
-
+       
 }
index e62270ab0b138124d1c91201b0670abd1d3f60d7..b3c66f62b85d94ee38faa9c45cc8fbb17a7e22d0 100644 (file)
@@ -90,9 +90,10 @@ public class Reflection {
        
 
        /**
+        * Find a method from the rocket component classes.
         * Throws an exception if method not found.
         */
-       public static Reflection.Method findMethodStatic(
+       public static Reflection.Method findMethod(
                        Class<? extends RocketComponent> componentClass,
                        String method, Class<?>... params) {
                Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass,
diff --git a/svn-commit.tmp b/svn-commit.tmp
deleted file mode 100644 (file)
index f3a1928..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-bug fixes and rocket optimization
---This line, and those below, will be ignored--
-
-M    test/net/sf/openrocket/optimization/rocketoptimization/modifiers/TestGenericModifier.java
-M    test/net/sf/openrocket/optimization/rocketoptimization/TestRocketOptimizationFunction.java
-A    src/META-INF/services/net.sf.openrocket.optimization.services.SimulationModifierService
-D    src/META-INF/services/net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationParameterService
-A  + src/META-INF/services/net.sf.openrocket.optimization.services.OptimizableParameterService
-M    src/net/sf/openrocket/unit/Value.java
-M    src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
-M    src/net/sf/openrocket/file/openrocket/savers/BodyTubeSaver.java
-M    src/net/sf/openrocket/models/wind/PinkNoiseWindModel.java
-M    src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java
-M    src/net/sf/openrocket/gui/dialogs/PrintDialog.java
-M    src/net/sf/openrocket/gui/main/BasicFrame.java
-M    src/net/sf/openrocket/gui/configdialog/RingComponentConfig.java
-M    src/net/sf/openrocket/gui/configdialog/RocketConfig.java
-M    src/net/sf/openrocket/gui/configdialog/MotorConfig.java
-A    src/net/sf/openrocket/gui/optimization
-A    src/net/sf/openrocket/gui/optimization/OptimizationTestDialog.java
-M    src/net/sf/openrocket/simulation/RK4SimulationStepper.java
-M    src/net/sf/openrocket/simulation/GUISimulationConditions.java
-A  + src/net/sf/openrocket/optimization/services
-A  + src/net/sf/openrocket/optimization/services/OptimizableParameterService.java
-A  + src/net/sf/openrocket/optimization/services/SimulationModifierService.java
-A    src/net/sf/openrocket/optimization/services/DefaultOptimizableParameterService.java
-A    src/net/sf/openrocket/optimization/services/OptimizationServiceHelper.java
-M    src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java
-M    src/net/sf/openrocket/optimization/general/ParallelExecutorCache.java
-M    src/net/sf/openrocket/optimization/general/ParallelFunctionCache.java
-M    src/net/sf/openrocket/optimization/general/Point.java
-D    src/net/sf/openrocket/optimization/rocketoptimization/OptimizableParameterService.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/modifiers/AbstractSimulationModifier.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/modifiers/GenericModifier.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/modifiers/GenericComponentModifier.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifier.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/parameters/MaximumAltitudeParameter.java
-D    src/net/sf/openrocket/optimization/rocketoptimization/services
-M    src/net/sf/openrocket/optimization/rocketoptimization/RocketOptimizationFunction.java
-D    src/net/sf/openrocket/optimization/rocketoptimization/SimulationModifierService.java
-A    src/net/sf/openrocket/optimization/rocketoptimization/RocketOptimizationListener.java
-A    src/net/sf/openrocket/optimization/rocketoptimization/domains
-A    src/net/sf/openrocket/optimization/rocketoptimization/domains/IdentitySimulationDomain.java
-A    src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java
-M    src/net/sf/openrocket/optimization/rocketoptimization/SimulationDomain.java
-M    src/net/sf/openrocket/rocketcomponent/FinSet.java
-M    src/net/sf/openrocket/rocketcomponent/RocketComponent.java
-M    src/net/sf/openrocket/rocketcomponent/BodyTube.java
-M    src/net/sf/openrocket/util/TestRockets.java
-M    src/net/sf/openrocket/aerodynamics/barrowman/SymmetricComponentCalc.java
-M    ChangeLog
-A    datafiles/examples/Simulation listeners.ork
-A    doc/design/optimization-classes.uxf
-M    l10n/messages.properties
index 719ee39c8ef7fcf6cd72351b3351cfea844c299e..153b921fbb8f0e94a45ed01cacc1e76d26490ee4 100644 (file)
@@ -1,6 +1,6 @@
 package net.sf.openrocket.l10n;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 
 import java.util.MissingResourceException;
 
@@ -37,7 +37,7 @@ public class TestExceptionSuppressingTranslator {
        public void testFailure() {
                ExceptionSuppressingTranslator est = new ExceptionSuppressingTranslator(translator);
                
-               assertEquals("Prerequisite failed", 0, ExceptionSuppressingTranslator.failures);
+               assertFalse("Prerequisite failed", ExceptionSuppressingTranslator.errorReported);
                
                // @formatter:off
                context.checking(new Expectations() {{
@@ -49,15 +49,15 @@ public class TestExceptionSuppressingTranslator {
                
                // Test first failure
                assertEquals("fake.key", est.get("fake.key"));
-               assertEquals(1, ExceptionSuppressingTranslator.failures);
+               assertTrue(ExceptionSuppressingTranslator.errorReported);
                
                // Test second failure
                assertEquals("fake.key", est.get("fake.key"));
-               assertEquals(1, ExceptionSuppressingTranslator.failures);
+               assertTrue(ExceptionSuppressingTranslator.errorReported);
                
                // Test failure with other key
                assertEquals("fake.key2", est.get("fake.key2"));
-               assertEquals(2, ExceptionSuppressingTranslator.failures);
+               assertTrue(ExceptionSuppressingTranslator.errorReported);
        }