[#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