+2011-07-17 Sampo Niskanen
+
+ * Component scaling support
+
2011-07-14 Sampo Niskanen
* [BUG] Body tube auto-radius not enabled
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?
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
! "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?
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
static {
// RocketComponent
setters.put("RocketComponent:name", new StringSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setName", String.class)));
+ Reflection.findMethod(RocketComponent.class, "setName", String.class)));
setters.put("RocketComponent:color", new ColorSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setColor", Color.class)));
+ Reflection.findMethod(RocketComponent.class, "setColor", Color.class)));
setters.put("RocketComponent:linestyle", new EnumSetter<LineStyle>(
- Reflection.findMethodStatic(RocketComponent.class, "setLineStyle", LineStyle.class),
+ Reflection.findMethod(RocketComponent.class, "setLineStyle", LineStyle.class),
LineStyle.class));
setters.put("RocketComponent:position", new PositionSetter());
setters.put("RocketComponent:overridemass", new OverrideSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setOverrideMass", double.class),
- Reflection.findMethodStatic(RocketComponent.class, "setMassOverridden", boolean.class)));
+ Reflection.findMethod(RocketComponent.class, "setOverrideMass", double.class),
+ Reflection.findMethod(RocketComponent.class, "setMassOverridden", boolean.class)));
setters.put("RocketComponent:overridecg", new OverrideSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setOverrideCGX", double.class),
- Reflection.findMethodStatic(RocketComponent.class, "setCGOverridden", boolean.class)));
+ Reflection.findMethod(RocketComponent.class, "setOverrideCGX", double.class),
+ Reflection.findMethod(RocketComponent.class, "setCGOverridden", boolean.class)));
setters.put("RocketComponent:overridesubcomponents", new BooleanSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setOverrideSubcomponents", boolean.class)));
+ Reflection.findMethod(RocketComponent.class, "setOverrideSubcomponents", boolean.class)));
setters.put("RocketComponent:comment", new StringSetter(
- Reflection.findMethodStatic(RocketComponent.class, "setComment", String.class)));
+ Reflection.findMethod(RocketComponent.class, "setComment", String.class)));
// ExternalComponent
setters.put("ExternalComponent:finish", new EnumSetter<Finish>(
- Reflection.findMethodStatic(ExternalComponent.class, "setFinish", Finish.class),
+ Reflection.findMethod(ExternalComponent.class, "setFinish", Finish.class),
Finish.class));
setters.put("ExternalComponent:material", new MaterialSetter(
- Reflection.findMethodStatic(ExternalComponent.class, "setMaterial", Material.class),
+ Reflection.findMethod(ExternalComponent.class, "setMaterial", Material.class),
Material.Type.BULK));
// BodyComponent
setters.put("BodyComponent:length", new DoubleSetter(
- Reflection.findMethodStatic(BodyComponent.class, "setLength", double.class)));
+ Reflection.findMethod(BodyComponent.class, "setLength", double.class)));
// SymmetricComponent
setters.put("SymmetricComponent:thickness", new DoubleSetter(
- Reflection.findMethodStatic(SymmetricComponent.class, "setThickness", double.class),
+ Reflection.findMethod(SymmetricComponent.class, "setThickness", double.class),
"filled",
- Reflection.findMethodStatic(SymmetricComponent.class, "setFilled", boolean.class)));
+ Reflection.findMethod(SymmetricComponent.class, "setFilled", boolean.class)));
// BodyTube
setters.put("BodyTube:radius", new DoubleSetter(
- Reflection.findMethodStatic(BodyTube.class, "setOuterRadius", double.class),
+ Reflection.findMethod(BodyTube.class, "setOuterRadius", double.class),
"auto",
- Reflection.findMethodStatic(BodyTube.class, "setOuterRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(BodyTube.class, "setOuterRadiusAutomatic", boolean.class)));
// Transition
setters.put("Transition:shape", new EnumSetter<Transition.Shape>(
- Reflection.findMethodStatic(Transition.class, "setType", Transition.Shape.class),
+ Reflection.findMethod(Transition.class, "setType", Transition.Shape.class),
Transition.Shape.class));
setters.put("Transition:shapeclipped", new BooleanSetter(
- Reflection.findMethodStatic(Transition.class, "setClipped", boolean.class)));
+ Reflection.findMethod(Transition.class, "setClipped", boolean.class)));
setters.put("Transition:shapeparameter", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setShapeParameter", double.class)));
+ Reflection.findMethod(Transition.class, "setShapeParameter", double.class)));
setters.put("Transition:foreradius", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setForeRadius", double.class),
+ Reflection.findMethod(Transition.class, "setForeRadius", double.class),
"auto",
- Reflection.findMethodStatic(Transition.class, "setForeRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(Transition.class, "setForeRadiusAutomatic", boolean.class)));
setters.put("Transition:aftradius", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setAftRadius", double.class),
+ Reflection.findMethod(Transition.class, "setAftRadius", double.class),
"auto",
- Reflection.findMethodStatic(Transition.class, "setAftRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(Transition.class, "setAftRadiusAutomatic", boolean.class)));
setters.put("Transition:foreshoulderradius", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setForeShoulderRadius", double.class)));
+ Reflection.findMethod(Transition.class, "setForeShoulderRadius", double.class)));
setters.put("Transition:foreshoulderlength", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setForeShoulderLength", double.class)));
+ Reflection.findMethod(Transition.class, "setForeShoulderLength", double.class)));
setters.put("Transition:foreshoulderthickness", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setForeShoulderThickness", double.class)));
+ Reflection.findMethod(Transition.class, "setForeShoulderThickness", double.class)));
setters.put("Transition:foreshouldercapped", new BooleanSetter(
- Reflection.findMethodStatic(Transition.class, "setForeShoulderCapped", boolean.class)));
+ Reflection.findMethod(Transition.class, "setForeShoulderCapped", boolean.class)));
setters.put("Transition:aftshoulderradius", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setAftShoulderRadius", double.class)));
+ Reflection.findMethod(Transition.class, "setAftShoulderRadius", double.class)));
setters.put("Transition:aftshoulderlength", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setAftShoulderLength", double.class)));
+ Reflection.findMethod(Transition.class, "setAftShoulderLength", double.class)));
setters.put("Transition:aftshoulderthickness", new DoubleSetter(
- Reflection.findMethodStatic(Transition.class, "setAftShoulderThickness", double.class)));
+ Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class)));
setters.put("Transition:aftshouldercapped", new BooleanSetter(
- Reflection.findMethodStatic(Transition.class, "setAftShoulderCapped", boolean.class)));
+ Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class)));
// NoseCone - disable disallowed elements
setters.put("NoseCone:foreradius", null);
// FinSet
setters.put("FinSet:fincount", new IntSetter(
- Reflection.findMethodStatic(FinSet.class, "setFinCount", int.class)));
+ Reflection.findMethod(FinSet.class, "setFinCount", int.class)));
setters.put("FinSet:rotation", new DoubleSetter(
- Reflection.findMethodStatic(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0));
+ Reflection.findMethod(FinSet.class, "setBaseRotation", double.class), Math.PI / 180.0));
setters.put("FinSet:thickness", new DoubleSetter(
- Reflection.findMethodStatic(FinSet.class, "setThickness", double.class)));
+ Reflection.findMethod(FinSet.class, "setThickness", double.class)));
setters.put("FinSet:crosssection", new EnumSetter<FinSet.CrossSection>(
- Reflection.findMethodStatic(FinSet.class, "setCrossSection", FinSet.CrossSection.class),
+ Reflection.findMethod(FinSet.class, "setCrossSection", FinSet.CrossSection.class),
FinSet.CrossSection.class));
setters.put("FinSet:cant", new DoubleSetter(
- Reflection.findMethodStatic(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0));
+ Reflection.findMethod(FinSet.class, "setCantAngle", double.class), Math.PI / 180.0));
setters.put("FinSet:tabheight", new DoubleSetter(
- Reflection.findMethodStatic(FinSet.class, "setTabHeight", double.class)));
+ Reflection.findMethod(FinSet.class, "setTabHeight", double.class)));
setters.put("FinSet:tablength", new DoubleSetter(
- Reflection.findMethodStatic(FinSet.class, "setTabLength", double.class)));
+ Reflection.findMethod(FinSet.class, "setTabLength", double.class)));
setters.put("FinSet:tabposition", new FinTabPositionSetter());
// TrapezoidFinSet
setters.put("TrapezoidFinSet:rootchord", new DoubleSetter(
- Reflection.findMethodStatic(TrapezoidFinSet.class, "setRootChord", double.class)));
+ Reflection.findMethod(TrapezoidFinSet.class, "setRootChord", double.class)));
setters.put("TrapezoidFinSet:tipchord", new DoubleSetter(
- Reflection.findMethodStatic(TrapezoidFinSet.class, "setTipChord", double.class)));
+ Reflection.findMethod(TrapezoidFinSet.class, "setTipChord", double.class)));
setters.put("TrapezoidFinSet:sweeplength", new DoubleSetter(
- Reflection.findMethodStatic(TrapezoidFinSet.class, "setSweep", double.class)));
+ Reflection.findMethod(TrapezoidFinSet.class, "setSweep", double.class)));
setters.put("TrapezoidFinSet:height", new DoubleSetter(
- Reflection.findMethodStatic(TrapezoidFinSet.class, "setHeight", double.class)));
+ Reflection.findMethod(TrapezoidFinSet.class, "setHeight", double.class)));
// EllipticalFinSet
setters.put("EllipticalFinSet:rootchord", new DoubleSetter(
- Reflection.findMethodStatic(EllipticalFinSet.class, "setLength", double.class)));
+ Reflection.findMethod(EllipticalFinSet.class, "setLength", double.class)));
setters.put("EllipticalFinSet:height", new DoubleSetter(
- Reflection.findMethodStatic(EllipticalFinSet.class, "setHeight", double.class)));
+ Reflection.findMethod(EllipticalFinSet.class, "setHeight", double.class)));
// FreeformFinSet points handled as a special handler
// LaunchLug
setters.put("LaunchLug:radius", new DoubleSetter(
- Reflection.findMethodStatic(LaunchLug.class, "setOuterRadius", double.class)));
+ Reflection.findMethod(LaunchLug.class, "setOuterRadius", double.class)));
setters.put("LaunchLug:length", new DoubleSetter(
- Reflection.findMethodStatic(LaunchLug.class, "setLength", double.class)));
+ Reflection.findMethod(LaunchLug.class, "setLength", double.class)));
setters.put("LaunchLug:thickness", new DoubleSetter(
- Reflection.findMethodStatic(LaunchLug.class, "setThickness", double.class)));
+ Reflection.findMethod(LaunchLug.class, "setThickness", double.class)));
setters.put("LaunchLug:radialdirection", new DoubleSetter(
- Reflection.findMethodStatic(LaunchLug.class, "setRadialDirection", double.class),
+ Reflection.findMethod(LaunchLug.class, "setRadialDirection", double.class),
Math.PI / 180.0));
// InternalComponent - nothing
// StructuralComponent
setters.put("StructuralComponent:material", new MaterialSetter(
- Reflection.findMethodStatic(StructuralComponent.class, "setMaterial", Material.class),
+ Reflection.findMethod(StructuralComponent.class, "setMaterial", Material.class),
Material.Type.BULK));
// RingComponent
setters.put("RingComponent:length", new DoubleSetter(
- Reflection.findMethodStatic(RingComponent.class, "setLength", double.class)));
+ Reflection.findMethod(RingComponent.class, "setLength", double.class)));
setters.put("RingComponent:radialposition", new DoubleSetter(
- Reflection.findMethodStatic(RingComponent.class, "setRadialPosition", double.class)));
+ Reflection.findMethod(RingComponent.class, "setRadialPosition", double.class)));
setters.put("RingComponent:radialdirection", new DoubleSetter(
- Reflection.findMethodStatic(RingComponent.class, "setRadialDirection", double.class),
+ Reflection.findMethod(RingComponent.class, "setRadialDirection", double.class),
Math.PI / 180.0));
// ThicknessRingComponent - radius on separate components due to differing automatics
setters.put("ThicknessRingComponent:thickness", new DoubleSetter(
- Reflection.findMethodStatic(ThicknessRingComponent.class, "setThickness", double.class)));
+ Reflection.findMethod(ThicknessRingComponent.class, "setThickness", double.class)));
// EngineBlock
setters.put("EngineBlock:outerradius", new DoubleSetter(
- Reflection.findMethodStatic(EngineBlock.class, "setOuterRadius", double.class),
+ Reflection.findMethod(EngineBlock.class, "setOuterRadius", double.class),
"auto",
- Reflection.findMethodStatic(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(EngineBlock.class, "setOuterRadiusAutomatic", boolean.class)));
// TubeCoupler
setters.put("TubeCoupler:outerradius", new DoubleSetter(
- Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadius", double.class),
+ Reflection.findMethod(TubeCoupler.class, "setOuterRadius", double.class),
"auto",
- Reflection.findMethodStatic(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(TubeCoupler.class, "setOuterRadiusAutomatic", boolean.class)));
// InnerTube
setters.put("InnerTube:outerradius", new DoubleSetter(
- Reflection.findMethodStatic(InnerTube.class, "setOuterRadius", double.class)));
+ Reflection.findMethod(InnerTube.class, "setOuterRadius", double.class)));
setters.put("InnerTube:clusterconfiguration", new ClusterConfigurationSetter());
setters.put("InnerTube:clusterscale", new DoubleSetter(
- Reflection.findMethodStatic(InnerTube.class, "setClusterScale", double.class)));
+ Reflection.findMethod(InnerTube.class, "setClusterScale", double.class)));
setters.put("InnerTube:clusterrotation", new DoubleSetter(
- Reflection.findMethodStatic(InnerTube.class, "setClusterRotation", double.class),
+ Reflection.findMethod(InnerTube.class, "setClusterRotation", double.class),
Math.PI / 180.0));
// RadiusRingComponent
// Bulkhead
setters.put("RadiusRingComponent:innerradius", new DoubleSetter(
- Reflection.findMethodStatic(RadiusRingComponent.class, "setInnerRadius", double.class)));
+ Reflection.findMethod(RadiusRingComponent.class, "setInnerRadius", double.class)));
setters.put("Bulkhead:outerradius", new DoubleSetter(
- Reflection.findMethodStatic(Bulkhead.class, "setOuterRadius", double.class),
+ Reflection.findMethod(Bulkhead.class, "setOuterRadius", double.class),
"auto",
- Reflection.findMethodStatic(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(Bulkhead.class, "setOuterRadiusAutomatic", boolean.class)));
// CenteringRing
setters.put("CenteringRing:innerradius", new DoubleSetter(
- Reflection.findMethodStatic(CenteringRing.class, "setInnerRadius", double.class),
+ Reflection.findMethod(CenteringRing.class, "setInnerRadius", double.class),
"auto",
- Reflection.findMethodStatic(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(CenteringRing.class, "setInnerRadiusAutomatic", boolean.class)));
setters.put("CenteringRing:outerradius", new DoubleSetter(
- Reflection.findMethodStatic(CenteringRing.class, "setOuterRadius", double.class),
+ Reflection.findMethod(CenteringRing.class, "setOuterRadius", double.class),
"auto",
- Reflection.findMethodStatic(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class)));
+ Reflection.findMethod(CenteringRing.class, "setOuterRadiusAutomatic", boolean.class)));
// MassObject
setters.put("MassObject:packedlength", new DoubleSetter(
- Reflection.findMethodStatic(MassObject.class, "setLength", double.class)));
+ Reflection.findMethod(MassObject.class, "setLength", double.class)));
setters.put("MassObject:packedradius", new DoubleSetter(
- Reflection.findMethodStatic(MassObject.class, "setRadius", double.class)));
+ Reflection.findMethod(MassObject.class, "setRadius", double.class)));
setters.put("MassObject:radialposition", new DoubleSetter(
- Reflection.findMethodStatic(MassObject.class, "setRadialPosition", double.class)));
+ Reflection.findMethod(MassObject.class, "setRadialPosition", double.class)));
setters.put("MassObject:radialdirection", new DoubleSetter(
- Reflection.findMethodStatic(MassObject.class, "setRadialDirection", double.class),
+ Reflection.findMethod(MassObject.class, "setRadialDirection", double.class),
Math.PI / 180.0));
// MassComponent
setters.put("MassComponent:mass", new DoubleSetter(
- Reflection.findMethodStatic(MassComponent.class, "setComponentMass", double.class)));
+ Reflection.findMethod(MassComponent.class, "setComponentMass", double.class)));
// ShockCord
setters.put("ShockCord:cordlength", new DoubleSetter(
- Reflection.findMethodStatic(ShockCord.class, "setCordLength", double.class)));
+ Reflection.findMethod(ShockCord.class, "setCordLength", double.class)));
setters.put("ShockCord:material", new MaterialSetter(
- Reflection.findMethodStatic(ShockCord.class, "setMaterial", Material.class),
+ Reflection.findMethod(ShockCord.class, "setMaterial", Material.class),
Material.Type.LINE));
// RecoveryDevice
setters.put("RecoveryDevice:cd", new DoubleSetter(
- Reflection.findMethodStatic(RecoveryDevice.class, "setCD", double.class),
+ Reflection.findMethod(RecoveryDevice.class, "setCD", double.class),
"auto",
- Reflection.findMethodStatic(RecoveryDevice.class, "setCDAutomatic", boolean.class)));
+ Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class)));
setters.put("RecoveryDevice:deployevent", new EnumSetter<RecoveryDevice.DeployEvent>(
- Reflection.findMethodStatic(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class),
+ Reflection.findMethod(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class),
RecoveryDevice.DeployEvent.class));
setters.put("RecoveryDevice:deployaltitude", new DoubleSetter(
- Reflection.findMethodStatic(RecoveryDevice.class, "setDeployAltitude", double.class)));
+ Reflection.findMethod(RecoveryDevice.class, "setDeployAltitude", double.class)));
setters.put("RecoveryDevice:deploydelay", new DoubleSetter(
- Reflection.findMethodStatic(RecoveryDevice.class, "setDeployDelay", double.class)));
+ Reflection.findMethod(RecoveryDevice.class, "setDeployDelay", double.class)));
setters.put("RecoveryDevice:material", new MaterialSetter(
- Reflection.findMethodStatic(RecoveryDevice.class, "setMaterial", Material.class),
+ Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class),
Material.Type.SURFACE));
// Parachute
setters.put("Parachute:diameter", new DoubleSetter(
- Reflection.findMethodStatic(Parachute.class, "setDiameter", double.class)));
+ Reflection.findMethod(Parachute.class, "setDiameter", double.class)));
setters.put("Parachute:linecount", new IntSetter(
- Reflection.findMethodStatic(Parachute.class, "setLineCount", int.class)));
+ Reflection.findMethod(Parachute.class, "setLineCount", int.class)));
setters.put("Parachute:linelength", new DoubleSetter(
- Reflection.findMethodStatic(Parachute.class, "setLineLength", double.class)));
+ Reflection.findMethod(Parachute.class, "setLineLength", double.class)));
setters.put("Parachute:linematerial", new MaterialSetter(
- Reflection.findMethodStatic(Parachute.class, "setLineMaterial", Material.class),
+ Reflection.findMethod(Parachute.class, "setLineMaterial", Material.class),
Material.Type.LINE));
// Streamer
setters.put("Streamer:striplength", new DoubleSetter(
- Reflection.findMethodStatic(Streamer.class, "setStripLength", double.class)));
+ Reflection.findMethod(Streamer.class, "setStripLength", double.class)));
setters.put("Streamer:stripwidth", new DoubleSetter(
- Reflection.findMethodStatic(Streamer.class, "setStripWidth", double.class)));
+ Reflection.findMethod(Streamer.class, "setStripWidth", double.class)));
// Rocket
// <motorconfiguration> handled by separate handler
setters.put("Rocket:referencetype", new EnumSetter<ReferenceType>(
- Reflection.findMethodStatic(Rocket.class, "setReferenceType", ReferenceType.class),
+ Reflection.findMethod(Rocket.class, "setReferenceType", ReferenceType.class),
ReferenceType.class));
setters.put("Rocket:customreference", new DoubleSetter(
- Reflection.findMethodStatic(Rocket.class, "setCustomReferenceLength", double.class)));
+ Reflection.findMethod(Rocket.class, "setCustomReferenceLength", double.class)));
setters.put("Rocket:designer", new StringSetter(
- Reflection.findMethodStatic(Rocket.class, "setDesigner", String.class)));
+ Reflection.findMethod(Rocket.class, "setDesigner", String.class)));
setters.put("Rocket:revision", new StringSetter(
- Reflection.findMethodStatic(Rocket.class, "setRevision", String.class)));
+ Reflection.findMethod(Rocket.class, "setRevision", String.class)));
}
class FinTabPositionSetter extends DoubleSetter {
public FinTabPositionSetter() {
- super(Reflection.findMethodStatic(FinSet.class, "setTabShift", double.class));
+ super(Reflection.findMethod(FinSet.class, "setTabShift", double.class));
}
@Override
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;
--- /dev/null
+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;
+ }
+ }
+}
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;
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);
}
-
- 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.
panel.add(label);
m = new DoubleModel(component, "CantAngle", UnitGroup.UNITS_ANGLE,
- -FinSet.MAX_CANT,FinSet.MAX_CANT);
+ -FinSet.MAX_CANT, FinSet.MAX_CANT);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
- panel.add(spin,"growx");
+ panel.add(spin, "growx");
- panel.add(new UnitSelector(m),"growx");
- panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT,FinSet.MAX_CANT)),
+ panel.add(new UnitSelector(m), "growx");
+ panel.add(new BasicSlider(m.getSliderModel(-FinSet.MAX_CANT, FinSet.MAX_CANT)),
"w 100lp, wrap 40lp");
-
-
+
+
//// Position
//// Position relative to:
- panel.add(new JLabel("FreeformFinSetCfg.lbl.Posrelativeto"));
-
+ panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Posrelativeto")));
+
combo = new JComboBox(
new EnumModel<RocketComponent.Position>(component, "RelativePosition",
new RocketComponent.Position[] {
- RocketComponent.Position.TOP,
- RocketComponent.Position.MIDDLE,
- RocketComponent.Position.BOTTOM,
- RocketComponent.Position.ABSOLUTE
+ RocketComponent.Position.TOP,
+ RocketComponent.Position.MIDDLE,
+ RocketComponent.Position.BOTTOM,
+ RocketComponent.Position.ABSOLUTE
}));
- panel.add(combo,"spanx 3, growx, wrap");
- //// plus
- panel.add(new JLabel("FreeformFinSetCfg.lbl.plus"),"right");
-
- m = new DoubleModel(component,"PositionValue",UnitGroup.UNITS_LENGTH);
+ panel.add(combo, "spanx 3, growx, wrap");
+ //// plus
+ panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.plus")), "right");
+
+ m = new DoubleModel(component, "PositionValue", UnitGroup.UNITS_LENGTH);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
- panel.add(spin,"growx");
+ panel.add(spin, "growx");
- panel.add(new UnitSelector(m),"growx");
+ panel.add(new UnitSelector(m), "growx");
panel.add(new BasicSlider(m.getSliderModel(
new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
new DoubleModel(component.getParent(), "Length"))),
"w 100lp, wrap");
-
-
-
+
+
+
mainPanel.add(panel, "aligny 20%");
mainPanel.add(new JSeparator(SwingConstants.VERTICAL), "growy, height 150lp");
+
+ panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
- panel = new JPanel(new MigLayout("gap rel unrel","[][65lp::][30lp::]",""));
-
-
-
-
+
+
+
//// Cross section
//// Fin cross section:
- panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")),"span, split");
+ panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.FincrossSection")), "span, split");
combo = new JComboBox(
- new EnumModel<FinSet.CrossSection>(component,"CrossSection"));
- panel.add(combo,"growx, wrap unrel");
+ new EnumModel<FinSet.CrossSection>(component, "CrossSection"));
+ panel.add(combo, "growx, wrap unrel");
//// Thickness:
panel.add(new JLabel(trans.get("FreeformFinSetCfg.lbl.Thickness")));
- m = new DoubleModel(component,"Thickness",UnitGroup.UNITS_LENGTH,0);
+ m = new DoubleModel(component, "Thickness", UnitGroup.UNITS_LENGTH, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
- panel.add(spin,"growx");
+ panel.add(spin, "growx");
- panel.add(new UnitSelector(m),"growx");
- panel.add(new BasicSlider(m.getSliderModel(0,0.01)),"w 100lp, wrap 30lp");
+ panel.add(new UnitSelector(m), "growx");
+ panel.add(new BasicSlider(m.getSliderModel(0, 0.01)), "w 100lp, wrap 30lp");
//// Material
materialPanel(panel, Material.Type.BULK);
-
-
+
+
mainPanel.add(panel, "aligny 20%");
return mainPanel;
}
-
+
private JPanel shapePane() {
JPanel panel = new JPanel(new MigLayout("fill"));
-
+
// Create the figure
figure = new FinPointFigure(finset);
ScaleScrollPane figurePane = new FinPointScrollPane();
figurePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
figurePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
-
+
// Create the table
tableModel = new FinPointTableModel();
table = new JTable(tableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- for (int i=0; i < Columns.values().length; i++) {
+ for (int i = 0; i < Columns.values().length; i++) {
table.getColumnModel().getColumn(i).
- setPreferredWidth(Columns.values()[i].getWidth());
+ setPreferredWidth(Columns.values()[i].getWidth());
}
JScrollPane tablePane = new JScrollPane(table);
+
+ // panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
+ // panel.add(new JLabel(" View:"), "wrap, aligny bottom");
-// panel.add(new JLabel("Coordinates:"), "aligny bottom, alignx 50%");
-// panel.add(new JLabel(" View:"), "wrap, aligny bottom");
-
-
- panel.add(tablePane,"growy, width 100lp:100lp:, height 100lp:250lp:");
- panel.add(figurePane,"gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
-
- panel.add(new StyledLabel("Double-click", -2), "alignx 50%");
+
+ panel.add(tablePane, "growy, width 100lp:100lp:, height 100lp:250lp:");
+ panel.add(figurePane, "gap unrel, spanx, growx, growy 1000, height 100lp:250lp:, wrap");
- panel.add(new ScaleSelector(figurePane),"spany 2");
- panel.add(new StyledLabel("Click+drag: Add and move points " +
- "Ctrl+click: Remove point", -2), "spany 2, right, wrap");
+ panel.add(new StyledLabel(trans.get("lbl.doubleClick1"), -2), "alignx 50%");
+ panel.add(new ScaleSelector(figurePane), "spany 2");
+ panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.clickDrag") + " " +
+ trans.get("FreeformFinSetConfig.lbl.ctrlClick"), -2), "spany 2, right, wrap");
- panel.add(new StyledLabel("to edit", -2), "alignx 50%");
+
+ panel.add(new StyledLabel(trans.get("FreeformFinSetConfig.lbl.doubleClick2"), -2), "alignx 50%");
return panel;
}
-
-
-
+
+
+
@Override
public void updateFields() {
super.updateFields();
}
-
-
+
+
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();
finset.addPoint(index);
try {
finset.setPoint(index, point.x, point.y);
- } catch (IllegalFinPointException ignore) { }
+ } catch (IllegalFinPointException ignore) {
+ }
dragIndex = index;
return;
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;
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);
}
}
super.mouseClicked(event);
return;
}
-
+
try {
finset.removePoint(index);
} catch (IllegalFinPointException ignore) {
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;
}
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);
@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());
}
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);
c = c.setY(value);
finset.setPoint(rowIndex, c.x, c.y);
-
+
} catch (NumberFormatException ignore) {
} catch (IllegalFinPointException ignore) {
}
--- /dev/null
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.SpinnerEditor;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.BodyComponent;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.MassComponent;
+import net.sf.openrocket.rocketcomponent.MassObject;
+import net.sf.openrocket.rocketcomponent.Parachute;
+import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
+import net.sf.openrocket.rocketcomponent.RingComponent;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.ShockCord;
+import net.sf.openrocket.rocketcomponent.Streamer;
+import net.sf.openrocket.rocketcomponent.SymmetricComponent;
+import net.sf.openrocket.rocketcomponent.ThicknessRingComponent;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.Reflection;
+import net.sf.openrocket.util.Reflection.Method;
+
+/**
+ * Dialog that allows scaling the rocket design.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ScaleDialog extends JDialog {
+
+ private static final LogHelper log = Application.getLogger();
+ private static final Translator trans = Application.getTranslator();
+
+
+ /*
+ * Scaler implementations
+ *
+ * Each scaled value (except override cg/mass) is defined using a Scaler instance.
+ */
+ private static final Map<Class<? extends RocketComponent>, List<Scaler>> SCALERS =
+ new HashMap<Class<? extends RocketComponent>, List<Scaler>>();
+ static {
+ List<Scaler> list;
+
+ // RocketComponent
+ addScaler(RocketComponent.class, "PositionValue");
+ SCALERS.get(RocketComponent.class).add(new OverrideScaler());
+
+ // BodyComponent
+ addScaler(BodyComponent.class, "Length");
+
+ // SymmetricComponent
+ addScaler(SymmetricComponent.class, "Thickness", "isFilled");
+
+ // Transition + Nose cone
+ addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic");
+ addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic");
+ addScaler(Transition.class, "ForeShoulderRadius");
+ addScaler(Transition.class, "ForeShoulderThickness");
+ addScaler(Transition.class, "ForeShoulderLength");
+ addScaler(Transition.class, "AftShoulderRadius");
+ addScaler(Transition.class, "AftShoulderThickness");
+ addScaler(Transition.class, "AftShoulderLength");
+
+ // Body tube
+ addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic");
+ addScaler(BodyTube.class, "MotorOverhang");
+
+ // Launch lug
+ addScaler(LaunchLug.class, "OuterRadius");
+ addScaler(LaunchLug.class, "Thickness");
+ addScaler(LaunchLug.class, "Length");
+
+ // FinSet
+ addScaler(FinSet.class, "Thickness");
+ addScaler(FinSet.class, "TabHeight");
+ addScaler(FinSet.class, "TabLength");
+ addScaler(FinSet.class, "TabShift");
+
+ // TrapezoidFinSet
+ addScaler(TrapezoidFinSet.class, "Sweep");
+ addScaler(TrapezoidFinSet.class, "RootChord");
+ addScaler(TrapezoidFinSet.class, "TipChord");
+ addScaler(TrapezoidFinSet.class, "Height");
+
+ // EllipticalFinSet
+ addScaler(EllipticalFinSet.class, "Length");
+ addScaler(EllipticalFinSet.class, "Height");
+
+ // FreeformFinSet
+ list = new ArrayList<ScaleDialog.Scaler>(1);
+ list.add(new FreeformFinSetScaler());
+ SCALERS.put(FreeformFinSet.class, list);
+
+ // MassObject
+ addScaler(MassObject.class, "Length");
+ addScaler(MassObject.class, "Radius");
+ addScaler(MassObject.class, "RadialPosition");
+
+ // MassComponent
+ list = new ArrayList<ScaleDialog.Scaler>(1);
+ list.add(new MassComponentScaler());
+ SCALERS.put(MassComponent.class, list);
+
+ // Parachute
+ addScaler(Parachute.class, "Diameter");
+ addScaler(Parachute.class, "LineLength");
+
+ // Streamer
+ addScaler(Streamer.class, "StripLength");
+ addScaler(Streamer.class, "StripWidth");
+
+ // ShockCord
+ addScaler(ShockCord.class, "CordLength");
+
+ // RingComponent
+ addScaler(RingComponent.class, "Length");
+ addScaler(RingComponent.class, "RadialPosition");
+
+ // ThicknessRingComponent
+ addScaler(ThicknessRingComponent.class, "OuterRadius", "isOuterRadiusAutomatic");
+ addScaler(ThicknessRingComponent.class, "Thickness");
+
+ // InnerTube
+ addScaler(InnerTube.class, "MotorOverhang");
+
+ // RadiusRingComponent
+ addScaler(RadiusRingComponent.class, "OuterRadius", "isOuterRadiusAutomatic");
+ addScaler(RadiusRingComponent.class, "InnerRadius", "isInnerRadiusAutomatic");
+ }
+
+ private static void addScaler(Class<? extends RocketComponent> componentClass, String methodName) {
+ addScaler(componentClass, methodName, null);
+ }
+
+ private static void addScaler(Class<? extends RocketComponent> componentClass, String methodName, String autoMethodName) {
+ List<Scaler> list = SCALERS.get(componentClass);
+ if (list == null) {
+ list = new ArrayList<ScaleDialog.Scaler>();
+ SCALERS.put(componentClass, list);
+ }
+ list.add(new GeneralScaler(componentClass, methodName, autoMethodName));
+ }
+
+
+
+
+
+ private static final double DEFAULT_INITIAL_SIZE = 0.1; // meters
+ private static final double SCALE_MIN = 0.01;
+ private static final double SCALE_MAX = 100.0;
+
+ private static final String SCALE_ROCKET = trans.get("lbl.scaleRocket");
+ private static final String SCALE_SUBSELECTION = trans.get("lbl.scaleSubselection");
+ private static final String SCALE_SELECTION = trans.get("lbl.scaleSelection");
+
+
+
+
+ private final DoubleModel multiplier = new DoubleModel(1.0, UnitGroup.UNITS_RELATIVE, SCALE_MIN, SCALE_MAX);
+ private final DoubleModel fromField = new DoubleModel(0, UnitGroup.UNITS_LENGTH, 0);
+ private final DoubleModel toField = new DoubleModel(0, UnitGroup.UNITS_LENGTH, 0);
+
+ private final OpenRocketDocument document;
+ private final RocketComponent selection;
+
+ private final JComboBox selectionOption;
+ private final JCheckBox scaleMassValues;
+
+ private boolean changing = false;
+
+ // FIXME: Localize
+
+ /**
+ * Sole constructor.
+ *
+ * @param document the document to modify.
+ * @param selection the currently selected component (or <code>null</code> if none selected).
+ * @param parent the parent window.
+ */
+ public ScaleDialog(OpenRocketDocument document, RocketComponent selection, Window parent) {
+ super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
+
+ this.document = document;
+ this.selection = selection;
+
+ // Generate options for scaling
+ List<String> options = new ArrayList<String>();
+ options.add(SCALE_ROCKET);
+ if (selection != null && selection.getChildCount() > 0) {
+ options.add(SCALE_SUBSELECTION);
+ }
+ if (selection != null) {
+ options.add(SCALE_SELECTION);
+ }
+
+
+ /*
+ * Select initial size for "from" field.
+ *
+ * If a component is selected, either its diameter (for SymmetricComponents) or length is selected.
+ * Otherwise the maximum body diameter is selected. As a fallback DEFAULT_INITIAL_SIZE is used.
+ */
+ //
+ double initialSize = 0;
+ if (selection != null) {
+ if (selection instanceof SymmetricComponent) {
+ SymmetricComponent s = (SymmetricComponent) selection;
+ initialSize = s.getForeRadius() * 2;
+ initialSize = MathUtil.max(initialSize, s.getAftRadius() * 2);
+ } else {
+ initialSize = selection.getLength();
+ }
+ } else {
+ for (RocketComponent c : document.getRocket()) {
+ if (c instanceof SymmetricComponent) {
+ SymmetricComponent s = (SymmetricComponent) c;
+ initialSize = s.getForeRadius() * 2;
+ initialSize = MathUtil.max(initialSize, s.getAftRadius() * 2);
+ }
+ }
+ }
+ if (initialSize < 0.001) {
+ Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
+ initialSize = unit.fromUnit(unit.round(unit.toUnit(DEFAULT_INITIAL_SIZE)));
+ }
+
+ fromField.setValue(initialSize);
+ toField.setValue(initialSize);
+
+
+ // Add actions to the values
+ multiplier.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (!changing) {
+ changing = true;
+ updateToField();
+ changing = false;
+ }
+ }
+ });
+ fromField.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (!changing) {
+ changing = true;
+ updateToField();
+ changing = false;
+ }
+ }
+ });
+ toField.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (!changing) {
+ changing = true;
+ updateMultiplier();
+ changing = false;
+ }
+ }
+ });
+
+
+
+ String tip;
+ JPanel panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", ""));
+ this.add(panel);
+
+
+ // Scaling selection
+ tip = trans.get("lbl.scale.ttip");
+ JLabel label = new JLabel(trans.get("lbl.scale"));
+ label.setToolTipText(tip);
+ panel.add(label, "span, split, gapright unrel");
+
+ selectionOption = new JComboBox(options.toArray());
+ selectionOption.setEditable(false);
+ selectionOption.setToolTipText(tip);
+ panel.add(selectionOption, "growx, wrap para*2");
+
+
+ // Scale multiplier
+ tip = trans.get("lbl.scaling.ttip");
+ label = new JLabel(trans.get("lbl.scaling"));
+ label.setToolTipText(tip);
+ panel.add(label, "gapright unrel");
+
+
+ JSpinner spin = new JSpinner(multiplier.getSpinnerModel());
+ spin.setEditor(new SpinnerEditor(spin));
+ spin.setToolTipText(tip);
+ panel.add(spin, "w :30lp:65lp");
+
+ UnitSelector unit = new UnitSelector(multiplier);
+ unit.setToolTipText(tip);
+ panel.add(unit, "w 30lp");
+ BasicSlider slider = new BasicSlider(multiplier.getSliderModel(0.25, 1.0, 4.0));
+ slider.setToolTipText(tip);
+ panel.add(slider, "w 100lp, growx, wrap para");
+
+
+ // Scale from ... to ...
+ tip = trans.get("lbl.scaleFromTo.ttip");
+ label = new JLabel(trans.get("lbl.scaleFrom"));
+ label.setToolTipText(tip);
+ panel.add(label, "gapright unrel, right");
+
+ spin = new JSpinner(fromField.getSpinnerModel());
+ spin.setEditor(new SpinnerEditor(spin));
+ spin.setToolTipText(tip);
+ panel.add(spin, "span, split, w :30lp:65lp");
+
+ unit = new UnitSelector(fromField);
+ unit.setToolTipText(tip);
+ panel.add(unit, "w 30lp");
+
+ label = new JLabel(trans.get("lbl.scaleTo"));
+ label.setToolTipText(tip);
+ panel.add(label, "gap unrel");
+
+ spin = new JSpinner(toField.getSpinnerModel());
+ spin.setEditor(new SpinnerEditor(spin));
+ spin.setToolTipText(tip);
+ panel.add(spin, "w :30lp:65lp");
+
+ unit = new UnitSelector(toField);
+ unit.setToolTipText(tip);
+ panel.add(unit, "w 30lp, wrap para*2");
+
+
+ // Scale override
+ scaleMassValues = new JCheckBox(trans.get("checkbox.scaleMass"));
+ scaleMassValues.setToolTipText(trans.get("checkbox.scaleMass.ttip"));
+ scaleMassValues.setSelected(true);
+ boolean overridden = false;
+ for (RocketComponent c : document.getRocket()) {
+ if (c instanceof MassComponent || c.isMassOverridden()) {
+ overridden = true;
+ break;
+ }
+ }
+ scaleMassValues.setEnabled(overridden);
+ panel.add(scaleMassValues, "span, wrap para*3");
+
+
+ // Buttons
+
+ JButton scale = new JButton(trans.get("button.scale"));
+ scale.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ doScale();
+ ScaleDialog.this.setVisible(false);
+ }
+ });
+ panel.add(scale, "span, split, right, gap para");
+
+ JButton cancel = new JButton(trans.get("button.cancel"));
+ cancel.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ ScaleDialog.this.setVisible(false);
+ }
+ });
+ panel.add(cancel, "right, gap para");
+
+
+
+ GUIUtil.setDisposableDialogOptions(this, scale);
+ }
+
+
+
+ private void doScale() {
+ double mul = multiplier.getValue();
+ if (!(SCALE_MIN <= mul && mul <= SCALE_MAX)) {
+ ExceptionHandler.handleErrorCondition("Illegal multiplier value, mul=" + mul);
+ return;
+ }
+
+ if (MathUtil.equals(mul, 1.0)) {
+ // Nothing to do
+ log.user("Scaling by value 1.0 - nothing to do");
+ return;
+ }
+
+ boolean scaleMass = scaleMassValues.isSelected();
+
+ Object item = selectionOption.getSelectedItem();
+ log.user("Scaling design by factor " + mul + ", option=" + item);
+ if (SCALE_ROCKET.equals(item)) {
+
+ // Scale the entire rocket design
+ try {
+ document.startUndo("Scale rocket");
+ for (RocketComponent c : document.getRocket()) {
+ scale(c, mul, scaleMass);
+ }
+ } finally {
+ document.stopUndo();
+ }
+
+ } else if (SCALE_SUBSELECTION.equals(item)) {
+
+ // Scale component and subcomponents
+ try {
+ document.startUndo("Scale components");
+ for (RocketComponent c : selection) {
+ scale(c, mul, scaleMass);
+ }
+ } finally {
+ document.stopUndo();
+ }
+
+ } else if (SCALE_SELECTION.equals(item)) {
+
+ // Scale only the selected component
+ try {
+ document.startUndo("Scale component");
+ scale(selection, mul, scaleMass);
+ } finally {
+ document.stopUndo();
+ }
+
+ } else {
+ throw new BugException("Unknown item selected, item=" + item);
+ }
+ }
+
+
+ /**
+ * Perform scaling on a single component.
+ */
+ private void scale(RocketComponent component, double mul, boolean scaleMass) {
+
+ Class<?> clazz = component.getClass();
+ while (clazz != null) {
+ List<Scaler> list = SCALERS.get(clazz);
+ if (list != null) {
+ for (Scaler s : list) {
+ s.scale(component, mul, scaleMass);
+ }
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+
+ private void updateToField() {
+ double mul = multiplier.getValue();
+ double from = fromField.getValue();
+ double to = from * mul;
+ toField.setValue(to);
+ }
+
+ private void updateMultiplier() {
+ double from = fromField.getValue();
+ double to = toField.getValue();
+ double mul = to / from;
+
+ if (!MathUtil.equals(from, 0)) {
+ mul = MathUtil.clamp(mul, SCALE_MIN, SCALE_MAX);
+ multiplier.setValue(mul);
+ }
+ updateToField();
+ }
+
+
+
+ /**
+ * Interface for scaling a specific component/value.
+ */
+ private interface Scaler {
+ public void scale(RocketComponent c, double multiplier, boolean scaleMass);
+ }
+
+ /**
+ * General scaler implementation that uses reflection to get/set a specific value.
+ */
+ private static class GeneralScaler implements Scaler {
+
+ private final Method getter;
+ private final Method setter;
+ private final Method autoMethod;
+
+ public GeneralScaler(Class<? extends RocketComponent> componentClass, String methodName, String autoMethodName) {
+
+ getter = Reflection.findMethod(componentClass, "get" + methodName);
+ setter = Reflection.findMethod(componentClass, "set" + methodName, double.class);
+ if (autoMethodName != null) {
+ autoMethod = Reflection.findMethod(componentClass, autoMethodName);
+ } else {
+ autoMethod = null;
+ }
+
+ }
+
+ @Override
+ public void scale(RocketComponent c, double multiplier, boolean scaleMass) {
+
+ // Do not scale if set to automatic
+ if (autoMethod != null) {
+ boolean auto = (Boolean) autoMethod.invoke(c);
+ if (auto) {
+ return;
+ }
+ }
+
+ // Scale value
+ double value = (Double) getter.invoke(c);
+ value = value * multiplier;
+ setter.invoke(c, value);
+ }
+
+ }
+
+
+ private static class OverrideScaler implements Scaler {
+
+ @Override
+ public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+
+ if (component.isCGOverridden()) {
+ double cgx = component.getOverrideCGX();
+ cgx = cgx * multiplier;
+ component.setOverrideCGX(cgx);
+ }
+
+ if (scaleMass && component.isMassOverridden()) {
+ double mass = component.getOverrideMass();
+ mass = mass * MathUtil.pow3(multiplier);
+ component.setOverrideMass(mass);
+ }
+ }
+
+ }
+
+ private static class MassComponentScaler implements Scaler {
+
+ @Override
+ public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+ if (scaleMass) {
+ MassComponent c = (MassComponent) component;
+ double mass = c.getComponentMass();
+ mass = mass * MathUtil.pow3(multiplier);
+ c.setComponentMass(mass);
+ }
+ }
+
+ }
+
+ private static class FreeformFinSetScaler implements Scaler {
+
+ @Override
+ public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
+ FreeformFinSet finset = (FreeformFinSet) component;
+ Coordinate[] points = finset.getFinPoints();
+ for (int i = 0; i < points.length; i++) {
+ points[i] = points[i].multiply(multiplier);
+ }
+ try {
+ finset.setPoints(points);
+ } catch (IllegalFinPointException e) {
+ throw new BugException("Failed to set points after scaling, original=" + Arrays.toString(finset.getFinPoints()) + " scaled=" + Arrays.toString(points), e);
+ }
+ }
+
+ }
+
+}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.ServiceLoader;
import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
-import net.sf.openrocket.optimization.services.OptimizableParameterService;
-import net.sf.openrocket.optimization.services.SimulationModifierService;
+import net.sf.openrocket.optimization.services.OptimizationServiceHelper;
+import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
+import net.sf.openrocket.util.GUIUtil;
public class GeneralOptimizationDialog extends JDialog {
+ private static final Translator trans = Application.getTranslator();
private final List<OptimizableParameter> optimizationParameters = new ArrayList<OptimizableParameter>();
private final Map<Object, List<SimulationModifier>> simulationModifiers =
new HashMap<Object, List<SimulationModifier>>();
- private static final Translator trans = Application.getTranslator();
-
+
- private final OpenRocketDocument document;
+ private final OpenRocketDocument baseDocument;
+ private final Rocket rocketCopy;
+
public GeneralOptimizationDialog(OpenRocketDocument document, Window parent) {
- this.document = document;
+ super(parent, "Rocket optimization");
+
+ this.baseDocument = document;
+ this.rocketCopy = document.getRocket().copyWithOriginalID();
loadOptimizationParameters();
loadSimulationModifiers();
+
+
+ JPanel panel = new JPanel(new MigLayout("fill"));
+
+
+ JTree tree = new SimulationModifierTree(rocketCopy, simulationModifiers);
+ JScrollPane scroll = new JScrollPane(tree);
+ panel.add(scroll, "width 300lp, height 300lp");
+
+
+ this.add(panel);
+ GUIUtil.setDisposableDialogOptions(this, null);
}
private void loadOptimizationParameters() {
- ServiceLoader<OptimizableParameterService> loader =
- ServiceLoader.load(OptimizableParameterService.class);
-
- for (OptimizableParameterService g : loader) {
- optimizationParameters.addAll(g.getParameters(document));
- }
+ optimizationParameters.addAll(OptimizationServiceHelper.getOptimizableParameters(baseDocument));
if (optimizationParameters.isEmpty()) {
throw new BugException("No rocket optimization parameters found, distribution built wrong.");
private void loadSimulationModifiers() {
- ServiceLoader<SimulationModifierService> loader = ServiceLoader.load(SimulationModifierService.class);
- for (SimulationModifierService g : loader) {
- for (SimulationModifier m : g.getModifiers(document)) {
- Object key = m.getRelatedObject();
- List<SimulationModifier> list = simulationModifiers.get(key);
- if (list == null) {
- list = new ArrayList<SimulationModifier>();
- simulationModifiers.put(key, list);
- }
- list.add(m);
+ for (SimulationModifier m : OptimizationServiceHelper.getSimulationModifiers(baseDocument)) {
+ Object key = m.getRelatedObject();
+ List<SimulationModifier> list = simulationModifiers.get(key);
+ if (list == null) {
+ list = new ArrayList<SimulationModifier>();
+ simulationModifiers.put(key, list);
}
+ list.add(m);
}
for (Object key : simulationModifiers.keySet()) {
}
+
+
}
--- /dev/null
+package net.sf.openrocket.gui.dialogs.optimization;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import net.sf.openrocket.gui.components.BasicTree;
+import net.sf.openrocket.gui.main.ComponentIcons;
+import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+/**
+ * A tree that displays the simulation modifiers in a tree structure.
+ * <p>
+ * All nodes in the model are instances of DefaultMutableTreeNode. The user objects
+ * within are either of type RocketComponent, String or SimulationModifier.
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SimulationModifierTree extends BasicTree {
+
+ /**
+ * Sole constructor.
+ *
+ * @param rocket the rocket.
+ * @param simulationModifiers the simulation modifiers, ordered and mapped by components
+ */
+ public SimulationModifierTree(Rocket rocket, Map<Object, List<SimulationModifier>> simulationModifiers) {
+ super(createModifierTree(rocket, simulationModifiers));
+
+ this.setCellRenderer(new ComponentModifierTreeRenderer());
+ expandComponents();
+ }
+
+
+
+ private static DefaultMutableTreeNode createModifierTree(Rocket rocket,
+ Map<Object, List<SimulationModifier>> simulationModifiers) {
+
+ DefaultMutableTreeNode baseNode = new DefaultMutableTreeNode(rocket);
+ populateTree(baseNode, rocket, simulationModifiers);
+
+ return baseNode;
+ }
+
+
+ private static void populateTree(DefaultMutableTreeNode node, RocketComponent component,
+ Map<Object, List<SimulationModifier>> simulationModifiers) {
+
+ // Add modifiers (if any)
+ List<SimulationModifier> modifiers = simulationModifiers.get(component);
+ if (modifiers != null) {
+ DefaultMutableTreeNode modifierNode;
+
+ if (component.getChildCount() > 0) {
+ modifierNode = new DefaultMutableTreeNode("Optimization parameters");
+ node.add(modifierNode);
+ } else {
+ modifierNode = node;
+ }
+
+ for (SimulationModifier m : modifiers) {
+ modifierNode.add(new DefaultMutableTreeNode(m));
+ }
+ }
+
+ // Add child components
+ for (RocketComponent c : component.getChildren()) {
+ DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c);
+ node.add(newNode);
+ populateTree(newNode, c, simulationModifiers);
+ }
+
+ }
+
+
+ @SuppressWarnings("rawtypes")
+ private void expandComponents() {
+ DefaultMutableTreeNode baseNode = (DefaultMutableTreeNode) this.getModel().getRoot();
+
+ Enumeration enumeration = baseNode.breadthFirstEnumeration();
+
+ while (enumeration.hasMoreElements()) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
+ Object object = node.getUserObject();
+ if (object instanceof RocketComponent) {
+ this.makeVisible(new TreePath(node.getPath()));
+ }
+ }
+ }
+
+
+
+
+ public class ComponentModifierTreeRenderer extends DefaultTreeCellRenderer {
+ private Font componentFont;
+ private Font stringFont;
+ private Font modifierFont;
+
+ @Override
+ public Component getTreeCellRendererComponent(
+ JTree tree,
+ Object value,
+ boolean sel,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus2) {
+
+ super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus2);
+
+ if (componentFont == null) {
+ makeFonts();
+ }
+
+
+ // Customize based on line type
+
+ Object object = ((DefaultMutableTreeNode) value).getUserObject();
+
+ // Set icon (for rocket components, null for others)
+ setIcon(ComponentIcons.getSmallIcon(object.getClass()));
+
+
+ // Set text color/style
+ if (object instanceof RocketComponent) {
+ setForeground(Color.GRAY);
+ setFont(componentFont);
+ } else if (object instanceof String) {
+ setForeground(Color.GRAY);
+ setFont(stringFont);
+ } else if (object instanceof SimulationModifier) {
+ setForeground(Color.BLACK);
+ setFont(modifierFont);
+ setText(((SimulationModifier) object).getName());
+ }
+
+ return this;
+ }
+
+ private void makeFonts() {
+ Font font = getFont();
+ componentFont = font.deriveFont(Font.ITALIC);
+ stringFont = font;
+ modifierFont = font.deriveFont(Font.BOLD);
+ }
+
+ }
+
+}
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;
+ /**
+ * Return the currently selected rocket component, or <code>null</code> if none selected.
+ */
+ private RocketComponent getSelectedComponent() {
+ TreePath path = componentSelectionModel.getSelectionPath();
+ if (path == null)
+ return null;
+ tree.scrollPathToVisible(path);
+
+ return (RocketComponent) path.getLastPathComponent();
+ }
+
+
/**
* Creates the menu for the window.
*/
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
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
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
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);
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();
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) {
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) {
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) {
//// 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) {
//// 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) {
//// 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) {
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
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
public class ComponentIcons {
private static final Translator trans = Application.getTranslator();
-
+
private static final String ICON_DIRECTORY = "pix/componenticons/";
private static final String SMALL_SUFFIX = "-small.png";
private static final String LARGE_SUFFIX = "-large.png";
- private static final HashMap<Class<?>,ImageIcon> SMALL_ICONS =
- new HashMap<Class<?>,ImageIcon>();
- private static final HashMap<Class<?>,ImageIcon> LARGE_ICONS =
- new HashMap<Class<?>,ImageIcon>();
- private static final HashMap<Class<?>,ImageIcon> DISABLED_ICONS =
- new HashMap<Class<?>,ImageIcon>();
-
+ private static final HashMap<Class<?>, ImageIcon> SMALL_ICONS =
+ new HashMap<Class<?>, ImageIcon>();
+ private static final HashMap<Class<?>, ImageIcon> LARGE_ICONS =
+ new HashMap<Class<?>, ImageIcon>();
+ private static final HashMap<Class<?>, ImageIcon> DISABLED_ICONS =
+ new HashMap<Class<?>, ImageIcon>();
+
static {
//// Nose cone
load("nosecone", trans.get("ComponentIcons.Nosecone"), NoseCone.class);
}
-
+ /**
+ * Return the small icon for a component type.
+ *
+ * @param c the component class.
+ * @return the icon, or <code>null</code> if none available.
+ */
public static Icon getSmallIcon(Class<?> c) {
return SMALL_ICONS.get(c);
}
+
+ /**
+ * Return the large icon for a component type.
+ *
+ * @param c the component class.
+ * @return the icon, or <code>null</code> if none available.
+ */
public static Icon getLargeIcon(Class<?> c) {
return LARGE_ICONS.get(c);
}
+
+ /**
+ * Return the large disabled icon for a component type.
+ *
+ * @param c the component class.
+ * @return the icon, or <code>null</code> if none available.
+ */
public static Icon getLargeDisabledIcon(Class<?> c) {
return DISABLED_ICONS.get(c);
}
-
-
+
+
private static ImageIcon loadSmall(String file, String desc) {
URL url = ClassLoader.getSystemResource(file);
- if (url==null) {
- ExceptionHandler.handleErrorCondition("ERROR: Couldn't find file: " + file);
+ if (url == null) {
+ ExceptionHandler.handleErrorCondition("ERROR: Couldn't find file: " + file);
return null;
}
return new ImageIcon(url, desc);
ImageIcon[] icons = new ImageIcon[2];
URL url = ClassLoader.getSystemResource(file);
- if (url != null) {
- BufferedImage bi,bi2;
- try {
+ if (url != null) {
+ BufferedImage bi, bi2;
+ try {
bi = ImageIO.read(url);
- bi2 = ImageIO.read(url); // How the fsck can one duplicate a BufferedImage???
+ bi2 = ImageIO.read(url); // How the fsck can one duplicate a BufferedImage???
} catch (IOException e) {
- ExceptionHandler.handleErrorCondition("ERROR: Couldn't read file: "+file, e);
- return new ImageIcon[]{null,null};
+ ExceptionHandler.handleErrorCondition("ERROR: Couldn't read file: " + file, e);
+ return new ImageIcon[] { null, null };
}
- icons[0] = new ImageIcon(bi,desc);
+ icons[0] = new ImageIcon(bi, desc);
// Create disabled icon
- if (false) { // Fade using alpha
-
- int rgb[] = bi2.getRGB(0,0,bi2.getWidth(),bi2.getHeight(),null,0,bi2.getWidth());
- for (int i=0; i<rgb.length; i++) {
- final int alpha = (rgb[i]>>24)&0xFF;
- rgb[i] = (rgb[i]&0xFFFFFF) | (alpha/3)<<24;
+ if (false) { // Fade using alpha
+
+ int rgb[] = bi2.getRGB(0, 0, bi2.getWidth(), bi2.getHeight(), null, 0, bi2.getWidth());
+ for (int i = 0; i < rgb.length; i++) {
+ final int alpha = (rgb[i] >> 24) & 0xFF;
+ rgb[i] = (rgb[i] & 0xFFFFFF) | (alpha / 3) << 24;
//rgb[i] = (rgb[i]&0xFFFFFF) | ((rgb[i]>>1)&0x3F000000);
}
bi2.setRGB(0, 0, bi2.getWidth(), bi2.getHeight(), rgb, 0, bi2.getWidth());
-
- } else { // Raster alpha
-
- for (int x=0; x < bi.getWidth(); x++) {
- for (int y=0; y < bi.getHeight(); y++) {
- if ((x+y)%2 == 0) {
+
+ } else { // Raster alpha
+
+ for (int x = 0; x < bi.getWidth(); x++) {
+ for (int y = 0; y < bi.getHeight(); y++) {
+ if ((x + y) % 2 == 0) {
bi2.setRGB(x, y, 0);
}
}
}
//// (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 };
+ }
}
}
}
// Unknown Error
- if (!(e instanceof Exception)) {
+ if (!(e instanceof Exception) && !(e instanceof LinkageError)) {
log.info("Showing Error dialog");
JOptionPane.showMessageDialog(null,
new Object[] {
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());
}
-
- 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;
- }
- }
-
+
}
/**
* A TreeModel that implements viewing of the rocket tree structure.
- * This transforms the internal view (which has nested Stages) into the user-view
- * (which has parallel Stages).
- *
- * To view with the internal structure, switch to using BareComponentTreeModel in
- * ComponentTree.java. NOTE: This class's makeTreePath will still be used, which
- * will create illegal paths, which results in problems with selections.
- *
- * TODO: MEDIUM: When converting a component to another component this model given
- * outdated information, since it uses the components themselves as the nodes.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
((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 };
package net.sf.openrocket.l10n;
-import java.util.HashSet;
import java.util.Locale;
import java.util.MissingResourceException;
-import java.util.Set;
import net.sf.openrocket.gui.main.ExceptionHandler;
/**
* A translator that suppresses MissingResourceExceptions and handles them gracefully.
- * For every missing key this class calls the exception handler exactly once, and
- * returns the key itself as the translation.
+ * For the first missing key this class calls the exception handler, and afterwards
+ * always returns the key for missing translations.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class ExceptionSuppressingTranslator implements Translator {
- private static final Set<String> errors = new HashSet<String>();
- // For unit testing:
- static int failures = 0;
+ static boolean errorReported = false;
private final Translator translator;
-
/**
* Sole constructor.
*
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);
}
}
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");
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");
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
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);
}
r = DEFAULT_RADIUS;
return r;
}
- return radius;
+ return outerRadius;
}
*/
@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);
}
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);
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))
this.radialDirection = direction;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
-
+
+
public void setLength(double length) {
if (MathUtil.equals(this.length, length))
this.length = length;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
-
-
+
+
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 {
x2 = MathUtil.clamp(x2, 0, body.getLength());
parentRadius = Math.max(s.getRadius(x1), s.getRadius(x2));
}
-
+
shiftY = Math.cos(radialDirection) * (parentRadius + radius);
shiftZ = Math.sin(radialDirection) * (parentRadius + radius);
-
+
// System.out.println("Computed shift: y="+shiftY+" z="+shiftZ);
}
-
-
+
+
@Override
public double getComponentVolume() {
return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness));
}
-
+
@Override
public Collection<Coordinate> getComponentBounds() {
ArrayList<Coordinate> set = new ArrayList<Coordinate>();
addBound(set, length, radius);
return set;
}
-
+
@Override
public Coordinate getComponentCG() {
return new Coordinate(length / 2, 0, 0, getComponentMass());
}
-
+
@Override
public String getComponentName() {
//// Launch lug
return trans.get("LaunchLug.Launchlug");
}
-
+
@Override
public double getLongitudinalUnitInertia() {
// 1/12 * (3 * (r1^2 + r2^2) + h^2)
- return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
- MathUtil.pow2(getLength())) / 12;
+ return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) + MathUtil.pow2(getLength())) / 12;
}
-
+
@Override
public double getRotationalUnitInertia() {
// 1/2 * (r1^2 + r2^2)
- return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
+ return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius())) / 2;
}
-
+
@Override
public boolean allowsChildren() {
return false;
}
-
+
@Override
public boolean isCompatible(Class<? extends RocketComponent> type) {
// Allow nothing to be attached to a LaunchLug
return false;
}
-
+
}
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
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public abstract class RingComponent extends StructuralComponent implements Coaxial {
-
+
protected boolean outerRadiusAutomatic = false;
protected boolean innerRadiusAutomatic = false;
-
+
private double radialDirection = 0;
private double radialPosition = 0;
-
+
private double shiftY = 0;
private double shiftZ = 0;
+
+
-
-
- @Override
+ @Override
public abstract double getOuterRadius();
- @Override
+
+ @Override
public abstract void setOuterRadius(double r);
-
- @Override
+
+ @Override
public abstract double getInnerRadius();
- @Override
+
+ @Override
public abstract void setInnerRadius(double r);
-
- @Override
+
+ @Override
public abstract double getThickness();
+
public abstract void setThickness(double thickness);
-
-
+
+
public final boolean isOuterRadiusAutomatic() {
return outerRadiusAutomatic;
}
-
+
+ // Setter is protected, subclasses may make it public
protected void setOuterRadiusAutomatic(boolean auto) {
if (auto == outerRadiusAutomatic)
return;
outerRadiusAutomatic = auto;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
public final boolean isInnerRadiusAutomatic() {
return innerRadiusAutomatic;
}
-
+
+ // Setter is protected, subclasses may make it public
protected void setInnerRadiusAutomatic(boolean auto) {
if (auto == innerRadiusAutomatic)
return;
innerRadiusAutomatic = auto;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
public final void setLength(double length) {
- double l = Math.max(length,0);
+ double l = Math.max(length, 0);
if (this.length == l)
return;
-
+
this.length = l;
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
/**
* Return the radial direction of displacement of the component. Direction 0
* is equivalent to the Y-direction.
public double getRadialDirection() {
return radialDirection;
}
-
+
/**
* Set the radial direction of displacement of the component. Direction 0
* is equivalent to the Y-direction.
shiftZ = radialPosition * Math.sin(radialDirection);
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
/**
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.
shiftZ = radialPosition * Math.sin(radialDirection);
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
public double getRadialShiftY() {
return shiftY;
}
-
+
public double getRadialShiftZ() {
return shiftZ;
}
-
+
public void setRadialShift(double y, double z) {
radialPosition = Math.hypot(y, z);
radialDirection = Math.atan2(z, y);
-
+
// Re-calculate to ensure consistency
shiftY = radialPosition * Math.cos(radialDirection);
shiftZ = radialPosition * Math.sin(radialDirection);
- assert(MathUtil.equals(y, shiftY));
- assert(MathUtil.equals(z, shiftZ));
-
+ assert (MathUtil.equals(y, shiftY));
+ assert (MathUtil.equals(z, shiftZ));
+
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
-
-
+
+
/**
* Return the number of times the component is multiplied.
*/
public int getClusterCount() {
if (this instanceof Clusterable)
- return ((Clusterable)this).getClusterConfiguration().getClusterCount();
+ return ((Clusterable) this).getClusterConfiguration().getClusterCount();
return 1;
}
-
-
+
+
/**
* Shift the coordinates according to the radial position and direction.
*/
@Override
public Coordinate[] shiftCoordinates(Coordinate[] array) {
- for (int i=0; i < array.length; i++) {
+ for (int i = 0; i < array.length; i++) {
array[i] = array[i].add(0, shiftY, shiftZ);
}
return array;
}
-
-
+
+
@Override
public Collection<Coordinate> getComponentBounds() {
List<Coordinate> bounds = new ArrayList<Coordinate>();
- addBound(bounds,0,getOuterRadius());
- addBound(bounds,length,getOuterRadius());
+ addBound(bounds, 0, getOuterRadius());
+ addBound(bounds, length, getOuterRadius());
return bounds;
}
-
-
+
+
@Override
public Coordinate getComponentCG() {
- return new Coordinate(length/2, 0, 0, getComponentMass());
+ return new Coordinate(length / 2, 0, 0, getComponentMass());
}
-
+
@Override
public double getComponentMass() {
return ringMass(getOuterRadius(), getInnerRadius(), getLength(),
getMaterial().getDensity()) * getClusterCount();
}
-
-
+
+
@Override
public double getLongitudinalUnitInertia() {
return ringLongitudinalUnitInertia(getOuterRadius(), getInnerRadius(), getLength());
}
-
+
@Override
public double getRotationalUnitInertia() {
return ringRotationalUnitInertia(getOuterRadius(), getInnerRadius());
}
-
+
}
/**
+ * Find a method from the rocket component classes.
* Throws an exception if method not found.
*/
- public static Reflection.Method findMethodStatic(
+ public static Reflection.Method findMethod(
Class<? extends RocketComponent> componentClass,
String method, Class<?>... params) {
Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass,
+++ /dev/null
-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
package net.sf.openrocket.l10n;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import java.util.MissingResourceException;
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() {{
// 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);
}