[#1567] Implement rail button screw

This commit is contained in:
SiboVG 2022-12-11 13:54:47 +01:00
parent 8c150299bc
commit 58bd18dc84
9 changed files with 114 additions and 37 deletions

View File

@ -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

View File

@ -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(

View File

@ -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());
}

View File

@ -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,

View File

@ -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));

View File

@ -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.
*/

View File

@ -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")));

View File

@ -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);
}
}
}

View File

@ -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);
}
}