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.motor.Motor;
8 import net.sf.openrocket.util.Coordinate;
9 import net.sf.openrocket.util.MathUtil;
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 {
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 public double getRadius() {
67 // Return auto radius from front or rear
69 SymmetricComponent c = this.getPreviousSymmetricComponent();
71 r = c.getFrontAutoRadius();
74 c = this.getNextSymmetricComponent();
76 r = c.getRearAutoRadius();
88 * Set the outer radius of the body tube. If the radius is less than the wall thickness,
89 * the wall thickness is decreased accordingly of the value of the radius.
90 * This method sets the automatic radius off.
92 public void setRadius(double radius) {
93 if ((this.radius == radius) && (autoRadius==false))
96 this.autoRadius = false;
97 this.radius = Math.max(radius,0);
99 if (this.thickness > this.radius)
100 this.thickness = this.radius;
101 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
106 * Returns whether the radius is selected automatically or not.
107 * Returns false also in case automatic radius selection is not possible.
109 public boolean isRadiusAutomatic() {
114 * Sets whether the radius is selected automatically or not.
116 public void setRadiusAutomatic(boolean auto) {
117 if (autoRadius == auto)
121 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
126 public double getAftRadius() { return getRadius(); }
128 public double getForeRadius() { return getRadius(); }
130 public boolean isAftRadiusAutomatic() { return isRadiusAutomatic(); }
132 public boolean isForeRadiusAutomatic() { return isRadiusAutomatic(); }
137 protected double getFrontAutoRadius() {
138 if (isRadiusAutomatic()) {
139 // Search for previous SymmetricComponent
140 SymmetricComponent c = this.getPreviousSymmetricComponent();
142 return c.getFrontAutoRadius();
151 protected double getRearAutoRadius() {
152 if (isRadiusAutomatic()) {
153 // Search for next SymmetricComponent
154 SymmetricComponent c = this.getNextSymmetricComponent();
156 return c.getRearAutoRadius();
170 public double getInnerRadius() {
173 return Math.max(getRadius()-thickness, 0);
176 public void setInnerRadius(double r) {
177 setThickness(getRadius()-r);
184 * Return the component name.
187 public String getComponentName() {
192 /************ Component calculations ***********/
194 // From SymmetricComponent
196 * Returns the outer radius at the position x. This returns the same value as getRadius().
199 public double getRadius(double x) {
204 * Returns the inner radius at the position x. If the tube is filled, returns always zero.
207 public double getInnerRadius(double x) {
211 return Math.max(getRadius()-thickness,0);
216 * Returns the body tube's center of gravity.
219 public Coordinate getComponentCG() {
220 return new Coordinate(length/2,0,0,getComponentMass());
224 * Returns the body tube's volume.
227 public double getComponentVolume() {
228 double r = getRadius();
230 return getFilledVolume(r,length);
232 return getFilledVolume(r,length) - getFilledVolume(getInnerRadius(0),length);
237 public double getLongitudalUnitInertia() {
238 // 1/12 * (3 * (r1^2 + r2^2) + h^2)
239 return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getRadius()) +
240 MathUtil.pow2(getLength())) / 12;
244 public double getRotationalUnitInertia() {
245 // 1/2 * (r1^2 + r2^2)
246 return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getRadius()))/2;
253 * Helper function for cylinder volume.
255 private static double getFilledVolume(double r, double l) {
256 return Math.PI * r*r * l;
261 * Adds bounding coordinates to the given set. The body tube will fit within the
262 * convex hull of the points.
264 * Currently the points are simply a rectangular box around the body tube.
267 public Collection<Coordinate> getComponentBounds() {
268 Collection<Coordinate> bounds = new ArrayList<Coordinate>(8);
269 double r = getRadius();
270 addBound(bounds,0,r);
271 addBound(bounds,length,r);
276 //////////////// Motor mount /////////////////
279 public boolean isMotorMount() {
284 public void setMotorMount(boolean mount) {
285 if (motorMount == mount)
288 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
292 public Motor getMotor(String id) {
296 // Check whether the id is valid for the current rocket
297 RocketComponent root = this.getRoot();
298 if (!(root instanceof Rocket))
300 if (!((Rocket) root).isMotorConfigurationID(id))
303 return motors.get(id);
307 public void setMotor(String id, Motor motor) {
310 throw new IllegalArgumentException("Cannot set non-null motor for id null");
313 Motor current = motors.get(id);
314 if ((motor == null && current == null) ||
315 (motor != null && motor.equals(current)))
317 motors.put(id, motor);
318 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
322 public double getMotorDelay(String id) {
323 Double delay = ejectionDelays.get(id);
325 return Motor.PLUGGED;
330 public void setMotorDelay(String id, double delay) {
331 ejectionDelays.put(id, delay);
332 fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
336 public int getMotorCount() {
341 public double getMotorMountDiameter() {
342 return getInnerRadius()*2;
346 public IgnitionEvent getIgnitionEvent() {
347 return ignitionEvent;
351 public void setIgnitionEvent(IgnitionEvent event) {
352 if (ignitionEvent == event)
354 ignitionEvent = event;
355 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
360 public double getIgnitionDelay() {
361 return ignitionDelay;
365 public void setIgnitionDelay(double delay) {
366 if (MathUtil.equals(delay, ignitionDelay))
368 ignitionDelay = delay;
369 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
374 public double getMotorOverhang() {
379 public void setMotorOverhang(double overhang) {
380 if (MathUtil.equals(this.overhang, overhang))
382 this.overhang = overhang;
383 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
388 public Coordinate getMotorPosition(String id) {
389 Motor motor = motors.get(id);
391 throw new IllegalArgumentException("No motor with id " + id + " defined.");
394 return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
402 * Copy the motor and ejection delay HashMaps.
404 * @see rocketcomponent.RocketComponent#copy()
406 @SuppressWarnings("unchecked")
408 public RocketComponent copy() {
409 RocketComponent c = super.copy();
410 ((BodyTube)c).motors = (HashMap<String,Motor>) motors.clone();
411 ((BodyTube)c).ejectionDelays = (HashMap<String,Double>) ejectionDelays.clone();