[#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.Shoulder = Shoulder | ||||
| 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 | ||||
| Parachute.Parachute = Parachute | ||||
|  | ||||
| @ -10,9 +10,11 @@ import net.sf.openrocket.util.Reflection; | ||||
| //// BooleanSetter - set a boolean value | ||||
| class BooleanSetter implements Setter { | ||||
| 	private final Reflection.Method setMethod; | ||||
| 	private Object[] extraParameters = null; | ||||
| 	 | ||||
| 	public BooleanSetter(Reflection.Method set) { | ||||
| 	public BooleanSetter(Reflection.Method set, Object... parameters) { | ||||
| 		setMethod = set; | ||||
| 		this.extraParameters = parameters; | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| @ -20,12 +22,23 @@ class BooleanSetter implements Setter { | ||||
| 			WarningSet warnings) { | ||||
| 		 | ||||
| 		s = s.trim(); | ||||
| 		final boolean setValue; | ||||
| 		if (s.equalsIgnoreCase("true")) { | ||||
| 			setMethod.invoke(c, true); | ||||
| 			setValue = true; | ||||
| 		} else if (s.equalsIgnoreCase("false")) { | ||||
| 			setMethod.invoke(c, false); | ||||
| 			setValue = false; | ||||
| 		} else { | ||||
| 			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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -249,6 +249,8 @@ class DocumentConfig { | ||||
| 		setters.put("Transition:aftshouldercapped", new BooleanSetter( | ||||
| 				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 | ||||
| 		setters.put("NoseCone:foreradius", null); | ||||
| 		setters.put("NoseCone:foreshoulderradius", null); | ||||
|  | ||||
| @ -1,7 +1,10 @@ | ||||
| package net.sf.openrocket.file.openrocket.savers; | ||||
| 
 | ||||
| import net.sf.openrocket.rocketcomponent.NoseCone; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| public class NoseConeSaver extends TransitionSaver { | ||||
| 
 | ||||
| @ -20,8 +23,23 @@ public class NoseConeSaver extends TransitionSaver { | ||||
| 
 | ||||
| 	@Override | ||||
| 	protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) { | ||||
| 		NoseCone noseCone = (NoseCone) c; | ||||
| 		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) { | ||||
| 		super.addParams(c, elements); | ||||
| 		net.sf.openrocket.rocketcomponent.Transition trans = (net.sf.openrocket.rocketcomponent.Transition) c; | ||||
| 		boolean nosecone = (trans instanceof NoseCone); | ||||
| 		 | ||||
| 		 | ||||
| 		Transition.Shape shape = trans.getType(); | ||||
| 		elements.add("<shape>" + shape.name().toLowerCase(Locale.ENGLISH) + "</shape>"); | ||||
| @ -42,13 +40,15 @@ public class TransitionSaver extends SymmetricComponentSaver { | ||||
| 			elements.add("<shapeparameter>" + trans.getShapeParameter() + "</shapeparameter>"); | ||||
| 		} | ||||
| 
 | ||||
| 		// Nose cones need other parameter saving, due to the isFlipped() parameter | ||||
| 		if (trans instanceof NoseCone) { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!nosecone) { | ||||
| 		if (trans.isForeRadiusAutomatic()) | ||||
| 			elements.add("<foreradius>auto " + trans.getForeRadiusNoAutomatic() + "</foreradius>"); | ||||
| 		else | ||||
| 			elements.add("<foreradius>" + trans.getForeRadius() + "</foreradius>"); | ||||
| 		} | ||||
| 		 | ||||
| 		if (trans.isAftRadiusAutomatic()) | ||||
| 			elements.add("<aftradius>auto " + trans.getAftRadiusNoAutomatic() + "</aftradius>"); | ||||
| @ -56,7 +56,6 @@ public class TransitionSaver extends SymmetricComponentSaver { | ||||
| 			elements.add("<aftradius>" + trans.getAftRadius() + "</aftradius>"); | ||||
| 		 | ||||
| 		 | ||||
| 		if (!nosecone) { | ||||
| 		elements.add("<foreshoulderradius>" + trans.getForeShoulderRadius() | ||||
| 				+ "</foreshoulderradius>"); | ||||
| 		elements.add("<foreshoulderlength>" + trans.getForeShoulderLength() | ||||
| @ -65,7 +64,6 @@ public class TransitionSaver extends SymmetricComponentSaver { | ||||
| 				+ "</foreshoulderthickness>"); | ||||
| 		elements.add("<foreshouldercapped>" + trans.isForeShoulderCapped() | ||||
| 				+ "</foreshouldercapped>"); | ||||
| 		} | ||||
| 		 | ||||
| 		elements.add("<aftshoulderradius>" + trans.getAftShoulderRadius() | ||||
| 				+ "</aftshoulderradius>"); | ||||
|  | ||||
| @ -65,7 +65,11 @@ public class PodSetDTO extends BasePartDTO implements AttachableParts { | ||||
|             } else if (child instanceof BodyTube) { | ||||
|                 addAttachedPart(new BodyTubeDTO((BodyTube) child)); | ||||
|             } else if (child instanceof NoseCone) { | ||||
|                 if (((NoseCone) child).isFlipped()) { | ||||
|                     addAttachedPart(new TransitionDTO((NoseCone) child)); | ||||
|                 } else { | ||||
|                     addAttachedPart(new NoseConeDTO((NoseCone) child)); | ||||
|                 } | ||||
|             } else if (child instanceof Transition) { | ||||
|                 addAttachedPart(new TransitionDTO((Transition) child)); | ||||
|             } | ||||
|  | ||||
| @ -93,9 +93,13 @@ public class StageDTO { | ||||
|         externalPart.add(theExternalPartDTO); | ||||
|     } | ||||
| 
 | ||||
|     private NoseConeDTO toNoseConeDTO(NoseCone nc) { | ||||
|     private AbstractTransitionDTO toNoseConeDTO(NoseCone nc) { | ||||
|         if (nc.isFlipped()) { | ||||
|             return new TransitionDTO(nc); | ||||
|         } else { | ||||
|             return new NoseConeDTO(nc); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private BodyTubeDTO toBodyTubeDTO(BodyTube bt) { | ||||
|         return new BodyTubeDTO(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.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.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 | ||||
|  * 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> | ||||
|  */ | ||||
| @ -21,6 +24,7 @@ public class NoseCone extends Transition implements InsideColorComponent { | ||||
| 	private static final Translator trans = Application.getTranslator(); | ||||
| 
 | ||||
| 	private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); | ||||
| 	private boolean isFlipped;		// If true, the nose cone is converted to a tail cone | ||||
| 	 | ||||
| 	/********* Constructors **********/ | ||||
| 	public NoseCone() { | ||||
| @ -29,16 +33,12 @@ public class NoseCone extends Transition implements InsideColorComponent { | ||||
| 	 | ||||
| 	public NoseCone(Transition.Shape type, double length, double radius) { | ||||
| 		super(); | ||||
| 		this.isFlipped = false; | ||||
| 		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.setLength(length); | ||||
| 		super.setClipped(false); | ||||
| 		resetForeRadius(); | ||||
| 		 | ||||
| 		super.setAftRadiusAutomatic(false); | ||||
| 		super.setAftRadius(radius); | ||||
| @ -47,72 +47,208 @@ public class NoseCone extends Transition implements InsideColorComponent { | ||||
| 		super.displayOrder_back = 0;		// Order for displaying the component in the 2D back view | ||||
| 	} | ||||
| 
 | ||||
| 	/********** Nose cone dimensions  **********/ | ||||
| 
 | ||||
| 	/********** Get/set methods for component parameters **********/ | ||||
| 	 | ||||
| 	@Override | ||||
| 	public double getForeRadius() { | ||||
| 		return 0; | ||||
| 	/** | ||||
| 	 * Returns the base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||
| 	 * This method should be used over {@link #getAftRadius()} because it works for both normal and flipped nose cones. | ||||
| 	 */ | ||||
| 	public double getBaseRadius() { | ||||
| 		return isFlipped ? getForeRadius() : getAftRadius(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void setForeRadius(double r) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * Returns the raw base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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 boolean isForeRadiusAutomatic() { | ||||
| 		return false; | ||||
| 	/** | ||||
| 	 * Sets the base radius of the nose cone (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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 void setForeRadiusAutomatic(boolean b) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * Returns whether the base radius of the nose cone takes it settings from the previous/next component | ||||
| 	 * (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 boolean usesPreviousCompAutomatic() { | ||||
| 		return false; | ||||
| 	/** | ||||
| 	 * Sets whether the base radius of the nose cone takes it settings from the previous/next component | ||||
| 	 * (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 double getForeShoulderLength() { | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public double getForeShoulderRadius() { | ||||
| 		return 0; | ||||
| 	/** | ||||
| 	 * Returns the shoulder length, regardless of how the nose cone is flipped (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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 double getForeShoulderThickness() { | ||||
| 		return 0; | ||||
| 	/** | ||||
| 	 * Sets the shoulder length (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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 boolean isForeShoulderCapped() { | ||||
| 		return false; | ||||
| 	/** | ||||
| 	 * Returns the shoulder radius (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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 setForeShoulderCapped(boolean capped) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * Sets the shoulder radius (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 setShoulderRadius(double radius) { | ||||
| 		if (isFlipped) { | ||||
| 			setForeShoulderRadius(radius); | ||||
| 		} else { | ||||
| 			setAftShoulderRadius(radius); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void setForeShoulderLength(double foreShoulderLength) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * Returns the shoulder thickness (independent of whether the nose cone is flipped or not). | ||||
| 	 * 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(); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void setForeShoulderRadius(double foreShoulderRadius) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * 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); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void setForeShoulderThickness(double foreShoulderThickness) { | ||||
| 		// No-op | ||||
| 	/** | ||||
| 	 * 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 | ||||
| @ -125,8 +261,6 @@ public class NoseCone extends Transition implements InsideColorComponent { | ||||
| 		// No-op | ||||
| 	} | ||||
| 
 | ||||
| 	 | ||||
| 
 | ||||
| 	/********** RocketComponent methods **********/ | ||||
| 	 | ||||
| 	@Override | ||||
| @ -136,7 +270,7 @@ public class NoseCone extends Transition implements InsideColorComponent { | ||||
| 
 | ||||
| 	@Override | ||||
| 	protected void loadFromPreset(ComponentPreset preset) { | ||||
| 		 | ||||
| 		setFlipped(false); | ||||
| 		//Many parameters are handled by the super class Transition.loadFromPreset | ||||
| 		super.loadFromPreset(preset); | ||||
| 	} | ||||
|  | ||||
| @ -5,17 +5,13 @@ import static net.sf.openrocket.util.MathUtil.pow2; | ||||
| import static net.sf.openrocket.util.MathUtil.pow3; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.EventObject; | ||||
| 
 | ||||
| import net.sf.openrocket.appearance.Appearance; | ||||
| import net.sf.openrocket.appearance.Decal; | ||||
| import net.sf.openrocket.l10n.Translator; | ||||
| import net.sf.openrocket.preset.ComponentPreset; | ||||
| import net.sf.openrocket.preset.ComponentPreset.Type; | ||||
| import net.sf.openrocket.startup.Application; | ||||
| import net.sf.openrocket.util.Coordinate; | ||||
| import net.sf.openrocket.util.MathUtil; | ||||
| import net.sf.openrocket.util.StateChangeListener; | ||||
| 
 | ||||
| 
 | ||||
| public class Transition extends SymmetricComponent implements InsideColorComponent { | ||||
| @ -27,8 +23,8 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 	private double shapeParameter; | ||||
| 	private boolean clipped; // Not to be read - use isClipped(), which may be overridden | ||||
| 
 | ||||
| 	private double foreRadius, aftRadius; | ||||
| 	private boolean autoForeRadius, autoAftRadius2; // Whether the start radius is automatic | ||||
| 	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) | ||||
| 	protected boolean autoForeRadius, autoAftRadius; // Whether the start radius is automatic | ||||
| 
 | ||||
| 
 | ||||
| 	private double foreShoulderRadius; | ||||
| @ -53,7 +49,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 		this.aftRadius = DEFAULT_RADIUS; | ||||
| 		this.length = DEFAULT_RADIUS * 3; | ||||
| 		this.autoForeRadius = true; | ||||
| 		this.autoAftRadius2 = true; | ||||
| 		this.autoAftRadius = true; | ||||
| 
 | ||||
| 		this.type = Shape.CONICAL; | ||||
| 		this.shapeParameter = 0; | ||||
| @ -81,19 +77,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 	@Override | ||||
| 	public double getForeRadius() { | ||||
| 		if (isForeRadiusAutomatic()) { | ||||
| 			// Get the automatic radius from the front | ||||
| 			double r = -1; | ||||
| 			SymmetricComponent c = this.getPreviousSymmetricComponent(); | ||||
| 			if (c != null) { | ||||
| 				r = c.getFrontAutoRadius(); | ||||
| 			} | ||||
| 			if (r < 0) | ||||
| 				r = DEFAULT_RADIUS; | ||||
| 			return r; | ||||
| 			return getAutoForeRadius(); | ||||
| 		} | ||||
| 		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 | ||||
| 	 * fore radius. | ||||
| @ -136,13 +137,24 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 		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) { | ||||
| 			if (listener instanceof Transition) { | ||||
| 				((Transition) listener).setForeRadiusAutomatic(auto); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// You can only set the auto fore radius if it is possible | ||||
| 		if (sanityCheck) { | ||||
| 			auto = auto && canUsePreviousCompAutomatic(); | ||||
| 		} | ||||
| 
 | ||||
| 		if (autoForeRadius == auto) | ||||
| 			return; | ||||
| 
 | ||||
| @ -152,25 +164,34 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setForeRadiusAutomatic(boolean auto) { | ||||
| 		setForeRadiusAutomatic(auto, false); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	////////  Aft radius  ///////// | ||||
| 
 | ||||
| 	@Override | ||||
| 	public double getAftRadius() { | ||||
| 		if (isAftRadiusAutomatic()) { | ||||
| 			// Return the auto radius from the rear | ||||
| 			double r = -1; | ||||
| 			SymmetricComponent c = this.getNextSymmetricComponent(); | ||||
| 			if (c != null) { | ||||
| 				r = c.getRearAutoRadius(); | ||||
| 			} | ||||
| 			if (r < 0) | ||||
| 				r = DEFAULT_RADIUS; | ||||
| 			return r; | ||||
| 			return getAutoAftRadius(); | ||||
| 		} | ||||
| 		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 | ||||
| 	 * 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; | ||||
| 
 | ||||
| 		this.autoAftRadius2 = false; | ||||
| 		this.autoAftRadius = false; | ||||
| 		this.aftRadius = Math.max(radius, 0); | ||||
| 
 | ||||
| 		if (doClamping && this.thickness > this.foreRadius && this.thickness > this.aftRadius) | ||||
| @ -210,25 +231,40 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 
 | ||||
| 	@Override | ||||
| 	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) { | ||||
| 			if (listener instanceof Transition) { | ||||
| 				((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; | ||||
| 
 | ||||
| 		autoAftRadius2 = auto; | ||||
| 		autoAftRadius = auto; | ||||
| 
 | ||||
| 		clearPreset(); | ||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setAftRadiusAutomatic(boolean auto) { | ||||
| 		setAftRadiusAutomatic(auto, false); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	//// Radius automatics | ||||
| 
 | ||||
| @ -257,6 +293,32 @@ public class Transition extends SymmetricComponent implements InsideColorCompone | ||||
| 		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  ///////// | ||||
| 
 | ||||
|  | ||||
| @ -58,6 +58,7 @@ The following file format versions exist: | ||||
|       Added PhotoStudio settings saving (<photostudio>) | ||||
|       Added override CD parameter (<overridecd>) | ||||
|       Added stage activeness remembrance (<stage> under <motorconfiguration>) | ||||
|       Added <isflipped> parameter for Nose Cones | ||||
|       Separated <overridesubcomponents> into individual parameters for mass, CG, and CD. | ||||
|       Rename <fincount> to <instancecount> (<fincount> 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.ActionListener; | ||||
| import java.awt.event.ItemEvent; | ||||
| import java.awt.event.ItemListener; | ||||
| 
 | ||||
| import javax.swing.JCheckBox; | ||||
| import javax.swing.JComboBox; | ||||
| @ -40,7 +42,7 @@ public class NoseConeConfig extends RocketComponentConfig { | ||||
| 	private JLabel shapeLabel; | ||||
| 	private JSpinner shapeSpinner; | ||||
| 	private JSlider shapeSlider; | ||||
| 	private final JCheckBox checkAutoAftRadius; | ||||
| 	private final JCheckBox checkAutoBaseRadius; | ||||
| 	private static final Translator trans = Application.getTranslator(); | ||||
| 	 | ||||
| 	// 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"))); | ||||
| 
 | ||||
| 			final DoubleModel aftRadiusModel = new DoubleModel(component, "AftRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius | ||||
| 			final JSpinner radiusSpinner = new JSpinner(aftRadiusModel.getSpinnerModel()); | ||||
| 			final DoubleModel baseRadius = new DoubleModel(component, "BaseRadius", 2.0, UnitGroup.UNITS_LENGTH, 0); // Diameter = 2*Radius | ||||
| 			final JSpinner radiusSpinner = new JSpinner(baseRadius.getSpinnerModel()); | ||||
| 			radiusSpinner.setEditor(new SpinnerEditor(radiusSpinner)); | ||||
| 			panel.add(radiusSpinner, "growx"); | ||||
| 			order.add(((SpinnerEditor) radiusSpinner.getEditor()).getTextField()); | ||||
| 
 | ||||
| 			panel.add(new UnitSelector(aftRadiusModel), "growx"); | ||||
| 			panel.add(new BasicSlider(aftRadiusModel.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap 0px"); | ||||
| 			panel.add(new UnitSelector(baseRadius), "growx"); | ||||
| 			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 | ||||
| 			checkAutoAftRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic")); | ||||
| 			panel.add(checkAutoAftRadius, "skip, span 2, wrap"); | ||||
| 			order.add(checkAutoAftRadius); | ||||
| 			updateCheckboxAutoAftRadius(); | ||||
| 			checkAutoBaseRadius.setText(trans.get("NoseConeCfg.checkbox.Automatic")); | ||||
| 			panel.add(checkAutoBaseRadius, "skip, span 2, wrap"); | ||||
| 			order.add(checkAutoBaseRadius); | ||||
| 			updateCheckboxAutoBaseRadius(((NoseCone) component).isFlipped()); | ||||
| 		} | ||||
| 
 | ||||
| 		{////  Wall thickness: | ||||
| @ -142,10 +144,24 @@ public class NoseConeConfig extends RocketComponentConfig { | ||||
| 			//// Filled | ||||
| 			filledCheckbox.setText(trans.get("NoseConeCfg.checkbox.Filled")); | ||||
| 			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); | ||||
| 		} | ||||
| 
 | ||||
| 		{//// 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"); | ||||
| 
 | ||||
| 		////  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. | ||||
| 	 * If there is no next symmetric component or if that component already has its auto checkbox checked, the | ||||
| 	 * checkAutoAftRadius checkbox is disabled. | ||||
| 	 * | ||||
| 	 * @param isFlipped	whether the nose cone is flipped | ||||
| 	 */ | ||||
| 	private void updateCheckboxAutoAftRadius() { | ||||
| 		if (component == null || checkAutoAftRadius == null) return; | ||||
| 	private void updateCheckboxAutoBaseRadius(boolean isFlipped) { | ||||
| 		if (component == null || checkAutoBaseRadius == null) return; | ||||
| 
 | ||||
| 		// Disable check button if there is no component to get the diameter from | ||||
| 		SymmetricComponent nextComp = ((NoseCone) component).getNextSymmetricComponent(); | ||||
| 		if (nextComp == null) { | ||||
| 			checkAutoAftRadius.setEnabled(false); | ||||
| 			((NoseCone) component).setAftRadiusAutomatic(false); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||
| 		NoseCone noseCone = ((NoseCone) component); | ||||
| 		SymmetricComponent referenceComp = isFlipped ? noseCone.getPreviousSymmetricComponent() : noseCone.getNextSymmetricComponent(); | ||||
| 		if (referenceComp == null) { | ||||
| 			checkAutoBaseRadius.setEnabled(false); | ||||
| 			((NoseCone) component).setBaseRadiusAutomatic(false); | ||||
| 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (!nextComp.usesPreviousCompAutomatic()) { | ||||
| 			checkAutoAftRadius.setEnabled(true); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic")); | ||||
| 		if ((!isFlipped&& !referenceComp.usesPreviousCompAutomatic()) || | ||||
| 				isFlipped && !referenceComp.usesNextCompAutomatic()) { | ||||
| 			checkAutoBaseRadius.setEnabled(true); | ||||
| 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic")); | ||||
| 		} else { | ||||
| 			checkAutoAftRadius.setEnabled(false); | ||||
| 			((NoseCone) component).setAftRadiusAutomatic(false); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||
| 			checkAutoBaseRadius.setEnabled(false); | ||||
| 			((NoseCone) component).setBaseRadiusAutomatic(false); | ||||
| 			checkAutoBaseRadius.setToolTipText(trans.get("NoseConeCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -613,17 +613,26 @@ public class RocketComponentConfig extends JPanel { | ||||
| 	 | ||||
| 	protected JPanel shoulderTab() { | ||||
| 		JPanel panel = new JPanel(new MigLayout("fill")); | ||||
| 		JPanel sub; | ||||
| 		DoubleModel m, m2; | ||||
| 		DoubleModel m0 = new DoubleModel(0); | ||||
| 		BooleanModel bm; | ||||
| 		JCheckBox check; | ||||
| 		JSpinner spin; | ||||
| 		 | ||||
| 		 | ||||
| 		////  Fore shoulder, not for NoseCone | ||||
| 		 | ||||
| 		if (!(component instanceof NoseCone)) { | ||||
| 			addForeShoulderSection(panel, m0); | ||||
| 		} | ||||
| 
 | ||||
| 		////  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::]", "")); | ||||
| 
 | ||||
| 		//// Fore shoulder | ||||
| @ -684,28 +693,39 @@ public class RocketComponentConfig extends JPanel { | ||||
| 		sub.add(check, "spanx"); | ||||
| 		order.add(check); | ||||
| 
 | ||||
| 			 | ||||
| 		panel.add(sub); | ||||
| 	} | ||||
| 
 | ||||
| 		 | ||||
| 		////  Aft shoulder | ||||
| 	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::]", "")); | ||||
| 
 | ||||
| 		if (component instanceof NoseCone) | ||||
| 		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 | ||||
| 		} 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, "AftShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0); | ||||
| 		m2 = new DoubleModel(component, "AftRadius", 2, UnitGroup.UNITS_LENGTH); | ||||
| 		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)); | ||||
| @ -719,7 +739,7 @@ public class RocketComponentConfig extends JPanel { | ||||
| 		////  Length: | ||||
| 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length"))); | ||||
| 
 | ||||
| 		m = new DoubleModel(component, "AftShoulderLength", UnitGroup.UNITS_LENGTH, 0); | ||||
| 		m = new DoubleModel(component, valueNameShoulder+"Length", UnitGroup.UNITS_LENGTH, 0); | ||||
| 
 | ||||
| 		spin = new JSpinner(m.getSpinnerModel()); | ||||
| 		spin.setEditor(new SpinnerEditor(spin)); | ||||
| @ -733,8 +753,8 @@ public class RocketComponentConfig extends JPanel { | ||||
| 		////  Thickness: | ||||
| 		sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness"))); | ||||
| 
 | ||||
| 		m = new DoubleModel(component, "AftShoulderThickness", UnitGroup.UNITS_LENGTH, 0); | ||||
| 		m2 = new DoubleModel(component, "AftShoulderRadius", UnitGroup.UNITS_LENGTH); | ||||
| 		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)); | ||||
| @ -746,7 +766,7 @@ public class RocketComponentConfig extends JPanel { | ||||
| 
 | ||||
| 
 | ||||
| 		////  Capped | ||||
| 		bm = new BooleanModel(component, "AftShoulderCapped"); | ||||
| 		bm = new BooleanModel(component, valueNameShoulder+"Capped"); | ||||
| 		check = new JCheckBox(bm); | ||||
| 		//// End capped | ||||
| 		check.setText(trans.get("RocketCompCfg.checkbox.Endcapped")); | ||||
| @ -754,16 +774,9 @@ public class RocketComponentConfig extends JPanel { | ||||
| 		sub.add(check, "spanx"); | ||||
| 		order.add(check); | ||||
| 
 | ||||
| 		 | ||||
| 		panel.add(sub); | ||||
| 		 | ||||
| 		 | ||||
| 		return panel; | ||||
| 	} | ||||
| 
 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	/* | ||||
| 	 * Private inner class to handle events in componentNameField. | ||||
| 	 */ | ||||
|  | ||||
| @ -228,18 +228,16 @@ public class TransitionConfig extends RocketComponentConfig { | ||||
| 	private void updateCheckboxAutoAftRadius() { | ||||
| 		if (component == null || checkAutoAftRadius == null) return; | ||||
| 
 | ||||
| 		// Disable check button if there is no component to get the diameter from | ||||
| 		SymmetricComponent nextComp = ((Transition) component).getNextSymmetricComponent(); | ||||
| 		if (nextComp == null) { | ||||
| 		Transition transition = (Transition) component; | ||||
| 		boolean enabled = transition.canUseNextCompAutomatic(); | ||||
| 		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); | ||||
| 			((Transition) component).setAftRadiusAutomatic(false); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (!nextComp.usesPreviousCompAutomatic()) { | ||||
| 			checkAutoAftRadius.setEnabled(true); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); | ||||
| 		} else { | ||||
| 		} else {															// Next component already has its auto radius checked | ||||
| 			checkAutoAftRadius.setEnabled(false); | ||||
| 			((Transition) component).setAftRadiusAutomatic(false); | ||||
| 			checkAutoAftRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||
| @ -254,18 +252,16 @@ public class TransitionConfig extends RocketComponentConfig { | ||||
| 	private void updateCheckboxAutoForeRadius() { | ||||
| 		if (component == null || checkAutoForeRadius == null) return; | ||||
| 
 | ||||
| 		// Disable check button if there is no component to get the diameter from | ||||
| 		SymmetricComponent prevComp = ((Transition) component).getPreviousSymmetricComponent(); | ||||
| 		if (prevComp == null) { | ||||
| 		Transition transition = (Transition) component; | ||||
| 		boolean enabled = transition.canUsePreviousCompAutomatic(); | ||||
| 		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); | ||||
| 			((Transition) component).setForeRadiusAutomatic(false); | ||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_noReferenceComponent")); | ||||
| 			return; | ||||
| 		} | ||||
| 		if (!prevComp.usesNextCompAutomatic()) { | ||||
| 			checkAutoForeRadius.setEnabled(true); | ||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic")); | ||||
| 		} else { | ||||
| 		} else {															// Next component already has its auto radius checked | ||||
| 			checkAutoForeRadius.setEnabled(false); | ||||
| 			((Transition) component).setForeRadiusAutomatic(false); | ||||
| 			checkAutoForeRadius.setToolTipText(trans.get("TransitionCfg.checkbox.ttip.Automatic_alreadyAuto")); | ||||
|  | ||||
| @ -83,7 +83,7 @@ public class ScaleDialog extends JDialog { | ||||
| 		// SymmetricComponent | ||||
| 		addScaler(SymmetricComponent.class, "Thickness", "isFilled", SCALERS_NO_OFFSET); | ||||
| 		 | ||||
| 		// Transition + Nose cone | ||||
| 		// Transition | ||||
| 		addScaler(Transition.class, "ForeRadius", "isForeRadiusAutomatic", SCALERS_NO_OFFSET); | ||||
| 		addScaler(Transition.class, "AftRadius", "isAftRadiusAutomatic", SCALERS_NO_OFFSET); | ||||
| 		addScaler(Transition.class, "ForeShoulderRadius", SCALERS_NO_OFFSET); | ||||
| @ -93,6 +93,12 @@ public class ScaleDialog extends JDialog { | ||||
| 		addScaler(Transition.class, "AftShoulderThickness", 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 | ||||
| 		addScaler(BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic", SCALERS_NO_OFFSET); | ||||
| 		addScaler(BodyTube.class, "MotorOverhang", 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) | ||||
| 		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); | ||||
| 			if (list != null && list.size() > 0) { | ||||
| 				for (Scaler s : list) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user