[#1567] Implement rail button screw
This commit is contained in:
		
							parent
							
								
									8c150299bc
								
							
						
					
					
						commit
						58bd18dc84
					
				| @ -1077,9 +1077,10 @@ LaunchLugCfg.tab.Generalprop = General properties | ||||
| ! RailButtonConfig | ||||
| RailBtnCfg.lbl.OuterDiam = Outer Diameter: | ||||
| RailBtnCfg.lbl.InnerDiam = Inner Diameter: | ||||
| RailBtnCfg.lbl.TotalHeight = Total Height: | ||||
| RailBtnCfg.lbl.TotalHeight = Total Height (excl. screw): | ||||
| RailBtnCfg.lbl.BaseHeight = Base Height: | ||||
| RailBtnCfg.lbl.FlangeHeight = Flange Height: | ||||
| RailBtnCfg.lbl.ScrewHeight = Screw Height: | ||||
| RailBtnCfg.lbl.Angle = Rotation: | ||||
| RailBtnCfg.lbl.PosRelativeTo = Position relative to: | ||||
| RailBtnCfg.lbl.Plus = plus | ||||
|  | ||||
| @ -203,6 +203,8 @@ class DocumentConfig { | ||||
| 				Reflection.findMethod( RailButton.class, "setBaseHeight", double.class))); | ||||
| 		setters.put("RailButton:flangeheight",  new DoubleSetter( | ||||
| 				Reflection.findMethod( RailButton.class, "setFlangeHeight", double.class))); | ||||
| 		setters.put("RailButton:screwheight",  new DoubleSetter( | ||||
| 				Reflection.findMethod( RailButton.class, "setScrewHeight", double.class))); | ||||
| 		setters.put("RailButton:outerdiameter",  new DoubleSetter( | ||||
| 				Reflection.findMethod( RailButton.class, "setOuterDiameter", double.class))); | ||||
| 		setters.put("RailButton:innerdiameter",  new DoubleSetter( | ||||
|  | ||||
| @ -30,6 +30,7 @@ public class RailButtonSaver extends ExternalComponentSaver { | ||||
| 		emitDouble( elements, "height", rb.getTotalHeight()); | ||||
| 		emitDouble( elements, "baseheight", rb.getBaseHeight()); | ||||
| 		emitDouble( elements, "flangeheight", rb.getFlangeHeight()); | ||||
| 		emitDouble( elements, "screwheight", rb.getScrewHeight()); | ||||
| 	} | ||||
| 
 | ||||
| 	 | ||||
|  | ||||
| @ -131,7 +131,7 @@ public class ComponentPreset implements Comparable<ComponentPreset>, Serializabl | ||||
| 				ComponentPreset.DESCRIPTION, | ||||
| 				ComponentPreset.BASE_HEIGHT, | ||||
| 				ComponentPreset.FLANGE_HEIGHT, | ||||
| 				//ComponentPreset.SCREW_HEIGHT,		// Add this later when we implement screws in the rail button | ||||
| 				ComponentPreset.SCREW_HEIGHT, | ||||
| 				ComponentPreset.HEIGHT, | ||||
| 				ComponentPreset.INNER_DIAMETER, | ||||
| 				ComponentPreset.OUTER_DIAMETER, | ||||
|  | ||||
| @ -46,7 +46,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 	protected double totalHeight_m; | ||||
| 	protected double flangeHeight_m; | ||||
|  	protected double baseHeight_m; | ||||
| 	protected double screwHeight_m;		// This has no effect at the moment; is for future use. | ||||
| 	protected double screwHeight_m; | ||||
| 
 | ||||
| 
 | ||||
| 	private double radialDistance_m=0; | ||||
| @ -208,19 +208,6 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||
| 	} | ||||
| 
 | ||||
| 	public void setTotalHeight(double newHeight ) { | ||||
| 		for (RocketComponent listener : configListeners) { | ||||
| 			if (listener instanceof RailButton) { | ||||
| 				((RailButton) listener).setTotalHeight(newHeight); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		this.totalHeight_m = Math.max(newHeight, this.flangeHeight_m + this.baseHeight_m); | ||||
| 
 | ||||
| 		clearPreset(); | ||||
| 		fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public boolean isAerodynamic(){ | ||||
| 		// TODO: implement aerodynamics | ||||
| @ -270,8 +257,8 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 	public BoundingBox getInstanceBoundingBox(){ | ||||
| 		BoundingBox instanceBounds = new BoundingBox(); | ||||
| 		 | ||||
| 		instanceBounds.update(new Coordinate(0, this.totalHeight_m, 0)); | ||||
| 		instanceBounds.update(new Coordinate(0, -this.totalHeight_m, 0)); | ||||
| 		instanceBounds.update(new Coordinate(0, this.totalHeight_m + this.screwHeight_m, 0)); | ||||
| 		instanceBounds.update(new Coordinate(0, -this.totalHeight_m - this.screwHeight_m, 0)); | ||||
| 		 | ||||
| 		final double r = this.getOuterDiameter() / 2; | ||||
| 		instanceBounds.update(new Coordinate(r, 0, r)); | ||||
| @ -322,9 +309,8 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 		final double volOuter = Math.PI*Math.pow( outerDiameter_m/2, 2)*flangeHeight_m; | ||||
| 		final double volInner = Math.PI*Math.pow( innerDiameter_m/2, 2)*getInnerHeight(); | ||||
| 		final double volStandoff = Math.PI*Math.pow( outerDiameter_m/2, 2)* baseHeight_m; | ||||
| 		return (volOuter+ | ||||
| 				volInner+ | ||||
| 				volStandoff); | ||||
| 		final double volScrew = 2f/3 * Math.PI * MathUtil.pow2(outerDiameter_m/2) * screwHeight_m; | ||||
| 		return volOuter + volInner + volStandoff + volScrew; | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| @ -386,13 +372,18 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 	@Override | ||||
| 	public Coordinate getComponentCG() { | ||||
| 		// Math.PI and density are assumed constant through calculation, and thus may be factored out. | ||||
| 		final double volumeBase = Math.pow(outerDiameter_m / 2, 2) * this.baseHeight_m; | ||||
| 		final double volumeFlange = Math.pow(outerDiameter_m / 2, 2)* this.flangeHeight_m; | ||||
| 		final double volumeInner = Math.pow(innerDiameter_m / 2, 2)* getInnerHeight(); | ||||
| 		final double totalVolume = volumeFlange + volumeInner + volumeBase; | ||||
| 		final double heightCM = (volumeFlange*( this.totalHeight_m-getFlangeHeight()/2) + volumeInner*( this.baseHeight_m + this.getInnerHeight()/2) + volumeBase*(this.baseHeight_m /2))/totalVolume; | ||||
| 		final double massBase = Math.pow(outerDiameter_m / 2, 2) * this.baseHeight_m; | ||||
| 		final double massInner = Math.pow(innerDiameter_m / 2, 2)* getInnerHeight(); | ||||
| 		final double massFlange = Math.pow(outerDiameter_m / 2, 2)* this.flangeHeight_m; | ||||
| 		final double massScrew = 2f/3 * MathUtil.pow2(outerDiameter_m/2) * screwHeight_m; | ||||
| 		final double totalMass = massFlange + massInner + massBase + massScrew; | ||||
| 		final double baseCM = this.baseHeight_m /2; | ||||
| 		final double innerCM = this.baseHeight_m + this.getInnerHeight()/2; | ||||
| 		final double flangeCM = this.totalHeight_m - getFlangeHeight()/2; | ||||
| 		final double screwCM = this.totalHeight_m + 4 * this.screwHeight_m / (3 * Math.PI); | ||||
| 		final double heightCM = (massBase*baseCM + massInner*innerCM + massFlange*flangeCM + massScrew*screwCM)/totalMass; | ||||
| 
 | ||||
| 		if( heightCM > this.totalHeight_m ){ | ||||
| 		if (heightCM > this.totalHeight_m + this.screwHeight_m) { | ||||
| 			throw new BugException(" bug found while computing the CG of a RailButton: "+this.getName()+"\n height of CG: "+heightCM); | ||||
| 		} | ||||
| 		 | ||||
| @ -450,6 +441,9 @@ public class RailButton extends ExternalComponent implements AnglePositionable, | ||||
| 		if (preset.has(ComponentPreset.BASE_HEIGHT)) { | ||||
| 			this.baseHeight_m = preset.get(ComponentPreset.BASE_HEIGHT); | ||||
| 		} | ||||
| 		if (preset.has(ComponentPreset.SCREW_HEIGHT)) { | ||||
| 			this.screwHeight_m = preset.get(ComponentPreset.SCREW_HEIGHT); | ||||
| 		} | ||||
| 		if (preset.has(ComponentPreset.CD) && preset.get(ComponentPreset.CD) > 0) { | ||||
| 			setCDOverridden(true); | ||||
| 			setOverrideCD(preset.get(ComponentPreset.CD)); | ||||
|  | ||||
| @ -173,7 +173,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab | ||||
| 	 | ||||
| 	////////////  Methods that must be implemented  //////////// | ||||
| 	 | ||||
| 	 | ||||
| 	  | ||||
| 	/** | ||||
| 	 * Static component name.  The name may not vary of the parameters, it must be static. | ||||
| 	 */ | ||||
|  | ||||
| @ -100,6 +100,16 @@ public class RailButtonConfig extends RocketComponentConfig { | ||||
| 			panel.add(new BasicSlider(heightModel.getSliderModel(new DoubleModel(component, "MinTotalHeight", UnitGroup.UNITS_LENGTH), 0.02)), | ||||
| 					"w 100lp, wrap para"); | ||||
| 		} | ||||
| 		{ //// Screw height | ||||
| 			panel.add(new JLabel(trans.get("RailBtnCfg.lbl.ScrewHeight"))); | ||||
| 			DoubleModel heightModel = new DoubleModel(component, "ScrewHeight", UnitGroup.UNITS_LENGTH, 0); | ||||
| 			JSpinner heightSpinner = new JSpinner(heightModel.getSpinnerModel()); | ||||
| 			heightSpinner.setEditor(new SpinnerEditor(heightSpinner)); | ||||
| 			panel.add(heightSpinner, "growx"); | ||||
| 			order.add(((SpinnerEditor) heightSpinner.getEditor()).getTextField()); | ||||
| 			panel.add(new UnitSelector(heightModel), "growx"); | ||||
| 			panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.02)), "w 100lp, wrap para"); | ||||
| 		} | ||||
| 
 | ||||
| 		{ //// Angular Position: | ||||
| 			panel.add(new JLabel(trans.get("RailBtnCfg.lbl.Angle"))); | ||||
|  | ||||
| @ -29,6 +29,8 @@ import net.sf.openrocket.rocketcomponent.TubeFinSet; | ||||
| import net.sf.openrocket.util.Coordinate; | ||||
| import net.sf.openrocket.util.Transformation; | ||||
| 
 | ||||
| import static com.jogamp.opengl.GL2ES3.GL_QUADS; | ||||
| 
 | ||||
| /* | ||||
|  * @author Bill Kuker <bkuker@billkuker.com> | ||||
|  * @author Daniel Williams <equipoise@gmail.com> | ||||
| @ -314,19 +316,32 @@ public class ComponentRenderer { | ||||
| 			glu.gluCylinder(q, ir, ir, r.getInnerHeight(), LOD, 1); | ||||
| 			 | ||||
| 			// Flange Cylinder | ||||
| 			gl.glTranslated(0, 0, r.getInnerHeight()); | ||||
| 			if (r.getFlangeHeight() > 0) { | ||||
| 				gl.glTranslated(0, 0, r.getInnerHeight()); | ||||
| 				glu.gluCylinder(q, or, or, r.getFlangeHeight(), LOD, 1); | ||||
| 				glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); | ||||
| 				glu.gluDisk(q, 0, or, LOD, 2); | ||||
| 				glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); | ||||
| 				gl.glTranslated(0, 0, r.getFlangeHeight()); | ||||
| 				glu.gluDisk(q, 0, or, LOD, 2); | ||||
| 			} else {	// Draw a closing cap if there is no flange | ||||
| 				gl.glTranslated(0, 0, r.getInnerHeight()); | ||||
| 			} else if (r.getScrewHeight() == 0) {	// Draw a closing cap if there is no flange | ||||
| 				glu.gluDisk(q, 0, ir, LOD, 2); | ||||
| 			} | ||||
| 
 | ||||
| 			// Screw | ||||
| 			if (r.getScrewHeight() > 0) { | ||||
| 				// Half dome | ||||
| 				gl.glClipPlane(GL2.GL_CLIP_PLANE0, new double[] { 0, 0, 1, 0 }, 0); | ||||
| 				gl.glEnable(GL2.GL_CLIP_PLANE0); | ||||
| 				gl.glScaled(1, 1, r.getScrewHeight() / (r.getOuterDiameter() / 2)); | ||||
| 				glu.gluSphere(q, r.getOuterDiameter() / 2.0, LOD, LOD); | ||||
| 				gl.glDisable(GL2.GL_CLIP_PLANE0); | ||||
| 
 | ||||
| 				// Closing disk | ||||
| 				glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); | ||||
| 				glu.gluDisk(q, ir, or, LOD, 2); | ||||
| 			} | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| package net.sf.openrocket.gui.rocketfigure; | ||||
| 
 | ||||
| import java.awt.Shape; | ||||
| import java.awt.geom.AffineTransform; | ||||
| import java.awt.geom.Arc2D; | ||||
| import java.awt.geom.Ellipse2D; | ||||
| import java.awt.geom.Line2D; | ||||
| import java.awt.geom.Path2D; | ||||
| @ -32,6 +34,7 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 		final double baseHeight = btn.getBaseHeight(); | ||||
| 		final double innerHeight = btn.getInnerHeight(); | ||||
| 		final double flangeHeight = btn.getFlangeHeight(); | ||||
| 		final double screwHeight = btn.getScrewHeight(); | ||||
| 		 | ||||
| 		final double outerDiameter = btn.getOuterDiameter(); | ||||
| 		final double outerRadius = outerDiameter/2; | ||||
| @ -50,9 +53,16 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 		final double baseHeightcos = baseHeight*cosr; | ||||
| 		final double innerHeightcos = innerHeight*cosr; | ||||
| 		final double flangeHeightcos = flangeHeight*cosr; | ||||
| 		double screwHeightcos = screwHeight*cosr; | ||||
| 
 | ||||
| 		// Don't draw the screw if it will overlap with the ellipse of the flange | ||||
| 		if (Math.abs(screwHeightcos) <= outerDiameter * Math.abs(sinr) / 2) { | ||||
| 			screwHeightcos = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		Path2D.Double path = new Path2D.Double(); | ||||
| 		Path2D.Double pathInvis = new Path2D.Double();	// Path for the invisible triangles | ||||
| 		Path2D.Double pathInvis = new Path2D.Double();	// Invisible paths to be used for selection of the rail button | ||||
| 		Arc2D.Double screwShape = null; | ||||
| 		{// base cylinder | ||||
| 			if (baseHeight > 0) { | ||||
| 				final double drawWidth = outerDiameter; | ||||
| @ -99,7 +109,7 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 			pathInvis.append(new Rectangle2D.Double(center.x - innerRadius, y_invis, drawWidth, Math.abs(innerHeightcos)), false); | ||||
| 		} | ||||
| 		{// flange cylinder | ||||
| 			if (flangeHeight > 0) { | ||||
| 			if (flangeHeight > 0 || screwHeight > 0) {		// Also draw when there is a screw (= the bottom of the screw) | ||||
| 				final double drawWidth = outerDiameter; | ||||
| 				final double drawHeight = outerDiameter * sinr; | ||||
| 				final Point2D.Double center = new Point2D.Double(loc.x, loc.y + baseHeightcos + innerHeightcos); | ||||
| @ -121,12 +131,22 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 				pathInvis.append(new Rectangle2D.Double(center.x - outerRadius, y_invis, drawWidth, Math.abs(flangeHeightcos)), false); | ||||
| 			} | ||||
| 		} | ||||
| 		{// screw | ||||
| 			if (screwHeight > 0) { | ||||
| 				final double drawWidth = outerDiameter; | ||||
| 				final double drawHeight = Math.abs(screwHeightcos) * 2;        // Times 2 because it is the full ellipse height | ||||
| 				final Point2D.Double origin = new Point2D.Double(loc.x - outerRadius, loc.y + baseHeightcos + innerHeightcos + flangeHeightcos - Math.abs(screwHeightcos)); | ||||
| 				screwShape = new Arc2D.Double(origin.x, origin.y, drawWidth, drawHeight, 0, -Math.signum(screwHeightcos) * 180, Arc2D.OPEN); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		RocketComponentShape[] shapes = RocketComponentShape.toArray(new Shape[]{ path }, component); | ||||
| 		Shape[] temp = screwShape == null ? new Shape[]{ path } : new Shape[]{ path, screwShape }; | ||||
| 		RocketComponentShape[] shapes = RocketComponentShape.toArray(temp, component); | ||||
| 		RocketComponentShape[] shapesInvis = RocketComponentShape.toArray(new Shape[]{ pathInvis }, component); | ||||
| 
 | ||||
| 		for (RocketComponentShape s : shapesInvis) | ||||
| 		for (RocketComponentShape s : shapesInvis) { | ||||
| 			s.setColor(Color.INVISIBLE); | ||||
| 		} | ||||
| 
 | ||||
| 		RocketComponentShape[] total = Arrays.copyOf(shapes, shapes.length + shapesInvis.length); | ||||
| 		System.arraycopy(shapesInvis, 0, total, shapes.length, shapesInvis.length); | ||||
| @ -140,6 +160,7 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 		final double baseHeight = btn.getBaseHeight(); | ||||
| 		final double innerHeight = btn.getInnerHeight(); | ||||
| 		final double flangeHeight = btn.getFlangeHeight(); | ||||
| 		final double screwHeight = btn.getScrewHeight(); | ||||
| 
 | ||||
| 		final double outerDiameter = btn.getOuterDiameter(); | ||||
| 		final double outerRadius = outerDiameter/2; | ||||
| @ -158,6 +179,7 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 		final double cosr = Math.cos(combined_angle_rad); | ||||
| 		 | ||||
| 		Path2D.Double path = new Path2D.Double(); | ||||
| 		Shape screwShape = null; | ||||
| 
 | ||||
| 		// base | ||||
| 		if (baseHeight > 0) { | ||||
| @ -171,15 +193,27 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 			path.append( getRotatedRectangle( loc.z+delta_z, loc.y+delta_y, innerRadius, innerHeight, combined_angle_rad), false); | ||||
| 		} | ||||
| 		{// flange | ||||
| 			if (flangeHeight > 0) { | ||||
| 			if (flangeHeight > 0 || screwHeight > 0) {		// Also draw when there is a screw (= the bottom of the screw) | ||||
| 				final double delta_r = baseHeight + innerHeight; | ||||
| 				final double delta_y = delta_r * cosr; | ||||
| 				final double delta_z = delta_r * sinr; | ||||
| 				path.append(getRotatedRectangle(loc.z + delta_z, loc.y + delta_y, outerRadius, flangeHeight, combined_angle_rad), false); | ||||
| 			} | ||||
| 		} | ||||
| 		{// screw | ||||
| 			if (screwHeight > 0) { | ||||
| 				final double drawWidth = outerDiameter; | ||||
| 				final double drawHeight = 2*screwHeight;	// Times 2 because it is the full ellipse height | ||||
| 				final double delta_y = baseHeight + innerHeight + flangeHeight - screwHeight; | ||||
| 				final double delta_z = -outerRadius; | ||||
| 				final Point2D.Double origin = new Point2D.Double(loc.z + delta_z, loc.y + delta_y); | ||||
| 				screwShape = new Arc2D.Double(origin.x, origin.y, drawWidth, drawHeight, 0, -180, Arc2D.OPEN); | ||||
| 				screwShape = getRotatedShape(screwShape, loc.z, loc.y, -combined_angle_rad); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return RocketComponentShape.toArray( new Shape[]{ path }, component); | ||||
| 		Shape[] temp = screwShape == null ? new Shape[]{ path } : new Shape[]{ path, screwShape }; | ||||
| 		return RocketComponentShape.toArray(temp, component); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| @ -197,4 +231,24 @@ public class RailButtonShapes extends RocketComponentShape { | ||||
| 		 | ||||
| 		return rect; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Rotates a shape about the specified coordinates. | ||||
| 	 * | ||||
| 	 * @param base  the shape (<code>null</code> permitted, returns <code>null</code>). | ||||
| 	 * @param angle  the angle (in radians). | ||||
| 	 * @param x  the x coordinate for the rotation point (in Java2D space). | ||||
| 	 * @param y  the y coordinate for the rotation point (in Java2D space). | ||||
| 	 * | ||||
| 	 * @return the rotated shape. | ||||
| 	 * | ||||
| 	 * @author David Gilbert | ||||
| 	 */ | ||||
| 	public static Shape getRotatedShape(final Shape base, final double x, final double y, final double angle) { | ||||
| 		if (base == null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		final AffineTransform rotate = AffineTransform.getRotateInstance(angle, x, y); | ||||
| 		return rotate.createTransformedShape(base); | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user