1 package net.sf.openrocket.rocketcomponent;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
7 import net.sf.openrocket.util.Coordinate;
8 import net.sf.openrocket.util.MathUtil;
12 * Rocket body tube component. Has only two parameters, a radius and length.
14 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
17 public class BodyTube extends SymmetricComponent implements MotorMount {
19 private double radius=0;
20 private boolean autoRadius = false; // Radius chosen automatically based on parent component
22 // When changing the inner radius, thickness is modified
24 private boolean motorMount = false;
25 private HashMap<String, Double> ejectionDelays = new HashMap<String, Double>();
26 private HashMap<String, Motor> motors = new HashMap<String, Motor>();
27 private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC;
28 private double ignitionDelay = 0;
29 private double overhang = 0;
35 this.length = 8*DEFAULT_RADIUS;
36 this.radius = DEFAULT_RADIUS;
37 this.autoRadius = true;
40 public BodyTube(double length, double radius) {
42 this.radius = Math.max(radius,0);
43 this.length = Math.max(length,0);
47 public BodyTube(double length, double radius, boolean filled) {
52 public BodyTube(double length, double radius, double thickness) {
55 this.thickness = thickness;
59 /************ Get/set component parameter methods ************/
62 * Return the outer radius of the body tube.
64 public double getRadius() {
66 // Return auto radius from front or rear
68 SymmetricComponent c = this.getPreviousSymmetricComponent();
70 r = c.getFrontAutoRadius();
73 c = this.getNextSymmetricComponent();
75 r = c.getRearAutoRadius();
87 * Set the outer radius of the body tube. If the radius is less than the wall thickness,
88 * the wall thickness is decreased accordingly of the value of the radius.
89 * This method sets the automatic radius off.
91 public void setRadius(double radius) {
92 if ((this.radius == radius) && (autoRadius==false))
95 this.autoRadius = false;
96 this.radius = Math.max(radius,0);
98 if (this.thickness > this.radius)
99 this.thickness = this.radius;
100 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
105 * Returns whether the radius is selected automatically or not.
106 * Returns false also in case automatic radius selection is not possible.
108 public boolean isRadiusAutomatic() {
113 * Sets whether the radius is selected automatically or not.
115 public void setRadiusAutomatic(boolean auto) {
116 if (autoRadius == auto)
120 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
125 public double getAftRadius() { return getRadius(); }
127 public double getForeRadius() { return getRadius(); }
129 public boolean isAftRadiusAutomatic() { return isRadiusAutomatic(); }
131 public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
136 protected double getFrontAutoRadius() {
137 if (isRadiusAutomatic()) {
138 // Search for previous SymmetricComponent
139 SymmetricComponent c = this.getPreviousSymmetricComponent();
141 return c.getFrontAutoRadius();
150 protected double getRearAutoRadius() {
151 if (isRadiusAutomatic()) {
152 // Search for next SymmetricComponent
153 SymmetricComponent c = this.getNextSymmetricComponent();
155 return c.getRearAutoRadius();
169 public double getInnerRadius() {
172 return Math.max(getRadius()-thickness, 0);
175 public void setInnerRadius(double r) {
176 setThickness(getRadius()-r);
183 * Return the component name.
186 public String getComponentName() {
191 /************ Component calculations ***********/
193 // From SymmetricComponent
195 * Returns the outer radius at the position x. This returns the same value as getRadius().
198 public double getRadius(double x) {
203 * Returns the inner radius at the position x. If the tube is filled, returns always zero.
206 public double getInnerRadius(double x) {
210 return Math.max(getRadius()-thickness,0);
215 * Returns the body tube's center of gravity.
218 public Coordinate getComponentCG() {
219 return new Coordinate(length/2,0,0,getComponentMass());
223 * Returns the body tube's volume.
226 public double getComponentVolume() {
227 double r = getRadius();
229 return getFilledVolume(r,length);
231 return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
236 public double getLongitudalUnitInertia() {
237 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
238 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) +
239 MathUtil.pow2(getLength())) / 12;
243 public double getRotationalUnitInertia() {
244 // 1/2 * (r1^2 + r2^2)
245 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius()))/2;
252 * Helper function for cylinder volume.
254 private static double getFilledVolume(double r, double l) {
255 return Math.PI * r*r * l;
260 * Adds bounding coordinates to the given set. The body tube will fit within the
261 * convex hull of the points.
263 * Currently the points are simply a rectangular box around the body tube.
266 public Collection<Coordinate> getComponentBounds() {
267 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
268 double r = getRadius();
269 addBound(bounds,0,r);
270 addBound(bounds,length,r);
275 //////////////// Motor mount /////////////////
278 public boolean isMotorMount() {
283 public void setMotorMount(boolean mount) {
284 if (motorMount == mount)
287 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
291 public Motor getMotor(String id) {
292 return motors.get(id);
296 public void setMotor(String id, Motor motor) {
297 Motor current = motors.get(id);
298 if ((motor == null && current == null) ||
299 (motor != null && motor.equals(current)))
301 motors.put(id, motor);
302 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
306 public double getMotorDelay(String id) {
307 Double delay = ejectionDelays.get(id);
309 return Motor.PLUGGED;
314 public void setMotorDelay(String id, double delay) {
315 ejectionDelays.put(id, delay);
316 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
320 public int getMotorCount() {
325 public double getMotorMountDiameter() {
326 return getInnerRadius()*2;
330 public IgnitionEvent getIgnitionEvent() {
331 return ignitionEvent;
335 public void setIgnitionEvent(IgnitionEvent event) {
336 if (ignitionEvent == event)
338 ignitionEvent = event;
339 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
344 public double getIgnitionDelay() {
345 return ignitionDelay;
349 public void setIgnitionDelay(double delay) {
350 if (MathUtil.equals(delay, ignitionDelay))
352 ignitionDelay = delay;
353 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
358 public double getMotorOverhang() {
363 public void setMotorOverhang(double overhang) {
364 if (MathUtil.equals(this.overhang, overhang))
366 this.overhang = overhang;
367 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
375 * Copy the motor and ejection delay HashMaps.
377 * @see rocketcomponent.RocketComponent#copy()
379 @SuppressWarnings("unchecked")
381 public RocketComponent copy() {
382 RocketComponent c = super.copy();
383 ((BodyTube)c).motors = (HashMap<String,Motor>) motors.clone();
384 ((BodyTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();