1 package net.sf.openrocket.gui.figureelements;
3 import net.sf.openrocket.aerodynamics.Warning;
4 import net.sf.openrocket.aerodynamics.WarningSet;
5 import net.sf.openrocket.rocketcomponent.Configuration;
6 import net.sf.openrocket.simulation.FlightData;
7 import net.sf.openrocket.unit.Unit;
8 import net.sf.openrocket.unit.UnitGroup;
9 import net.sf.openrocket.util.MathUtil;
10 import net.sf.openrocket.util.Prefs;
12 import java.awt.Color;
14 import java.awt.Graphics2D;
15 import java.awt.Rectangle;
16 import java.awt.font.GlyphVector;
17 import java.awt.geom.Rectangle2D;
19 import static net.sf.openrocket.util.Chars.ALPHA;
20 import static net.sf.openrocket.util.Chars.THETA;
24 * A <code>FigureElement</code> that draws text at different positions in the figure
25 * with general data about the rocket.
27 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
29 public class RocketInfo implements FigureElement {
31 // Margin around the figure edges, pixels
32 private static final int MARGIN = 8;
35 private static final Font FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 11);
36 private static final Font SMALLFONT = new Font(Font.SANS_SERIF, Font.PLAIN, 9);
39 private final Caret cpCaret = new CPCaret(0,0);
40 private final Caret cgCaret = new CGCaret(0,0);
42 private final Configuration configuration;
43 private final UnitGroup stabilityUnits;
45 private double cg = 0, cp = 0;
46 private double length = 0, diameter = 0;
47 private double mass = 0;
48 private double aoa = Double.NaN, theta = Double.NaN, mach = Prefs.getDefaultMach();
50 private WarningSet warnings = null;
52 private boolean calculatingData = false;
53 private FlightData flightData = null;
55 private Graphics2D g2 = null;
56 private float line = 0;
57 private float x1, x2, y1, y2;
63 public RocketInfo(Configuration configuration) {
64 this.configuration = configuration;
65 this.stabilityUnits = UnitGroup.stabilityUnits(configuration);
70 public void paint(Graphics2D g2, double scale) {
71 throw new UnsupportedOperationException("paint() must be called with coordinates");
75 public void paint(Graphics2D g2, double scale, Rectangle visible) {
77 this.line = FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
78 g2.getFontRenderContext()).getHeight();
80 x1 = visible.x + MARGIN;
81 x2 = visible.x + visible.width - MARGIN;
82 y1 = visible.y + line ;
83 y2 = visible.y + visible.height - MARGIN;
88 drawFlightInformation();
92 public void setCG(double cg) {
96 public void setCP(double cp) {
100 public void setLength(double length) {
101 this.length = length;
104 public void setDiameter(double diameter) {
105 this.diameter = diameter;
108 public void setMass(double mass) {
112 public void setWarnings(WarningSet warnings) {
113 this.warnings = warnings.clone();
116 public void setAOA(double aoa) {
120 public void setTheta(double theta) {
124 public void setMach(double mach) {
129 public void setFlightData(FlightData data) {
130 this.flightData = data;
133 public void setCalculatingData(boolean calc) {
134 this.calculatingData = calc;
140 private void drawMainInfo() {
141 GlyphVector name = createText(configuration.getRocket().getName());
142 GlyphVector lengthLine = createText(
143 "Length " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(length) +
145 UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter));
148 if (configuration.hasMotors())
149 massText = "Mass with motors ";
151 massText = "Mass with no motors ";
153 massText += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(mass);
155 GlyphVector massLine = createText(massText);
158 g2.setColor(Color.BLACK);
160 g2.drawGlyphVector(name, x1, y1);
161 g2.drawGlyphVector(lengthLine, x1, y1+line);
162 g2.drawGlyphVector(massLine, x1, y1+2*line);
167 private void drawStabilityInfo() {
170 at = "at M="+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(mach);
171 if (!Double.isNaN(aoa)) {
172 at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
174 if (!Double.isNaN(theta)) {
175 at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
178 GlyphVector cgValue = createText(
180 GlyphVector cpValue = createText(
182 GlyphVector stabValue = createText(
185 GlyphVector cgText = createText("CG: ");
186 GlyphVector cpText = createText("CP: ");
187 GlyphVector stabText = createText("Stability: ");
188 GlyphVector atText = createSmallText(at);
190 Rectangle2D cgRect = cgValue.getVisualBounds();
191 Rectangle2D cpRect = cpValue.getVisualBounds();
192 Rectangle2D cgTextRect = cgText.getVisualBounds();
193 Rectangle2D cpTextRect = cpText.getVisualBounds();
194 Rectangle2D stabRect = stabValue.getVisualBounds();
195 Rectangle2D stabTextRect = stabText.getVisualBounds();
196 Rectangle2D atTextRect = atText.getVisualBounds();
198 double unitWidth = MathUtil.max(cpRect.getWidth(), cgRect.getWidth(),
199 stabRect.getWidth());
200 double textWidth = Math.max(cpTextRect.getWidth(), cgTextRect.getWidth());
203 g2.setColor(Color.BLACK);
205 g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1);
206 g2.drawGlyphVector(cgValue, (float)(x2-cgRect.getWidth()), y1+line);
207 g2.drawGlyphVector(cpValue, (float)(x2-cpRect.getWidth()), y1+2*line);
209 g2.drawGlyphVector(stabText, (float)(x2-unitWidth-stabTextRect.getWidth()), y1);
210 g2.drawGlyphVector(cgText, (float)(x2-unitWidth-cgTextRect.getWidth()), y1+line);
211 g2.drawGlyphVector(cpText, (float)(x2-unitWidth-cpTextRect.getWidth()), y1+2*line);
213 cgCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+line-0.3*line);
214 cgCaret.paint(g2, 1.7);
216 cpCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+2*line-0.3*line);
217 cpCaret.paint(g2, 1.7);
220 if (unitWidth + textWidth + 10 > atTextRect.getWidth()) {
221 atPos = (float)(x2-(unitWidth+textWidth+10+atTextRect.getWidth())/2);
223 atPos = (float)(x2 - atTextRect.getWidth());
226 g2.setColor(Color.GRAY);
227 g2.drawGlyphVector(atText, atPos, y1 + 3*line);
232 * Get the mass, in default mass units.
236 public double getMass() {
241 * Get the mass in specified mass units.
243 * @param u UnitGroup.MASS
247 public String getMass(Unit u) {
248 return u.toStringUnit(mass);
252 * Get the stability, in calibers.
254 * @return the current stability margin
256 public String getStability () {
257 return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg);
261 * Get the center of pressure in default length units.
263 * @return the distance from the tip to the center of pressure, in default length units
265 public String getCp () {
266 return getCp(UnitGroup.UNITS_LENGTH.getDefaultUnit());
270 * Get the center of pressure in default length units.
272 * @param u UnitGroup.LENGTH
274 * @return the distance from the tip to the center of pressure, in default length units
276 public String getCp (Unit u) {
277 return u.toStringUnit(cp);
281 * Get the center of gravity in default length units.
283 * @return the distance from the tip to the center of gravity, in default length units
285 public String getCg () {
286 return getCg(UnitGroup.UNITS_LENGTH.getDefaultUnit());
290 * Get the center of gravity in specified length units.
292 * @param u UnitGroup.LENGTH
293 * @return the distance from the tip to the center of gravity, in specified units
295 public String getCg (Unit u) {
296 return u.toStringUnit(cg);
300 * Get the flight data for the current motor configuration.
302 * @return flight data, or null
304 public FlightData getFlightData () {
308 private void drawWarnings() {
309 if (warnings == null || warnings.isEmpty())
312 GlyphVector[] texts = new GlyphVector[warnings.size()+1];
315 texts[0] = createText("Warning:");
317 for (Warning w: warnings) {
318 texts[i] = createText(w.toString());
322 for (GlyphVector v: texts) {
323 Rectangle2D rect = v.getVisualBounds();
324 if (rect.getWidth() > max)
325 max = rect.getWidth();
329 float y = y2 - line * warnings.size();
330 g2.setColor(new Color(255,0,0,130));
332 for (GlyphVector v: texts) {
333 Rectangle2D rect = v.getVisualBounds();
334 g2.drawGlyphVector(v, (float)(x2 - max/2 - rect.getWidth()/2), y);
340 private void drawFlightInformation() {
341 double height = drawFlightData();
343 if (calculatingData) {
344 GlyphVector calculating = createText("Calculating...");
345 g2.setColor(Color.BLACK);
346 g2.drawGlyphVector(calculating, x1, (float)(y2-height));
351 private double drawFlightData() {
352 if (flightData == null)
357 GlyphVector apogee = createText("Apogee: ");
358 GlyphVector maxVelocity = createText("Max. velocity: ");
359 GlyphVector maxAcceleration = createText("Max. acceleration: ");
361 GlyphVector apogeeValue, velocityValue, accelerationValue;
362 if (!Double.isNaN(flightData.getMaxAltitude())) {
363 apogeeValue = createText(
364 UnitGroup.UNITS_DISTANCE.toStringUnit(flightData.getMaxAltitude()));
366 apogeeValue = createText("N/A");
368 if (!Double.isNaN(flightData.getMaxVelocity())) {
369 velocityValue = createText(
370 UnitGroup.UNITS_VELOCITY.toStringUnit(flightData.getMaxVelocity()) +
372 UnitGroup.UNITS_COEFFICIENT.toString(flightData.getMaxMachNumber()) + ")");
374 velocityValue = createText("N/A");
376 if (!Double.isNaN(flightData.getMaxAcceleration())) {
377 accelerationValue = createText(
378 UnitGroup.UNITS_ACCELERATION.toStringUnit(flightData.getMaxAcceleration()));
380 accelerationValue = createText("N/A");
384 rect = apogee.getVisualBounds();
385 width = MathUtil.max(width, rect.getWidth());
387 rect = maxVelocity.getVisualBounds();
388 width = MathUtil.max(width, rect.getWidth());
390 rect = maxAcceleration.getVisualBounds();
391 width = MathUtil.max(width, rect.getWidth());
395 if (!calculatingData)
396 g2.setColor(new Color(0,0,127));
398 g2.setColor(new Color(0,0,127,127));
401 g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line));
402 g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line));
403 g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2));
405 g2.drawGlyphVector(apogeeValue, (float)(x1+width), (float)(y2-2*line));
406 g2.drawGlyphVector(velocityValue, (float)(x1+width), (float)(y2-line));
407 g2.drawGlyphVector(accelerationValue, (float)(x1+width), (float)(y2));
414 private GlyphVector createText(String text) {
415 return FONT.createGlyphVector(g2.getFontRenderContext(), text);
418 private GlyphVector createSmallText(String text) {
419 return SMALLFONT.createGlyphVector(g2.getFontRenderContext(), text);