1 package net.sf.openrocket.rocketcomponent;
3 import net.sf.openrocket.motor.Motor;
4 import net.sf.openrocket.util.Coordinate;
5 import net.sf.openrocket.util.MathUtil;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.HashMap;
13 * Rocket body tube component. Has only two parameters, a radius and length.
15 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
18 public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial {
20 private double radius=0;
21 private boolean autoRadius = false; // Radius chosen automatically based on parent component
23 // When changing the inner radius, thickness is modified
25 private boolean motorMount = false;
26 private HashMap<String, Double> ejectionDelays = new HashMap<String, Double>();
27 private HashMap<String, Motor> motors = new HashMap<String, Motor>();
28 private IgnitionEvent ignitionEvent = IgnitionEvent.AUTOMATIC;
29 private double ignitionDelay = 0;
30 private double overhang = 0;
36 this.length = 8*DEFAULT_RADIUS;
37 this.radius = DEFAULT_RADIUS;
38 this.autoRadius = true;
41 public BodyTube(double length, double radius) {
43 this.radius = Math.max(radius,0);
44 this.length = Math.max(length,0);
48 public BodyTube(double length, double radius, boolean filled) {
53 public BodyTube(double length, double radius, double thickness) {
56 this.thickness = thickness;
60 /************ Get/set component parameter methods ************/
63 * Return the outer radius of the body tube.
65 * @return the outside radius of the tube
68 public double getOuterRadius () {
70 // Return auto radius from front or rear
72 SymmetricComponent c = this.getPreviousSymmetricComponent();
74 r = c.getFrontAutoRadius();
77 c = this.getNextSymmetricComponent();
79 r = c.getRearAutoRadius();
91 * Set the outer radius of the body tube. If the radius is less than the wall thickness,
92 * the wall thickness is decreased accordingly of the value of the radius.
93 * This method sets the automatic radius off.
95 * @param radius the outside radius in standard units
98 public void setOuterRadius (double radius) {
99 if ((this.radius == radius) && (autoRadius==false))
102 this.autoRadius = false;
103 this.radius = Math.max(radius,0);
105 if (this.thickness > this.radius)
106 this.thickness = this.radius;
107 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
112 * Returns whether the radius is selected automatically or not.
113 * Returns false also in case automatic radius selection is not possible.
115 public boolean isRadiusAutomatic() {
120 * Sets whether the radius is selected automatically or not.
122 public void setRadiusAutomatic(boolean auto) {
123 if (autoRadius == auto)
127 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
132 public double getAftRadius() { return getOuterRadius(); }
134 public double getForeRadius() { return getOuterRadius(); }
136 public boolean isAftRadiusAutomatic() { return isRadiusAutomatic(); }
138 public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
143 protected double getFrontAutoRadius() {
144 if (isRadiusAutomatic()) {
145 // Search for previous SymmetricComponent
146 SymmetricComponent c = this.getPreviousSymmetricComponent();
148 return c.getFrontAutoRadius();
153 return getOuterRadius();
157 protected double getRearAutoRadius() {
158 if (isRadiusAutomatic()) {
159 // Search for next SymmetricComponent
160 SymmetricComponent c = this.getNextSymmetricComponent();
162 return c.getRearAutoRadius();
167 return getOuterRadius();
176 public double getInnerRadius() {
179 return Math.max(getOuterRadius()-thickness, 0);
183 public void setInnerRadius(double r) {
184 setThickness(getOuterRadius()-r);
191 * Return the component name.
194 public String getComponentName() {
199 * Accept a visitor to this BodyTube in the component hierarchy.
201 * @param theVisitor the visitor that will be called back with a reference to this BodyTube
204 public void accept (final ComponentVisitor theVisitor) {
205 theVisitor.visit(this);
209 /************ Component calculations ***********/
211 // From SymmetricComponent
213 * Returns the outer radius at the position x. This returns the same value as getOuterRadius().
216 public double getRadius(double x) {
217 return getOuterRadius();
221 * Returns the inner radius at the position x. If the tube is filled, returns always zero.
224 public double getInnerRadius(double x) {
228 return Math.max(getOuterRadius()-thickness,0);
233 * Returns the body tube's center of gravity.
236 public Coordinate getComponentCG() {
237 return new Coordinate(length/2,0,0,getComponentMass());
241 * Returns the body tube's volume.
244 public double getComponentVolume() {
245 double r = getOuterRadius();
247 return getFilledVolume(r,length);
249 return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
254 public double getLongitudalUnitInertia() {
255 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
256 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
257 MathUtil.pow2(getLength())) / 12;
261 public double getRotationalUnitInertia() {
262 // 1/2 * (r1^2 + r2^2)
263 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
270 * Helper function for cylinder volume.
272 private static double getFilledVolume(double r, double l) {
273 return Math.PI * r*r * l;
278 * Adds bounding coordinates to the given set. The body tube will fit within the
279 * convex hull of the points.
281 * Currently the points are simply a rectangular box around the body tube.
284 public Collection<Coordinate> getComponentBounds() {
285 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
286 double r = getOuterRadius();
287 addBound(bounds,0,r);
288 addBound(bounds,length,r);
293 //////////////// Motor mount /////////////////
296 public boolean isMotorMount() {
301 public void setMotorMount(boolean mount) {
302 if (motorMount == mount)
305 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
309 public Motor getMotor(String id) {
313 // Check whether the id is valid for the current rocket
314 RocketComponent root = this.getRoot();
315 if (!(root instanceof Rocket))
317 if (!((Rocket) root).isMotorConfigurationID(id))
320 return motors.get(id);
324 public void setMotor(String id, Motor motor) {
327 throw new IllegalArgumentException("Cannot set non-null motor for id null");
330 Motor current = motors.get(id);
331 if ((motor == null && current == null) ||
332 (motor != null && motor.equals(current)))
334 motors.put(id, motor);
335 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
339 public double getMotorDelay(String id) {
340 Double delay = ejectionDelays.get(id);
342 return Motor.PLUGGED;
347 public void setMotorDelay(String id, double delay) {
348 ejectionDelays.put(id, delay);
349 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
353 public int getMotorCount() {
358 public double getMotorMountDiameter() {
359 return getInnerRadius()*2;
363 public IgnitionEvent getIgnitionEvent() {
364 return ignitionEvent;
368 public void setIgnitionEvent(IgnitionEvent event) {
369 if (ignitionEvent == event)
371 ignitionEvent = event;
372 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
377 public double getIgnitionDelay() {
378 return ignitionDelay;
382 public void setIgnitionDelay(double delay) {
383 if (MathUtil.equals(delay, ignitionDelay))
385 ignitionDelay = delay;
386 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
391 public double getMotorOverhang() {
396 public void setMotorOverhang(double overhang) {
397 if (MathUtil.equals(this.overhang, overhang))
399 this.overhang = overhang;
400 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
405 public Coordinate getMotorPosition(String id) {
406 Motor motor = motors.get(id);
408 throw new IllegalArgumentException("No motor with id " + id + " defined.");
411 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
419 * Copy the motor and ejection delay HashMaps.
421 * @see rocketcomponent.RocketComponent#copy()
423 @SuppressWarnings("unchecked")
425 public RocketComponent copy() {
426 RocketComponent c = super.copy();
427 ((BodyTube)c).motors = (HashMap<String,Motor>) motors.clone();
428 ((BodyTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();