From 10c2e190c2e70564be6768a1acafa232dfff8e5c Mon Sep 17 00:00:00 2001 From: plaa Date: Sun, 17 Jul 2011 21:03:07 +0000 Subject: [PATCH] Component scaling support git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@135 180e2498-e6e9-4542-8430-84ac67f01cd8 --- ChangeLog | 4 + l10n/messages.properties | 55 +- .../file/openrocket/OpenRocketLoader.java | 182 +++--- .../openrocket/gui/adaptors/DoubleModel.java | 26 + .../openrocket/gui/components/BasicTree.java | 145 +++++ .../configdialog/FreeformFinSetConfig.java | 239 +++---- .../openrocket/gui/dialogs/ScaleDialog.java | 609 ++++++++++++++++++ .../GeneralOptimizationDialog.java | 61 +- .../optimization/SimulationModifierTree.java | 159 +++++ .../sf/openrocket/gui/main/BasicFrame.java | 80 ++- .../openrocket/gui/main/ComponentIcons.java | 97 +-- .../openrocket/gui/main/ExceptionHandler.java | 2 +- .../gui/main/componenttree/ComponentTree.java | 106 +-- .../componenttree/ComponentTreeModel.java | 35 - .../l10n/ExceptionSuppressingTranslator.java | 15 +- .../DefaultSimulationModifierService.java | 7 +- .../openrocket/rocketcomponent/BodyTube.java | 16 +- .../openrocket/rocketcomponent/LaunchLug.java | 106 +-- .../rocketcomponent/RingComponent.java | 119 ++-- src/net/sf/openrocket/util/Reflection.java | 3 +- svn-commit.tmp | 54 -- .../TestExceptionSuppressingTranslator.java | 10 +- 22 files changed, 1510 insertions(+), 620 deletions(-) create mode 100644 src/net/sf/openrocket/gui/components/BasicTree.java create mode 100644 src/net/sf/openrocket/gui/dialogs/ScaleDialog.java create mode 100644 src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java delete mode 100644 svn-commit.tmp diff --git a/ChangeLog b/ChangeLog index 074e24f8..cd9cd697 100644 --- 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 diff --git a/l10n/messages.properties b/l10n/messages.properties index 7f44c722..d44c1f69 100644 --- a/l10n/messages.properties +++ b/l10n/messages.properties @@ -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 diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java index a487e48c..d6cf6160 100644 --- a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java +++ b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java @@ -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( - 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( - 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( - 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( - 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( - 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 // handled by separate handler setters.put("Rocket:referencetype", new EnumSetter( - 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 diff --git a/src/net/sf/openrocket/gui/adaptors/DoubleModel.java b/src/net/sf/openrocket/gui/adaptors/DoubleModel.java index 7b3783a0..ce81e107 100644 --- a/src/net/sf/openrocket/gui/adaptors/DoubleModel.java +++ b/src/net/sf/openrocket/gui/adaptors/DoubleModel.java @@ -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 index 00000000..6ec8804e --- /dev/null +++ b/src/net/sf/openrocket/gui/components/BasicTree.java @@ -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; + } + } +} diff --git a/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 87be5fb4..06c877c7 100644 --- a/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -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(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(component,"CrossSection")); - panel.add(combo,"growx, wrap unrel"); + new EnumModel(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 index 00000000..d53a1a54 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java @@ -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 + */ +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, List> SCALERS = + new HashMap, List>(); + static { + List 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(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(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 componentClass, String methodName) { + addScaler(componentClass, methodName, null); + } + + private static void addScaler(Class componentClass, String methodName, String autoMethodName) { + List list = SCALERS.get(componentClass); + if (list == null) { + list = new ArrayList(); + 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 null 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 options = new ArrayList(); + 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 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 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); + } + } + + } + +} diff --git a/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java index 1253a011..a68412e1 100644 --- a/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java @@ -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 optimizationParameters = new ArrayList(); private final Map> simulationModifiers = new HashMap>(); - 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 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 loader = ServiceLoader.load(SimulationModifierService.class); - for (SimulationModifierService g : loader) { - for (SimulationModifier m : g.getModifiers(document)) { - Object key = m.getRelatedObject(); - List list = simulationModifiers.get(key); - if (list == null) { - list = new ArrayList(); - simulationModifiers.put(key, list); - } - list.add(m); + for (SimulationModifier m : OptimizationServiceHelper.getSimulationModifiers(baseDocument)) { + Object key = m.getRelatedObject(); + List list = simulationModifiers.get(key); + if (list == null) { + list = new ArrayList(); + 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 index 00000000..73296bbd --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java @@ -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. + *

+ * All nodes in the model are instances of DefaultMutableTreeNode. The user objects + * within are either of type RocketComponent, String or SimulationModifier. + * + * @author Sampo Niskanen + */ +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> simulationModifiers) { + super(createModifierTree(rocket, simulationModifiers)); + + this.setCellRenderer(new ComponentModifierTreeRenderer()); + expandComponents(); + } + + + + private static DefaultMutableTreeNode createModifierTree(Rocket rocket, + Map> simulationModifiers) { + + DefaultMutableTreeNode baseNode = new DefaultMutableTreeNode(rocket); + populateTree(baseNode, rocket, simulationModifiers); + + return baseNode; + } + + + private static void populateTree(DefaultMutableTreeNode node, RocketComponent component, + Map> simulationModifiers) { + + // Add modifiers (if any) + List 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); + } + + } + +} diff --git a/src/net/sf/openrocket/gui/main/BasicFrame.java b/src/net/sf/openrocket/gui/main/BasicFrame.java index fe662a0a..b6b74380 100644 --- a/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -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 null 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 diff --git a/src/net/sf/openrocket/gui/main/ComponentIcons.java b/src/net/sf/openrocket/gui/main/ComponentIcons.java index 82257718..cd71c2a6 100644 --- a/src/net/sf/openrocket/gui/main/ComponentIcons.java +++ b/src/net/sf/openrocket/gui/main/ComponentIcons.java @@ -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,ImageIcon> SMALL_ICONS = - new HashMap,ImageIcon>(); - private static final HashMap,ImageIcon> LARGE_ICONS = - new HashMap,ImageIcon>(); - private static final HashMap,ImageIcon> DISABLED_ICONS = - new HashMap,ImageIcon>(); - + private static final HashMap, ImageIcon> SMALL_ICONS = + new HashMap, ImageIcon>(); + private static final HashMap, ImageIcon> LARGE_ICONS = + new HashMap, ImageIcon>(); + private static final HashMap, ImageIcon> DISABLED_ICONS = + new HashMap, 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 null 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 null 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 null 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>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 }; + } } } diff --git a/src/net/sf/openrocket/gui/main/ExceptionHandler.java b/src/net/sf/openrocket/gui/main/ExceptionHandler.java index 15e0cbf0..a3d51de3 100644 --- a/src/net/sf/openrocket/gui/main/ExceptionHandler.java +++ b/src/net/sf/openrocket/gui/main/ExceptionHandler.java @@ -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[] { diff --git a/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java b/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java index ef13629a..f78bfd00 100644 --- a/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java +++ b/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java @@ -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; - } - } - + } diff --git a/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java b/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java index 8cf58282..fe141f61 100644 --- a/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java +++ b/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java @@ -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 */ @@ -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 }; diff --git a/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java b/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java index a4cedc05..4cca766b 100644 --- a/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java +++ b/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java @@ -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 */ public class ExceptionSuppressingTranslator implements Translator { - private static final Set errors = new HashSet(); - // 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); } } diff --git a/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java b/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java index 92d5286c..977cd186 100644 --- a/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java +++ b/src/net/sf/openrocket/optimization/services/DefaultSimulationModifierService.java @@ -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"); diff --git a/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/src/net/sf/openrocket/rocketcomponent/BodyTube.java index 7725bbae..d3b6194e 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -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); } diff --git a/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index 8a6b2751..5614e077 100644 --- a/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -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 getComponentBounds() { ArrayList set = new ArrayList(); @@ -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 type) { // Allow nothing to be attached to a LaunchLug return false; } - + } diff --git a/src/net/sf/openrocket/rocketcomponent/RingComponent.java b/src/net/sf/openrocket/rocketcomponent/RingComponent.java index 233457c2..9e2c16bc 100644 --- a/src/net/sf/openrocket/rocketcomponent/RingComponent.java +++ b/src/net/sf/openrocket/rocketcomponent/RingComponent.java @@ -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 */ 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 getComponentBounds() { List bounds = new ArrayList(); - 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()); } - + } diff --git a/src/net/sf/openrocket/util/Reflection.java b/src/net/sf/openrocket/util/Reflection.java index e62270ab..b3c66f62 100644 --- a/src/net/sf/openrocket/util/Reflection.java +++ b/src/net/sf/openrocket/util/Reflection.java @@ -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 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 index f3a19287..00000000 --- a/svn-commit.tmp +++ /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 diff --git a/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java b/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java index 719ee39c..153b921f 100644 --- a/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java +++ b/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java @@ -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); } -- 2.30.2