[#1889] Add option to flip nose cone to tail cone
This commit is contained in:
		
							parent
							
								
									ab428307bd
								
							
						
					
					
						commit
						c686522df2
					
				| @ -1148,6 +1148,8 @@ NoseConeCfg.tab.General = General | |||||||
| NoseConeCfg.tab.ttip.General = General properties | NoseConeCfg.tab.ttip.General = General properties | ||||||
| NoseConeCfg.tab.Shoulder = Shoulder | NoseConeCfg.tab.Shoulder = Shoulder | ||||||
| NoseConeCfg.tab.ttip.Shoulder = Shoulder properties | NoseConeCfg.tab.ttip.Shoulder = Shoulder properties | ||||||
|  | NoseConeCfg.checkbox.Flip = Flip to tail cone | ||||||
|  | NoseConeCfg.checkbox.Flip.ttip = Flips the nose cone direction to a tail cone. | ||||||
| 
 | 
 | ||||||
| ! ParachuteConfig | ! ParachuteConfig | ||||||
| Parachute.Parachute = Parachute | Parachute.Parachute = Parachute | ||||||
|  | |||||||
| @ -10,9 +10,11 @@ import net.sf.openrocket.util.Reflection; | |||||||
| //// BooleanSetter - set a boolean value | //// BooleanSetter - set a boolean value | ||||||
| class BooleanSetter implements Setter { | class BooleanSetter implements Setter { | ||||||
| 	private final Reflection.Method setMethod; | 	private final Reflection.Method setMethod; | ||||||
|  | 	private Object[] extraParameters = null; | ||||||
| 	 | 	 | ||||||
| 	public BooleanSetter(Reflection.Method set) { | 	public BooleanSetter(Reflection.Method set, Object... parameters) { | ||||||
| 		setMethod = set; | 		setMethod = set; | ||||||
|  | 		this.extraParameters = parameters; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	@Override | 	@Override | ||||||
| @ -20,12 +22,23 @@ class BooleanSetter implements Setter { | |||||||
| 			WarningSet warnings) { | 			WarningSet warnings) { | ||||||
| 		 | 		 | ||||||
| 		s = s.trim(); | 		s = s.trim(); | ||||||
|  | 		final boolean setValue; | ||||||
| 		if (s.equalsIgnoreCase("true")) { | 		if (s.equalsIgnoreCase("true")) { | ||||||
| 			setMethod.invoke(c, true); | 			setValue = true; | ||||||
| 		} else if (s.equalsIgnoreCase("false")) { | 		} else if (s.equalsIgnoreCase("false")) { | ||||||
| 			setMethod.invoke(c, false); | 			setValue = false; | ||||||
| 		} else { | 		} else { | ||||||
| 			warnings.add(Warning.FILE_INVALID_PARAMETER); | 			warnings.add(Warning.FILE_INVALID_PARAMETER); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (extraParameters != null) { | ||||||
|  | 			Object[] parameters = new Object[extraParameters.length + 1]; | ||||||
|  | 			parameters[0] = setValue; | ||||||
|  | 			System.arraycopy(extraParameters, 0, parameters, 1, extraParameters.length); | ||||||
|  | 			setMethod.invoke(c, parameters); | ||||||
|  | 		} else { | ||||||
|  | 			setMethod.invoke(c, setValue); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -248,7 +248,9 @@ class DocumentConfig { | |||||||
| 				Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); | 				Reflection.findMethod(Transition.class, "setAftShoulderThickness", double.class))); | ||||||
| 		setters.put("Transition:aftshouldercapped", new BooleanSetter( | 		setters.put("Transition:aftshouldercapped", new BooleanSetter( | ||||||
| 				Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); | 				Reflection.findMethod(Transition.class, "setAftShoulderCapped", boolean.class))); | ||||||
| 		 | 
 | ||||||
|  | 		setters.put("NoseCone:isflipped", new BooleanSetter( | ||||||
|  | 				Reflection.findMethod(NoseCone.class, "setFlipped", boolean.class, boolean.class), false)); | ||||||
| 		// NoseCone - disable disallowed elements | 		// NoseCone - disable disallowed elements | ||||||
| 		setters.put("NoseCone:foreradius", null); | 		setters.put("NoseCone:foreradius", null); | ||||||
| 		setters.put("NoseCone:foreshoulderradius", null); | 		setters.put("NoseCone:foreshoulderradius", null); | ||||||
|  | |||||||
| @ -1,7 +1,10 @@ | |||||||
| package net.sf.openrocket.file.openrocket.savers; | package net.sf.openrocket.file.openrocket.savers; | ||||||
| 
 | 
 | ||||||
|  | import net.sf.openrocket.rocketcomponent.NoseCone; | ||||||
|  | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Locale; | ||||||
| 
 | 
 | ||||||
| public class NoseConeSaver extends TransitionSaver { | public class NoseConeSaver extends TransitionSaver { | ||||||
| 
 | 
 | ||||||
| @ -20,8 +23,23 @@ public class NoseConeSaver extends TransitionSaver { | |||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { | 	protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { | ||||||
|  | 		NoseCone noseCone = (NoseCone) c; | ||||||
| 		super.addParams(c, elements); | 		super.addParams(c, elements); | ||||||
| 		 | 
 | ||||||
| 		// Transition handles nose cone saving as well | 		if (noseCone.isBaseRadiusAutomatic()) | ||||||
|  | 			elements.add("<aftradius>auto " + noseCone.getBaseRadiusNoAutomatic() + "</aftradius>"); | ||||||
|  | 		else | ||||||
|  | 			elements.add("<aftradius>" + noseCone.getBaseRadius() + "</aftradius>"); | ||||||
|  | 
 | ||||||
|  | 		elements.add("<aftshoulderradius>" + noseCone.getShoulderRadius() | ||||||
|  | 				+ "</aftshoulderradius>"); | ||||||
|  | 		elements.add("<aftshoulderlength>" + noseCone.getShoulderLength() | ||||||
|  | 				+ "</aftshoulderlength>"); | ||||||
|  | 		elements.add("<aftshoulderthickness>" + noseCone.getShoulderThickness() | ||||||
|  | 				+ "</aftshoulderthickness>"); | ||||||
|  | 		elements.add("<aftshouldercapped>" + noseCone.isShoulderCapped() | ||||||
|  | 				+ "</aftshouldercapped>"); | ||||||
|  | 
 | ||||||
|  | 		elements.add("<isflipped>" + noseCone.isFlipped() + "</isflipped>"); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -30,8 +30,6 @@ public class TransitionSaver extends SymmetricComponentSaver { | |||||||
| 	protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { | 	protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { | ||||||
| 		super.addParams(c, elements); | 		super.addParams(c, elements); | ||||||
| 		net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; | 		net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; | ||||||
| 		boolean nosecone = (trans instanceof NoseCone); |  | ||||||
| 		 |  | ||||||
| 		 | 		 | ||||||
| 		Transition.Shape shape = trans.getType(); | 		Transition.Shape shape = trans.getType(); | ||||||
| 		elements.add("<shape>" + shape.name().toLowerCase(Locale.ENGLISH) + "</shape>"); | 		elements.add("<shape>" + shape.name().toLowerCase(Locale.ENGLISH) + "</shape>"); | ||||||
| @ -41,14 +39,16 @@ public class TransitionSaver extends SymmetricComponentSaver { | |||||||
| 		if (shape.usesParameter()) { | 		if (shape.usesParameter()) { | ||||||
| 			elements.add("<shapeparameter>" + trans.getShapeParameter() + "</shapeparameter>"); | 			elements.add("<shapeparameter>" + trans.getShapeParameter() + "</shapeparameter>"); | ||||||
| 		} | 		} | ||||||
| 		 | 
 | ||||||
| 		 | 		// Nose cones need other parameter saving, due to the isFlipped() parameter | ||||||
| 		if (!nosecone) { | 		if (trans instanceof NoseCone) { | ||||||
| 			if (trans.isForeRadiusAutomatic()) | 			return; | ||||||
| 				elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>"); |  | ||||||
| 			else |  | ||||||
| 				elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>"); |  | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (trans.isForeRadiusAutomatic()) | ||||||
|  | 			elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>"); | ||||||
|  | 		else | ||||||
|  | 			elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>"); | ||||||
| 		 | 		 | ||||||
| 		if (trans.isAftRadiusAutomatic()) | 		if (trans.isAftRadiusAutomatic()) | ||||||
| 			elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>"); | 			elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>"); | ||||||
| @ -56,16 +56,14 @@ public class TransitionSaver extends SymmetricComponentSaver { | |||||||
| 			elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>"); | 			elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>"); | ||||||
| 		 | 		 | ||||||
| 		 | 		 | ||||||
| 		if (!nosecone) { | 		elements.add("<foreshoulderradius>" + trans.getForeShoulderRadius() | ||||||
| 			elements.add("<foreshoulderradius>" + trans.getForeShoulderRadius() | 				+ "</foreshoulderradius>"); | ||||||
| 					+ "</foreshoulderradius>"); | 		elements.add("<foreshoulderlength>" + trans.getForeShoulderLength() | ||||||
| 			elements.add("<foreshoulderlength>" + trans.getForeShoulderLength() | 				+ "</foreshoulderlength>"); | ||||||
| 					+ "</foreshoulderlength>"); | 		elements.add("<foreshoulderthickness>" + trans.getForeShoulderThickness() | ||||||
| 			elements.add("<foreshoulderthickness>" + trans.getForeShoulderThickness() | 				+ "</foreshoulderthickness>"); | ||||||
| 					+ "</foreshoulderthickness>"); | 		elements.add("<foreshouldercapped>" + trans.isForeShoulderCapped() | ||||||
| 			elements.add("<foreshouldercapped>" + trans.isForeShoulderCapped() | 				+ "</foreshouldercapped>"); | ||||||
| 					+ "</foreshouldercapped>"); |  | ||||||
| 		} |  | ||||||
| 		 | 		 | ||||||
| 		elements.add("<aftshoulderradius>" + trans.getAftShoulderRadius() | 		elements.add("<aftshoulderradius>" + trans.getAftShoulderRadius() | ||||||
| 				+ "</aftshoulderradius>"); | 				+ "</aftshoulderradius>"); | ||||||
|  | |||||||
| @ -65,7 +65,11 @@ public class PodSetDTO extends BasePartDTO implements AttachableParts { | |||||||
|             } else if (child instanceof BodyTube) { |             } else if (child instanceof BodyTube) { | ||||||
|                 addAttachedPart(new BodyTubeDTO((BodyTube) child)); |                 addAttachedPart(new BodyTubeDTO((BodyTube) child)); | ||||||
|             } else if (child instanceof NoseCone) { |             } else if (child instanceof NoseCone) { | ||||||
|                 addAttachedPart(new NoseConeDTO((NoseCone) child)); |                 if (((NoseCone) child).isFlipped()) { | ||||||
|  |                     addAttachedPart(new TransitionDTO((NoseCone) child)); | ||||||
|  |                 } else { | ||||||
|  |                     addAttachedPart(new NoseConeDTO((NoseCone) child)); | ||||||
|  |                 } | ||||||
|             } else if (child instanceof Transition) { |             } else if (child instanceof Transition) { | ||||||
|                 addAttachedPart(new TransitionDTO((Transition) child)); |                 addAttachedPart(new TransitionDTO((Transition) child)); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -93,8 +93,12 @@ public class StageDTO { | |||||||
|         externalPart.add(theExternalPartDTO); |         externalPart.add(theExternalPartDTO); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private NoseConeDTO toNoseConeDTO(NoseCone nc) { |     private AbstractTransitionDTO toNoseConeDTO(NoseCone nc) { | ||||||
|         return new NoseConeDTO(nc); |         if (nc.isFlipped()) { | ||||||
|  |             return new TransitionDTO(nc); | ||||||
|  |         } else { | ||||||
|  |             return new NoseConeDTO(nc); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private BodyTubeDTO toBodyTubeDTO(BodyTube bt) { |     private BodyTubeDTO toBodyTubeDTO(BodyTube bt) { | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ public class DefaultSimulationModifierService implements SimulationModifierServi | |||||||
| 		 */ | 		 */ | ||||||
| 		 | 		 | ||||||
| 		addModifier("optimization.modifier.nosecone.length", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Length"); | 		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", "isAftRadiusAutomatic"); | 		addModifier("optimization.modifier.nosecone.diameter", UnitGroup.UNITS_LENGTH, 2.0, NoseCone.class, "BaseRadius", "isBaseRadiusAutomatic"); | ||||||
| 		addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness", "isFilled"); | 		addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness", "isFilled"); | ||||||
| 		 | 		 | ||||||
| 		addModifier("optimization.modifier.transition.length", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Length"); | 		addModifier("optimization.modifier.transition.length", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Length"); | ||||||
|  | |||||||
| @ -13,6 +13,9 @@ import java.util.EventObject; | |||||||
| /** | /** | ||||||
|  * Rocket nose cones of various types.  Implemented as a transition with the |  * Rocket nose cones of various types.  Implemented as a transition with the | ||||||
|  * fore radius == 0. |  * fore radius == 0. | ||||||
|  |  * <p> | ||||||
|  |  * The normal nose cone can be converted to a tail cone by setting the {@link #isFlipped} parameter. | ||||||
|  |  * This will flip all the aft side dimensions with the fore side dimensions. | ||||||
|  * |  * | ||||||
|  * @author Sampo Niskanen <sampo.niskanen@iki.fi> |  * @author Sampo Niskanen <sampo.niskanen@iki.fi> | ||||||
|  */ |  */ | ||||||
| @ -21,6 +24,7 @@ public class NoseCone extends Transition implements InsideColorComponent { | |||||||
| 	private static final Translator trans = Application.getTranslator(); | 	private static final Translator trans = Application.getTranslator(); | ||||||
| 
 | 
 | ||||||
| 	private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); | 	private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); | ||||||
|  | 	private boolean isFlipped;		// If true, the nose cone is converted to a tail cone | ||||||
| 	 | 	 | ||||||
| 	/********* Constructors **********/ | 	/********* Constructors **********/ | ||||||
| 	public NoseCone() { | 	public NoseCone() { | ||||||
| @ -29,16 +33,12 @@ public class NoseCone extends Transition implements InsideColorComponent { | |||||||
| 	 | 	 | ||||||
| 	public NoseCone(Transition.Shape type, double length, double radius) { | 	public NoseCone(Transition.Shape type, double length, double radius) { | ||||||
| 		super(); | 		super(); | ||||||
|  | 		this.isFlipped = false; | ||||||
| 		super.setType(type); | 		super.setType(type); | ||||||
| 		super.setForeRadiusAutomatic(false); |  | ||||||
| 		super.setForeRadius(0); |  | ||||||
| 		super.setForeShoulderLength(0); |  | ||||||
| 		super.setForeShoulderRadius(0.9 * radius); |  | ||||||
| 		super.setForeShoulderThickness(0); |  | ||||||
| 		super.setForeShoulderCapped(filled); |  | ||||||
| 		super.setThickness(0.002); | 		super.setThickness(0.002); | ||||||
| 		super.setLength(length); | 		super.setLength(length); | ||||||
| 		super.setClipped(false); | 		super.setClipped(false); | ||||||
|  | 		resetForeRadius(); | ||||||
| 		 | 		 | ||||||
| 		super.setAftRadiusAutomatic(false); | 		super.setAftRadiusAutomatic(false); | ||||||
| 		super.setAftRadius(radius); | 		super.setAftRadius(radius); | ||||||
| @ -46,86 +46,220 @@ public class NoseCone extends Transition implements InsideColorComponent { | |||||||
| 		super.displayOrder_side = 1;		// Order for displaying the component in the 2D side view | 		super.displayOrder_side = 1;		// Order for displaying the component in the 2D side view | ||||||
| 		super.displayOrder_back = 0;		// Order for displaying the component in the 2D back view | 		super.displayOrder_back = 0;		// Order for displaying the component in the 2D back view | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	 | 	/********** Nose cone dimensions  **********/ | ||||||
| 	/********** Get/set methods for component parameters **********/ | 
 | ||||||
| 	 | 	/** | ||||||
| 	@Override | 	 * Returns the base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||||
| 	public double getForeRadius() { | 	 * This method should be used over {@link #getAftRadius()} because it works for both normal and flipped nose cones. | ||||||
| 		return 0; | 	 */ | ||||||
| 	} | 	public double getBaseRadius() { | ||||||
| 	 | 		return isFlipped ? getForeRadius() : getAftRadius(); | ||||||
| 	@Override |  | ||||||
| 	public void setForeRadius(double r) { |  | ||||||
| 		// No-op |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	@Override |  | ||||||
| 	public boolean isForeRadiusAutomatic() { |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	@Override |  | ||||||
| 	public void setForeRadiusAutomatic(boolean b) { |  | ||||||
| 		// No-op |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public boolean usesPreviousCompAutomatic() { | 	 * Returns the raw base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||||
| 		return false; | 	 * This method should be used over {@link #getAftRadiusNoAutomatic()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public double getBaseRadiusNoAutomatic() { | ||||||
|  | 		return isFlipped ? getForeRadiusNoAutomatic() : getAftRadiusNoAutomatic(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public double getForeShoulderLength() { | 	 * Sets the base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||||
| 		return 0; | 	 * This method should be used over {@link #setAftRadius(double)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setBaseRadius(double radius) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeRadius(radius); | ||||||
|  | 		} else { | ||||||
|  | 			setAftRadius(radius); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public double getForeShoulderRadius() { | 	 * Returns whether the base radius of the nose cone takes it settings from the previous/next component | ||||||
| 		return 0; | 	 * (independent of whether the nose cone is flipped or not). | ||||||
|  | 	 * This method should be used over {@link #isAftRadiusAutomatic()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public boolean isBaseRadiusAutomatic() { | ||||||
|  | 		return isFlipped ? isForeRadiusAutomatic() : isAftRadiusAutomatic(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public double getForeShoulderThickness() { | 	 * Sets whether the base radius of the nose cone takes it settings from the previous/next component | ||||||
| 		return 0; | 	 * (independent of whether the nose cone is flipped or not). | ||||||
|  | 	 * This method should be used over {@link #setAftRadiusAutomatic(boolean)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setBaseRadiusAutomatic(boolean auto) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeRadiusAutomatic(auto); | ||||||
|  | 		} else { | ||||||
|  | 			setAftRadiusAutomatic(auto); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public boolean isForeShoulderCapped() { | 	 * Returns the shoulder length, regardless of how the nose cone is flipped (independent of whether the nose cone is flipped or not). | ||||||
| 		return false; | 	 * This method should be used over {@link #getAftShoulderLength()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public double getShoulderLength() { | ||||||
|  | 		return isFlipped ? getForeShoulderLength() : getAftShoulderLength(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public void setForeShoulderCapped(boolean capped) { | 	 * Sets the shoulder length (independent of whether the nose cone is flipped or not). | ||||||
| 		// No-op | 	 * This method should be used over {@link #setAftShoulderLength(double)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setShoulderLength(double length) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeShoulderLength(length); | ||||||
|  | 		} else { | ||||||
|  | 			setAftShoulderLength(length); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public void setForeShoulderLength(double foreShoulderLength) { | 	 * Returns the shoulder radius (independent of whether the nose cone is flipped or not). | ||||||
| 		// No-op | 	 * This method should be used over {@link #getAftShoulderRadius()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public double getShoulderRadius() { | ||||||
|  | 		return isFlipped ? getForeShoulderRadius() : getAftShoulderRadius(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public void setForeShoulderRadius(double foreShoulderRadius) { | 	 * Sets the shoulder radius (independent of whether the nose cone is flipped or not). | ||||||
| 		// No-op | 	 * This method should be used over {@link #setAftShoulderRadius(double)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setShoulderRadius(double radius) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeShoulderRadius(radius); | ||||||
|  | 		} else { | ||||||
|  | 			setAftShoulderRadius(radius); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	/** | ||||||
| 	public void setForeShoulderThickness(double foreShoulderThickness) { | 	 * Returns the shoulder thickness (independent of whether the nose cone is flipped or not). | ||||||
| 		// No-op | 	 * This method should be used over {@link #getAftShoulderThickness()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public double getShoulderThickness() { | ||||||
|  | 		return isFlipped ? getForeShoulderThickness() : getAftShoulderThickness(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Sets the shoulder thickness (independent of whether the nose cone is flipped or not). | ||||||
|  | 	 * This method should be used over {@link #setAftShoulderRadius(double)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setShoulderThickness(double thickness) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeShoulderThickness(thickness); | ||||||
|  | 		} else { | ||||||
|  | 			setAftShoulderThickness(thickness); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns the shoulder cap (independent of whether the nose cone is flipped or not). | ||||||
|  | 	 * This method should be used over {@link #isAftShoulderCapped()} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public boolean isShoulderCapped() { | ||||||
|  | 		return isFlipped ? isForeShoulderCapped() : isAftShoulderCapped(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Sets the shoulder cap (independent of whether the nose cone is flipped or not). | ||||||
|  | 	 * This method should be used over {@link #setAftShoulderCapped(boolean)} because it works for both normal and flipped nose cones. | ||||||
|  | 	 */ | ||||||
|  | 	public void setShoulderCapped(boolean capped) { | ||||||
|  | 		if (isFlipped) { | ||||||
|  | 			setForeShoulderCapped(capped); | ||||||
|  | 		} else { | ||||||
|  | 			setAftShoulderCapped(capped); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/********** Other  **********/ | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Return true if the nose cone is flipped, i.e. converted to a tail cone, false if it is a regular nose cone. | ||||||
|  | 	 */ | ||||||
|  | 	public boolean isFlipped() { | ||||||
|  | 		return isFlipped; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Set the nose cone to be flipped, i.e. converted to a tail cone, or set it to be a regular nose cone. | ||||||
|  | 	 * @param flipped if true, the nose cone is converted to a tail cone, if false it is a regular nose cone. | ||||||
|  | 	 * @param sanityCheck whether to check if the auto radius parameter can be used for the new nose cone orientation | ||||||
|  | 	 */ | ||||||
|  | 	public void setFlipped(boolean flipped, boolean sanityCheck) { | ||||||
|  | 		if (isFlipped == flipped) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		setBypassChangeEvent(true); | ||||||
|  | 		if (flipped) { | ||||||
|  | 			setForeRadius(getAftRadiusNoAutomatic()); | ||||||
|  | 			setForeRadiusAutomatic(isAftRadiusAutomatic(), sanityCheck); | ||||||
|  | 			setForeShoulderLength(getAftShoulderLength()); | ||||||
|  | 			setForeShoulderRadius(getAftShoulderRadius()); | ||||||
|  | 			setForeShoulderThickness(getAftShoulderThickness()); | ||||||
|  | 			setForeShoulderCapped(isAftShoulderCapped()); | ||||||
|  | 
 | ||||||
|  | 			resetAftRadius(); | ||||||
|  | 		} else { | ||||||
|  | 			setAftRadius(getForeRadiusNoAutomatic()); | ||||||
|  | 			setAftRadiusAutomatic(isForeRadiusAutomatic(), sanityCheck); | ||||||
|  | 			setAftShoulderLength(getForeShoulderLength()); | ||||||
|  | 			setAftShoulderRadius(getForeShoulderRadius()); | ||||||
|  | 			setAftShoulderThickness(getForeShoulderThickness()); | ||||||
|  | 			setAftShoulderCapped(isForeShoulderCapped()); | ||||||
|  | 
 | ||||||
|  | 			resetForeRadius(); | ||||||
|  | 		} | ||||||
|  | 		setBypassChangeEvent(false); | ||||||
|  | 
 | ||||||
|  | 		isFlipped = flipped; | ||||||
|  | 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Set the nose cone to be flipped, i.e. converted to a tail cone, or set it to be a regular nose cone. | ||||||
|  | 	 * @param flipped if true, the nose cone is converted to a tail cone, if false it is a regular nose cone. | ||||||
|  | 	 */ | ||||||
|  | 	public void setFlipped(boolean flipped) { | ||||||
|  | 		setFlipped(flipped, true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void resetForeRadius() { | ||||||
|  | 		setForeRadius(0); | ||||||
|  | 		setForeRadiusAutomatic(false); | ||||||
|  | 		setForeShoulderLength(0); | ||||||
|  | 		setForeShoulderRadius(0); | ||||||
|  | 		setForeShoulderThickness(0); | ||||||
|  | 		setForeShoulderCapped(false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void resetAftRadius() { | ||||||
|  | 		setAftRadius(0); | ||||||
|  | 		setAftRadiusAutomatic(false); | ||||||
|  | 		setAftShoulderLength(0); | ||||||
|  | 		setAftShoulderRadius(0); | ||||||
|  | 		setAftShoulderThickness(0); | ||||||
|  | 		setAftShoulderCapped(false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean isClipped() { | 	public boolean isClipped() { | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public void setClipped(boolean b) { | 	public void setClipped(boolean b) { | ||||||
| 		// No-op | 		// No-op | ||||||
| 	} | 	} | ||||||
| 	 |  | ||||||
| 	 |  | ||||||
| 
 | 
 | ||||||
| 	/********** RocketComponent methods **********/ | 	/********** RocketComponent methods **********/ | ||||||
| 	 | 	 | ||||||
| @ -136,7 +270,7 @@ public class NoseCone extends Transition implements InsideColorComponent { | |||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	protected void loadFromPreset(ComponentPreset preset) { | 	protected void loadFromPreset(ComponentPreset preset) { | ||||||
| 		 | 		setFlipped(false); | ||||||
| 		//Many parameters are handled by the super class Transition.loadFromPreset | 		//Many parameters are handled by the super class Transition.loadFromPreset | ||||||
| 		super.loadFromPreset(preset); | 		super.loadFromPreset(preset); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -5,17 +5,13 @@ import static net.sf.openrocket.util.MathUtil.pow2; | |||||||
| import static net.sf.openrocket.util.MathUtil.pow3; | import static net.sf.openrocket.util.MathUtil.pow3; | ||||||
| 
 | 
 | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.EventObject; |  | ||||||
| 
 | 
 | ||||||
| import net.sf.openrocket.appearance.Appearance; |  | ||||||
| import net.sf.openrocket.appearance.Decal; |  | ||||||
| import net.sf.openrocket.l10n.Translator; | import net.sf.openrocket.l10n.Translator; | ||||||
| import net.sf.openrocket.preset.ComponentPreset; | import net.sf.openrocket.preset.ComponentPreset; | ||||||
| import net.sf.openrocket.preset.ComponentPreset.Type; | import net.sf.openrocket.preset.ComponentPreset.Type; | ||||||
| import net.sf.openrocket.startup.Application; | import net.sf.openrocket.startup.Application; | ||||||
| import net.sf.openrocket.util.Coordinate; | import net.sf.openrocket.util.Coordinate; | ||||||
| import net.sf.openrocket.util.MathUtil; | import net.sf.openrocket.util.MathUtil; | ||||||
| import net.sf.openrocket.util.StateChangeListener; |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| public class Transition extends SymmetricComponent implements InsideColorComponent { | public class Transition extends SymmetricComponent implements InsideColorComponent { | ||||||
| @ -27,8 +23,8 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 	private double shapeParameter; | 	private double shapeParameter; | ||||||
| 	private boolean clipped; // Not to be read - use isClipped(), which may be overridden | 	private boolean clipped; // Not to be read - use isClipped(), which may be overridden | ||||||
| 
 | 
 | ||||||
| 	private double foreRadius, aftRadius; | 	protected double foreRadius, aftRadius;		// Warning: avoid using these directly, use getForeRadius() and getAftRadius() instead (because the definition of the two can change for flipped nose cones) | ||||||
| 	private boolean autoForeRadius, autoAftRadius2; // Whether the start radius is automatic | 	protected boolean autoForeRadius, autoAftRadius; // Whether the start radius is automatic | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	private double foreShoulderRadius; | 	private double foreShoulderRadius; | ||||||
| @ -53,7 +49,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 		this.aftRadius = DEFAULT_RADIUS; | 		this.aftRadius = DEFAULT_RADIUS; | ||||||
| 		this.length = DEFAULT_RADIUS * 3; | 		this.length = DEFAULT_RADIUS * 3; | ||||||
| 		this.autoForeRadius = true; | 		this.autoForeRadius = true; | ||||||
| 		this.autoAftRadius2 = true; | 		this.autoAftRadius = true; | ||||||
| 
 | 
 | ||||||
| 		this.type = Shape.CONICAL; | 		this.type = Shape.CONICAL; | ||||||
| 		this.shapeParameter = 0; | 		this.shapeParameter = 0; | ||||||
| @ -81,19 +77,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 	@Override | 	@Override | ||||||
| 	public double getForeRadius() { | 	public double getForeRadius() { | ||||||
| 		if (isForeRadiusAutomatic()) { | 		if (isForeRadiusAutomatic()) { | ||||||
| 			// Get the automatic radius from the front | 			return getAutoForeRadius(); | ||||||
| 			double r = -1; |  | ||||||
| 			SymmetricComponent c = this.getPreviousSymmetricComponent(); |  | ||||||
| 			if (c != null) { |  | ||||||
| 				r = c.getFrontAutoRadius(); |  | ||||||
| 			} |  | ||||||
| 			if (r < 0) |  | ||||||
| 				r = DEFAULT_RADIUS; |  | ||||||
| 			return r; |  | ||||||
| 		} | 		} | ||||||
| 		return foreRadius; | 		return foreRadius; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns the automatic radius from the front, taken from the previous component. Returns the default radius if there | ||||||
|  | 	 * is no previous component. | ||||||
|  | 	 */ | ||||||
|  | 	protected double getAutoForeRadius() { | ||||||
|  | 		SymmetricComponent c = this.getPreviousSymmetricComponent(); | ||||||
|  | 		if (c != null) { | ||||||
|  | 			return c.getFrontAutoRadius(); | ||||||
|  | 		} else { | ||||||
|  | 			return DEFAULT_RADIUS; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Return the fore radius that was manually entered, so not the value that the component received from automatic | 	 * Return the fore radius that was manually entered, so not the value that the component received from automatic | ||||||
| 	 * fore radius. | 	 * fore radius. | ||||||
| @ -136,13 +137,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 		return autoForeRadius; | 		return autoForeRadius; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public void setForeRadiusAutomatic(boolean auto) { | 	/** | ||||||
|  | 	 * Set the fore radius to automatic mode (takes its value from the previous symmetric component's radius). | ||||||
|  | 	 * | ||||||
|  | 	 * @param auto whether to set the fore radius to automatic mode | ||||||
|  | 	 * @param sanityCheck whether to sanity check auto mode for whether there is a previous component of which you can take the radius | ||||||
|  | 	 */ | ||||||
|  | 	public void setForeRadiusAutomatic(boolean auto, boolean sanityCheck) { | ||||||
| 		for (RocketComponent listener : configListeners) { | 		for (RocketComponent listener : configListeners) { | ||||||
| 			if (listener instanceof Transition) { | 			if (listener instanceof Transition) { | ||||||
| 				((Transition) listener).setForeRadiusAutomatic(auto); | 				((Transition) listener).setForeRadiusAutomatic(auto); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		// You can only set the auto fore radius if it is possible | ||||||
|  | 		if (sanityCheck) { | ||||||
|  | 			auto = auto && canUsePreviousCompAutomatic(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (autoForeRadius == auto) | 		if (autoForeRadius == auto) | ||||||
| 			return; | 			return; | ||||||
| 
 | 
 | ||||||
| @ -152,25 +164,34 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	public void setForeRadiusAutomatic(boolean auto) { | ||||||
|  | 		setForeRadiusAutomatic(auto, false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	////////  Aft radius  ///////// | 	////////  Aft radius  ///////// | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public double getAftRadius() { | 	public double getAftRadius() { | ||||||
| 		if (isAftRadiusAutomatic()) { | 		if (isAftRadiusAutomatic()) { | ||||||
| 			// Return the auto radius from the rear | 			return getAutoAftRadius(); | ||||||
| 			double r = -1; |  | ||||||
| 			SymmetricComponent c = this.getNextSymmetricComponent(); |  | ||||||
| 			if (c != null) { |  | ||||||
| 				r = c.getRearAutoRadius(); |  | ||||||
| 			} |  | ||||||
| 			if (r < 0) |  | ||||||
| 				r = DEFAULT_RADIUS; |  | ||||||
| 			return r; |  | ||||||
| 		} | 		} | ||||||
| 		return aftRadius; | 		return aftRadius; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns the automatic radius from the rear, taken from the next component. Returns the default radius if there | ||||||
|  | 	 * is no next component. | ||||||
|  | 	 */ | ||||||
|  | 	protected double getAutoAftRadius() { | ||||||
|  | 		SymmetricComponent c = this.getNextSymmetricComponent(); | ||||||
|  | 		if (c != null) { | ||||||
|  | 			return c.getRearAutoRadius(); | ||||||
|  | 		} else { | ||||||
|  | 			return DEFAULT_RADIUS; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Return the aft radius that was manually entered, so not the value that the component received from automatic | 	 * Return the aft radius that was manually entered, so not the value that the component received from automatic | ||||||
| 	 * zft radius. | 	 * zft radius. | ||||||
| @ -191,10 +212,10 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if ((this.aftRadius == radius) && (autoAftRadius2 == false)) | 		if ((this.aftRadius == radius) && (autoAftRadius == false)) | ||||||
| 			return; | 			return; | ||||||
| 
 | 
 | ||||||
| 		this.autoAftRadius2 = false; | 		this.autoAftRadius = false; | ||||||
| 		this.aftRadius = Math.max(radius, 0); | 		this.aftRadius = Math.max(radius, 0); | ||||||
| 
 | 
 | ||||||
| 		if (doClamping && this.thickness > this.foreRadius && this.thickness > this.aftRadius) | 		if (doClamping && this.thickness > this.foreRadius && this.thickness > this.aftRadius) | ||||||
| @ -210,25 +231,40 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean isAftRadiusAutomatic() { | 	public boolean isAftRadiusAutomatic() { | ||||||
| 		return autoAftRadius2; | 		return autoAftRadius; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public void setAftRadiusAutomatic(boolean auto) { | 	/** | ||||||
|  | 	 * Set the aft radius to automatic mode (takes its value from the next symmetric component's radius). | ||||||
|  | 	 * | ||||||
|  | 	 * @param auto whether to set the aft radius to automatic mode | ||||||
|  | 	 * @param sanityCheck whether to sanity check auto mode for whether there is a next component of which you can take the radius | ||||||
|  | 	 */ | ||||||
|  | 	public void setAftRadiusAutomatic(boolean auto, boolean sanityCheck) { | ||||||
| 		for (RocketComponent listener : configListeners) { | 		for (RocketComponent listener : configListeners) { | ||||||
| 			if (listener instanceof Transition) { | 			if (listener instanceof Transition) { | ||||||
| 				((Transition) listener).setAftRadiusAutomatic(auto); | 				((Transition) listener).setAftRadiusAutomatic(auto); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (autoAftRadius2 == auto) | 		// You can only set the auto aft radius if it is possible | ||||||
|  | 		if (sanityCheck) { | ||||||
|  | 			auto = auto && canUseNextCompAutomatic(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (autoAftRadius == auto) | ||||||
| 			return; | 			return; | ||||||
| 
 | 
 | ||||||
| 		autoAftRadius2 = auto; | 		autoAftRadius = auto; | ||||||
| 
 | 
 | ||||||
| 		clearPreset(); | 		clearPreset(); | ||||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	public void setAftRadiusAutomatic(boolean auto) { | ||||||
|  | 		setAftRadiusAutomatic(auto, false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	//// Radius automatics | 	//// Radius automatics | ||||||
| 
 | 
 | ||||||
| @ -257,6 +293,32 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | |||||||
| 		return isAftRadiusAutomatic(); | 		return isAftRadiusAutomatic(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Checks whether this component can use the automatic radius of the previous symmetric component. | ||||||
|  | 	 * @return false if there is no previous symmetric component, or if the previous component already has this component | ||||||
|  | 	 * 			as its auto dimension reference | ||||||
|  | 	 */ | ||||||
|  | 	public boolean canUsePreviousCompAutomatic() { | ||||||
|  | 		SymmetricComponent referenceComp = getPreviousSymmetricComponent(); | ||||||
|  | 		if (referenceComp == null) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		return !referenceComp.usesNextCompAutomatic(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Checks whether this component can use the automatic radius of the next symmetric component. | ||||||
|  | 	 * @return false if there is no next symmetric component, or if the next component already has this component | ||||||
|  | 	 * 			as its auto dimension reference | ||||||
|  | 	 */ | ||||||
|  | 	public boolean canUseNextCompAutomatic() { | ||||||
|  | 		SymmetricComponent referenceComp = getNextSymmetricComponent(); | ||||||
|  | 		if (referenceComp == null) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		return !referenceComp.usesPreviousCompAutomatic(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	////////  Type & shape  ///////// | 	////////  Type & shape  ///////// | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -58,6 +58,7 @@ The following file format versions exist: | |||||||
|       Added PhotoStudio settings saving (<photostudio>) |       Added PhotoStudio settings saving (<photostudio>) | ||||||
|       Added override CD parameter (<overridecd>) |       Added override CD parameter (<overridecd>) | ||||||
|       Added stage activeness remembrance (<stage> under <motorconfiguration>) |       Added stage activeness remembrance (<stage> under <motorconfiguration>) | ||||||
|  |       Added <isflipped> parameter for Nose Cones | ||||||
|       Separated <overridesubcomponents> into individual parameters for mass, CG, and CD. |       Separated <overridesubcomponents> into individual parameters for mass, CG, and CD. | ||||||
|       Rename <fincount> to <instancecount> (<fincount> remains for backward compatibility) |       Rename <fincount> to <instancecount> (<fincount> remains for backward compatibility) | ||||||
|       Rename <position> to <axialoffset> (<position> remains for backward compatibility) |       Rename <position> to <axialoffset> (<position> remains for backward compatibility) | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package net.sf.openrocket.gui.configdialog; | |||||||
| 
 | 
 | ||||||
| import java.awt.event.ActionEvent; | import java.awt.event.ActionEvent; | ||||||
| import java.awt.event.ActionListener; | import java.awt.event.ActionListener; | ||||||
|  | import java.awt.event.ItemEvent; | ||||||
|  | import java.awt.event.ItemListener; | ||||||
| 
 | 
 | ||||||
| import javax.swing.JCheckBox; | import javax.swing.JCheckBox; | ||||||
| import javax.swing.JComboBox; | import javax.swing.JComboBox; | ||||||
| @ -40,7 +42,7 @@ public class NoseConeConfig extends RocketComponentConfig { | |||||||
| 	private JLabel shapeLabel; | 	private JLabel shapeLabel; | ||||||
| 	private JSpinner shapeSpinner; | 	private JSpinner shapeSpinner; | ||||||
| 	private JSlider shapeSlider; | 	private JSlider shapeSlider; | ||||||
| 	private final JCheckBox checkAutoAftRadius; | 	private final JCheckBox checkAutoBaseRadius; | ||||||
| 	private static final Translator trans = Application.getTranslator(); | 	private static final Translator trans = Application.getTranslator(); | ||||||
| 	 | 	 | ||||||
| 	// Prepended to the description from NoseCone.DESCRIPTIONS | 	// Prepended to the description from NoseCone.DESCRIPTIONS | ||||||
| @ -106,21 +108,21 @@ public class NoseConeConfig extends RocketComponentConfig { | |||||||
| 
 | 
 | ||||||
| 			panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Basediam"))); | 			panel.add(new JLabel(trans.get("NoseConeCfg.lbl.Basediam"))); | ||||||
| 
 | 
 | ||||||
| 			final DoubleModel aftRadiusModel = new DoubleModel(component, "AftRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius | 			final DoubleModel baseRadius = new DoubleModel(component, "BaseRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius | ||||||
| 			final JSpinner radiusSpinner = new JSpinner(aftRadiusModel.getSpinnerModel()); | 			final JSpinner radiusSpinner = new JSpinner(baseRadius.getSpinnerModel()); | ||||||
| 			radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner)); | 			radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner)); | ||||||
| 			panel.add(radiusSpinner, "growx"); | 			panel.add(radiusSpinner, "growx"); | ||||||
| 			order.add(((SpinnerEditor) radiusSpinner.getEditor()).getTextField()); | 			order.add(((SpinnerEditor) radiusSpinner.getEditor()).getTextField()); | ||||||
| 
 | 
 | ||||||
| 			panel.add(new UnitSelector(aftRadiusModel), "growx"); | 			panel.add(new UnitSelector(baseRadius), "growx"); | ||||||
| 			panel.add(new BasicSlider(aftRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px"); | 			panel.add(new BasicSlider(baseRadius.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px"); | ||||||
| 
 | 
 | ||||||
| 			checkAutoAftRadius = new JCheckBox(aftRadiusModel.getAutomaticAction()); | 			checkAutoBaseRadius = new JCheckBox(baseRadius.getAutomaticAction()); | ||||||
| 			//// Automatic | 			//// Automatic | ||||||
| 			checkAutoAftRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic")); | 			checkAutoBaseRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic")); | ||||||
| 			panel.add(checkAutoAftRadius, "skip, span 2, wrap"); | 			panel.add(checkAutoBaseRadius, "skip, span 2, wrap"); | ||||||
| 			order.add(checkAutoAftRadius); | 			order.add(checkAutoBaseRadius); | ||||||
| 			updateCheckboxAutoAftRadius(); | 			updateCheckboxAutoBaseRadius(((NoseCone) component).isFlipped()); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		{////  Wall thickness: | 		{////  Wall thickness: | ||||||
| @ -142,10 +144,24 @@ public class NoseConeConfig extends RocketComponentConfig { | |||||||
| 			//// Filled | 			//// Filled | ||||||
| 			filledCheckbox.setText(trans.get("NoseConeCfg.checkbox.Filled")); | 			filledCheckbox.setText(trans.get("NoseConeCfg.checkbox.Filled")); | ||||||
| 			filledCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Filled.ttip")); | 			filledCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Filled.ttip")); | ||||||
| 			panel.add(filledCheckbox, "skip, span 2, wrap"); | 			panel.add(filledCheckbox, "skip, span 2, wrap para"); | ||||||
| 			order.add(filledCheckbox); | 			order.add(filledCheckbox); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		{//// Flip to tail cone: | ||||||
|  | 			final JCheckBox flipCheckbox = new JCheckBox(new BooleanModel(component, "Flipped")); | ||||||
|  | 			flipCheckbox.setText(trans.get("NoseConeCfg.checkbox.Flip")); | ||||||
|  | 			flipCheckbox.setToolTipText(trans.get("NoseConeCfg.checkbox.Flip.ttip")); | ||||||
|  | 			panel.add(flipCheckbox, "spanx, wrap"); | ||||||
|  | 			order.add(flipCheckbox); | ||||||
|  | 			flipCheckbox.addItemListener(new ItemListener() { | ||||||
|  | 				@Override | ||||||
|  | 				public void itemStateChanged(ItemEvent e) { | ||||||
|  | 					updateCheckboxAutoBaseRadius(e.getStateChange() == ItemEvent.SELECTED); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		panel.add(new JLabel(""), "growy"); | 		panel.add(new JLabel(""), "growy"); | ||||||
| 
 | 
 | ||||||
| 		////  Description | 		////  Description | ||||||
| @ -189,25 +205,29 @@ public class NoseConeConfig extends RocketComponentConfig { | |||||||
| 	 * Sets the checkAutoAftRadius checkbox's enabled state and tooltip text, based on the state of its next component. | 	 * Sets the checkAutoAftRadius checkbox's enabled state and tooltip text, based on the state of its next component. | ||||||
| 	 * If there is no next symmetric component or if that component already has its auto checkbox checked, the | 	 * If there is no next symmetric component or if that component already has its auto checkbox checked, the | ||||||
| 	 * checkAutoAftRadius checkbox is disabled. | 	 * checkAutoAftRadius checkbox is disabled. | ||||||
|  | 	 * | ||||||
|  | 	 * @param isFlipped	whether the nose cone is flipped | ||||||
| 	 */ | 	 */ | ||||||
| 	private void updateCheckboxAutoAftRadius() { | 	private void updateCheckboxAutoBaseRadius(boolean isFlipped) { | ||||||
| 		if (component == null || checkAutoAftRadius == null) return; | 		if (component == null || checkAutoBaseRadius == null) return; | ||||||
| 
 | 
 | ||||||
| 		// Disable check button if there is no component to get the diameter from | 		// Disable check button if there is no component to get the diameter from | ||||||
| 		SymmetricComponent nextComp = ((NoseCone) component).getNextSymmetricComponent(); | 		NoseCone noseCone = ((NoseCone) component); | ||||||
| 		if (nextComp == null) { | 		SymmetricComponent referenceComp = isFlipped ? noseCone.getPreviousSymmetricComponent() : noseCone.getNextSymmetricComponent(); | ||||||
| 			checkAutoAftRadius.setEnabled(false); | 		if (referenceComp == null) { | ||||||
| 			((NoseCone) component).setAftRadiusAutomatic(false); | 			checkAutoBaseRadius.setEnabled(false); | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent")); | 			((NoseCone) component).setBaseRadiusAutomatic(false); | ||||||
|  | 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		if (!nextComp.usesPreviousCompAutomatic()) { | 		if ((!isFlipped&& !referenceComp.usesPreviousCompAutomatic()) || | ||||||
| 			checkAutoAftRadius.setEnabled(true); | 				isFlipped && !referenceComp.usesNextCompAutomatic()) { | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic")); | 			checkAutoBaseRadius.setEnabled(true); | ||||||
|  | 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic")); | ||||||
| 		} else { | 		} else { | ||||||
| 			checkAutoAftRadius.setEnabled(false); | 			checkAutoBaseRadius.setEnabled(false); | ||||||
| 			((NoseCone) component).setAftRadiusAutomatic(false); | 			((NoseCone) component).setBaseRadiusAutomatic(false); | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto")); | 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -613,157 +613,170 @@ public class RocketComponentConfig extends JPanel { | |||||||
| 	 | 	 | ||||||
| 	protected JPanel shoulderTab() { | 	protected JPanel shoulderTab() { | ||||||
| 		JPanel panel = new JPanel(new MigLayout("fill")); | 		JPanel panel = new JPanel(new MigLayout("fill")); | ||||||
| 		JPanel sub; |  | ||||||
| 		DoubleModel m, m2; |  | ||||||
| 		DoubleModel m0 = new DoubleModel(0); | 		DoubleModel m0 = new DoubleModel(0); | ||||||
| 		BooleanModel bm; |  | ||||||
| 		JCheckBox check; |  | ||||||
| 		JSpinner spin; |  | ||||||
| 		 |  | ||||||
| 		 | 		 | ||||||
| 		////  Fore shoulder, not for NoseCone | 		////  Fore shoulder, not for NoseCone | ||||||
| 		 |  | ||||||
| 		if (!(component instanceof NoseCone)) { | 		if (!(component instanceof NoseCone)) { | ||||||
| 			sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); | 			addForeShoulderSection(panel, m0); | ||||||
| 			 |  | ||||||
| 			//// Fore shoulder |  | ||||||
| 			sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.border.Foreshoulder"))); |  | ||||||
| 			 |  | ||||||
| 			 |  | ||||||
| 			////  Radius |  | ||||||
| 			//// Diameter: |  | ||||||
| 			sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter"))); |  | ||||||
| 			 |  | ||||||
| 			m = new DoubleModel(component, "ForeShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0); |  | ||||||
| 			m2 = new DoubleModel(component, "ForeRadius", 2, UnitGroup.UNITS_LENGTH); |  | ||||||
| 			 |  | ||||||
| 			spin = new JSpinner(m.getSpinnerModel()); |  | ||||||
| 			spin.setEditor(new SpinnerEditor(spin)); |  | ||||||
| 			sub.add(spin, "growx"); |  | ||||||
| 			order.add(((SpinnerEditor) spin.getEditor()).getTextField()); |  | ||||||
| 			 |  | ||||||
| 			sub.add(new UnitSelector(m), "growx"); |  | ||||||
| 			sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); |  | ||||||
| 			 |  | ||||||
| 			 |  | ||||||
| 			////  Length: |  | ||||||
| 			sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); |  | ||||||
| 			 |  | ||||||
| 			m = new DoubleModel(component, "ForeShoulderLength", UnitGroup.UNITS_LENGTH, 0); |  | ||||||
| 			 |  | ||||||
| 			spin = new JSpinner(m.getSpinnerModel()); |  | ||||||
| 			spin.setEditor(new SpinnerEditor(spin)); |  | ||||||
| 			sub.add(spin, "growx"); |  | ||||||
| 			order.add(((SpinnerEditor) spin.getEditor()).getTextField()); |  | ||||||
| 			 |  | ||||||
| 			sub.add(new UnitSelector(m), "growx"); |  | ||||||
| 			sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap"); |  | ||||||
| 			 |  | ||||||
| 			 |  | ||||||
| 			////  Thickness: |  | ||||||
| 			sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); |  | ||||||
| 			 |  | ||||||
| 			m = new DoubleModel(component, "ForeShoulderThickness", UnitGroup.UNITS_LENGTH, 0); |  | ||||||
| 			m2 = new DoubleModel(component, "ForeShoulderRadius", UnitGroup.UNITS_LENGTH); |  | ||||||
| 			 |  | ||||||
| 			spin = new JSpinner(m.getSpinnerModel()); |  | ||||||
| 			spin.setEditor(new SpinnerEditor(spin)); |  | ||||||
| 			sub.add(spin, "growx"); |  | ||||||
| 			order.add(((SpinnerEditor) spin.getEditor()).getTextField()); |  | ||||||
| 			 |  | ||||||
| 			sub.add(new UnitSelector(m), "growx"); |  | ||||||
| 			sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); |  | ||||||
| 			 |  | ||||||
| 			 |  | ||||||
| 			////  Capped |  | ||||||
| 			bm = new BooleanModel(component, "ForeShoulderCapped"); |  | ||||||
| 			check = new JCheckBox(bm); |  | ||||||
| 			//// End capped |  | ||||||
| 			check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); |  | ||||||
| 			check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip")); |  | ||||||
| 			sub.add(check, "spanx"); |  | ||||||
| 			order.add(check); |  | ||||||
| 			 |  | ||||||
| 			 |  | ||||||
| 			panel.add(sub); |  | ||||||
| 		} | 		} | ||||||
| 		 | 
 | ||||||
| 		 |  | ||||||
| 		////  Aft shoulder | 		////  Aft shoulder | ||||||
|  | 		addAftShoulderSection(panel, m0); | ||||||
|  | 
 | ||||||
|  | 		return panel; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void addForeShoulderSection(JPanel panel, DoubleModel m0) { | ||||||
|  | 		DoubleModel m; | ||||||
|  | 		JCheckBox check; | ||||||
|  | 		JPanel sub; | ||||||
|  | 		DoubleModel m2; | ||||||
|  | 		JSpinner spin; | ||||||
|  | 		BooleanModel bm; | ||||||
| 		sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); | 		sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); | ||||||
| 		 | 
 | ||||||
| 		if (component instanceof NoseCone) | 		//// Fore shoulder | ||||||
| 			//// Nose cone shoulder | 		sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.border.Foreshoulder"))); | ||||||
| 			sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Noseconeshoulder"))); | 
 | ||||||
| 		else | 
 | ||||||
| 			//// Aft shoulder |  | ||||||
| 			sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Aftshoulder"))); |  | ||||||
| 		 |  | ||||||
| 		 |  | ||||||
| 		////  Radius | 		////  Radius | ||||||
| 		//// Diameter: | 		//// Diameter: | ||||||
| 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter"))); | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter"))); | ||||||
| 		 | 
 | ||||||
| 		m = new DoubleModel(component, "AftShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0); | 		m = new DoubleModel(component, "ForeShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0); | ||||||
| 		m2 = new DoubleModel(component, "AftRadius", 2, UnitGroup.UNITS_LENGTH); | 		m2 = new DoubleModel(component, "ForeRadius", 2, UnitGroup.UNITS_LENGTH); | ||||||
| 		 | 
 | ||||||
| 		spin = new JSpinner(m.getSpinnerModel()); | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
| 		spin.setEditor(new SpinnerEditor(spin)); | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
| 		sub.add(spin, "growx"); | 		sub.add(spin, "growx"); | ||||||
| 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
| 		 | 
 | ||||||
| 		sub.add(new UnitSelector(m), "growx"); | 		sub.add(new UnitSelector(m), "growx"); | ||||||
| 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | ||||||
| 		 | 
 | ||||||
| 		 | 
 | ||||||
| 		////  Length: | 		////  Length: | ||||||
| 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); | ||||||
| 		 | 
 | ||||||
| 		m = new DoubleModel(component, "AftShoulderLength", UnitGroup.UNITS_LENGTH, 0); | 		m = new DoubleModel(component, "ForeShoulderLength", UnitGroup.UNITS_LENGTH, 0); | ||||||
| 		 | 
 | ||||||
| 		spin = new JSpinner(m.getSpinnerModel()); | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
| 		spin.setEditor(new SpinnerEditor(spin)); | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
| 		sub.add(spin, "growx"); | 		sub.add(spin, "growx"); | ||||||
| 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
| 		 | 
 | ||||||
| 		sub.add(new UnitSelector(m), "growx"); | 		sub.add(new UnitSelector(m), "growx"); | ||||||
| 		sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap"); | 		sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap"); | ||||||
| 		 | 
 | ||||||
| 		 | 
 | ||||||
| 		////  Thickness: | 		////  Thickness: | ||||||
| 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); | ||||||
| 		 | 
 | ||||||
| 		m = new DoubleModel(component, "AftShoulderThickness", UnitGroup.UNITS_LENGTH, 0); | 		m = new DoubleModel(component, "ForeShoulderThickness", UnitGroup.UNITS_LENGTH, 0); | ||||||
| 		m2 = new DoubleModel(component, "AftShoulderRadius", UnitGroup.UNITS_LENGTH); | 		m2 = new DoubleModel(component, "ForeShoulderRadius", UnitGroup.UNITS_LENGTH); | ||||||
| 		 | 
 | ||||||
| 		spin = new JSpinner(m.getSpinnerModel()); | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
| 		spin.setEditor(new SpinnerEditor(spin)); | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
| 		sub.add(spin, "growx"); | 		sub.add(spin, "growx"); | ||||||
| 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
| 		 | 
 | ||||||
| 		sub.add(new UnitSelector(m), "growx"); | 		sub.add(new UnitSelector(m), "growx"); | ||||||
| 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | ||||||
| 		 | 
 | ||||||
| 		 | 
 | ||||||
| 		////  Capped | 		////  Capped | ||||||
| 		bm = new BooleanModel(component, "AftShoulderCapped"); | 		bm = new BooleanModel(component, "ForeShoulderCapped"); | ||||||
| 		check = new JCheckBox(bm); | 		check = new JCheckBox(bm); | ||||||
| 		//// End capped | 		//// End capped | ||||||
| 		check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); | 		check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); | ||||||
| 		check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip")); | 		check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip")); | ||||||
| 		sub.add(check, "spanx"); | 		sub.add(check, "spanx"); | ||||||
| 		order.add(check); | 		order.add(check); | ||||||
| 		 | 
 | ||||||
| 		 |  | ||||||
| 		panel.add(sub); | 		panel.add(sub); | ||||||
| 		 |  | ||||||
| 		 |  | ||||||
| 		return panel; |  | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	 | 	private void addAftShoulderSection(JPanel panel, DoubleModel m0) { | ||||||
| 	 | 		JSpinner spin; | ||||||
| 	 | 		JCheckBox check; | ||||||
|  | 		DoubleModel m; | ||||||
|  | 		DoubleModel m2; | ||||||
|  | 		JPanel sub; | ||||||
|  | 		BooleanModel bm; | ||||||
|  | 		sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", "")); | ||||||
|  | 
 | ||||||
|  | 		String valueNameShoulder = "AftShoulder"; | ||||||
|  | 		String valueNameRadius = "AftRadius"; | ||||||
|  | 
 | ||||||
|  | 		if (component instanceof NoseCone) { | ||||||
|  | 			// Nose cones have a special shoulder method to cope with flipped nose cones | ||||||
|  | 			valueNameShoulder = "Shoulder"; | ||||||
|  | 			valueNameRadius = "BaseRadius"; | ||||||
|  | 			//// Nose cone shoulder | ||||||
|  | 			sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Noseconeshoulder"))); | ||||||
|  | 		} else { | ||||||
|  | 			//// Aft shoulder | ||||||
|  | 			sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Aftshoulder"))); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		////  Radius | ||||||
|  | 		//// Diameter: | ||||||
|  | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter"))); | ||||||
|  | 
 | ||||||
|  | 		m = new DoubleModel(component, valueNameShoulder+"Radius", 2, UnitGroup.UNITS_LENGTH, 0); | ||||||
|  | 		m2 = new DoubleModel(component, valueNameRadius, 2, UnitGroup.UNITS_LENGTH); | ||||||
|  | 
 | ||||||
|  | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
|  | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
|  | 		sub.add(spin, "growx"); | ||||||
|  | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
|  | 
 | ||||||
|  | 		sub.add(new UnitSelector(m), "growx"); | ||||||
|  | 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		////  Length: | ||||||
|  | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); | ||||||
|  | 
 | ||||||
|  | 		m = new DoubleModel(component, valueNameShoulder+"Length", UnitGroup.UNITS_LENGTH, 0); | ||||||
|  | 
 | ||||||
|  | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
|  | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
|  | 		sub.add(spin, "growx"); | ||||||
|  | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
|  | 
 | ||||||
|  | 		sub.add(new UnitSelector(m), "growx"); | ||||||
|  | 		sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		////  Thickness: | ||||||
|  | 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); | ||||||
|  | 
 | ||||||
|  | 		m = new DoubleModel(component, valueNameShoulder+"Thickness", UnitGroup.UNITS_LENGTH, 0); | ||||||
|  | 		m2 = new DoubleModel(component, valueNameShoulder+"Radius", UnitGroup.UNITS_LENGTH); | ||||||
|  | 
 | ||||||
|  | 		spin = new JSpinner(m.getSpinnerModel()); | ||||||
|  | 		spin.setEditor(new SpinnerEditor(spin)); | ||||||
|  | 		sub.add(spin, "growx"); | ||||||
|  | 		order.add(((SpinnerEditor) spin.getEditor()).getTextField()); | ||||||
|  | 
 | ||||||
|  | 		sub.add(new UnitSelector(m), "growx"); | ||||||
|  | 		sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		////  Capped | ||||||
|  | 		bm = new BooleanModel(component, valueNameShoulder+"Capped"); | ||||||
|  | 		check = new JCheckBox(bm); | ||||||
|  | 		//// End capped | ||||||
|  | 		check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); | ||||||
|  | 		check.setToolTipText(trans.get("RocketCompCfg.checkbox.Endcapped.ttip")); | ||||||
|  | 		sub.add(check, "spanx"); | ||||||
|  | 		order.add(check); | ||||||
|  | 
 | ||||||
|  | 		panel.add(sub); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/* | 	/* | ||||||
| 	 * Private inner class to handle events in componentNameField. | 	 * Private inner class to handle events in componentNameField. | ||||||
| 	 */ | 	 */ | ||||||
|  | |||||||
| @ -228,18 +228,16 @@ public class TransitionConfig extends RocketComponentConfig { | |||||||
| 	private void updateCheckboxAutoAftRadius() { | 	private void updateCheckboxAutoAftRadius() { | ||||||
| 		if (component == null || checkAutoAftRadius == null) return; | 		if (component == null || checkAutoAftRadius == null) return; | ||||||
| 
 | 
 | ||||||
| 		// Disable check button if there is no component to get the diameter from | 		Transition transition = (Transition) component; | ||||||
| 		SymmetricComponent nextComp = ((Transition) component).getNextSymmetricComponent(); | 		boolean enabled = transition.canUseNextCompAutomatic(); | ||||||
| 		if (nextComp == null) { | 		if (enabled) {														// Can use auto radius | ||||||
|  | 			checkAutoAftRadius.setEnabled(true); | ||||||
|  | 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); | ||||||
|  | 		} else if (transition.getNextSymmetricComponent() == null) {		// No next component to take the auto radius from | ||||||
| 			checkAutoAftRadius.setEnabled(false); | 			checkAutoAftRadius.setEnabled(false); | ||||||
| 			((Transition) component).setAftRadiusAutomatic(false); | 			((Transition) component).setAftRadiusAutomatic(false); | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||||
| 			return; | 		} else {															// Next component already has its auto radius checked | ||||||
| 		} |  | ||||||
| 		if (!nextComp.usesPreviousCompAutomatic()) { |  | ||||||
| 			checkAutoAftRadius.setEnabled(true); |  | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); |  | ||||||
| 		} else { |  | ||||||
| 			checkAutoAftRadius.setEnabled(false); | 			checkAutoAftRadius.setEnabled(false); | ||||||
| 			((Transition) component).setAftRadiusAutomatic(false); | 			((Transition) component).setAftRadiusAutomatic(false); | ||||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||||
| @ -254,18 +252,16 @@ public class TransitionConfig extends RocketComponentConfig { | |||||||
| 	private void updateCheckboxAutoForeRadius() { | 	private void updateCheckboxAutoForeRadius() { | ||||||
| 		if (component == null || checkAutoForeRadius == null) return; | 		if (component == null || checkAutoForeRadius == null) return; | ||||||
| 
 | 
 | ||||||
| 		// Disable check button if there is no component to get the diameter from | 		Transition transition = (Transition) component; | ||||||
| 		SymmetricComponent prevComp = ((Transition) component).getPreviousSymmetricComponent(); | 		boolean enabled = transition.canUsePreviousCompAutomatic(); | ||||||
| 		if (prevComp == null) { | 		if (enabled) {														// Can use auto radius | ||||||
|  | 			checkAutoForeRadius.setEnabled(true); | ||||||
|  | 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); | ||||||
|  | 		} else if (transition.getPreviousSymmetricComponent() == null) {		// No next component to take the auto radius from | ||||||
| 			checkAutoForeRadius.setEnabled(false); | 			checkAutoForeRadius.setEnabled(false); | ||||||
| 			((Transition) component).setForeRadiusAutomatic(false); | 			((Transition) component).setForeRadiusAutomatic(false); | ||||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||||
| 			return; | 		} else {															// Next component already has its auto radius checked | ||||||
| 		} |  | ||||||
| 		if (!prevComp.usesNextCompAutomatic()) { |  | ||||||
| 			checkAutoForeRadius.setEnabled(true); |  | ||||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); |  | ||||||
| 		} else { |  | ||||||
| 			checkAutoForeRadius.setEnabled(false); | 			checkAutoForeRadius.setEnabled(false); | ||||||
| 			((Transition) component).setForeRadiusAutomatic(false); | 			((Transition) component).setForeRadiusAutomatic(false); | ||||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||||
|  | |||||||
| @ -83,7 +83,7 @@ public class ScaleDialog extends JDialog { | |||||||
| 		// SymmetricComponent | 		// SymmetricComponent | ||||||
| 		addScaler(SymmetricComponent.class, "Thickness", "isFilled", SCALERS_NO_OFFSET); | 		addScaler(SymmetricComponent.class, "Thickness", "isFilled", SCALERS_NO_OFFSET); | ||||||
| 		 | 		 | ||||||
| 		// Transition + Nose cone | 		// Transition | ||||||
| 		addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic", SCALERS_NO_OFFSET); | ||||||
| 		addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic", SCALERS_NO_OFFSET); | ||||||
| 		addScaler(Transition.class, "ForeShoulderRadius", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "ForeShoulderRadius", SCALERS_NO_OFFSET); | ||||||
| @ -92,6 +92,12 @@ public class ScaleDialog extends JDialog { | |||||||
| 		addScaler(Transition.class, "AftShoulderRadius", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "AftShoulderRadius", SCALERS_NO_OFFSET); | ||||||
| 		addScaler(Transition.class, "AftShoulderThickness", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "AftShoulderThickness", SCALERS_NO_OFFSET); | ||||||
| 		addScaler(Transition.class, "AftShoulderLength", SCALERS_NO_OFFSET); | 		addScaler(Transition.class, "AftShoulderLength", SCALERS_NO_OFFSET); | ||||||
|  | 
 | ||||||
|  | 		// Nose cone | ||||||
|  | 		addScaler(NoseCone.class, "BaseRadius", "isBaseRadiusAutomatic", SCALERS_NO_OFFSET); | ||||||
|  | 		addScaler(NoseCone.class, "ShoulderRadius", SCALERS_NO_OFFSET); | ||||||
|  | 		addScaler(NoseCone.class, "ShoulderThickness", SCALERS_NO_OFFSET); | ||||||
|  | 		addScaler(NoseCone.class, "ShoulderLength", SCALERS_NO_OFFSET); | ||||||
| 		 | 		 | ||||||
| 		// Body tube | 		// Body tube | ||||||
| 		addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic", SCALERS_NO_OFFSET); | 		addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic", SCALERS_NO_OFFSET); | ||||||
| @ -559,6 +565,10 @@ public class ScaleDialog extends JDialog { | |||||||
| 		} | 		} | ||||||
| 		Collections.reverse(classes);	// Always do the super component scales first (can cause problems otherwise in the scale order) | 		Collections.reverse(classes);	// Always do the super component scales first (can cause problems otherwise in the scale order) | ||||||
| 		for (Class<?> cl : classes) { | 		for (Class<?> cl : classes) { | ||||||
|  | 			// Don't use the super-class methods of transitions for nose cones | ||||||
|  | 			if (cl == Transition.class && component instanceof NoseCone) { | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			List<Scaler> list = SCALERS_NO_OFFSET.get(cl); | 			List<Scaler> list = SCALERS_NO_OFFSET.get(cl); | ||||||
| 			if (list != null && list.size() > 0) { | 			if (list != null && list.size() > 0) { | ||||||
| 				for (Scaler s : list) { | 				for (Scaler s : list) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user