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.l10n.Translator;
6 import net.sf.openrocket.rocketcomponent.Configuration;
7 import net.sf.openrocket.simulation.FlightData;
8 import net.sf.openrocket.startup.Application;
9 import net.sf.openrocket.unit.Unit;
10 import net.sf.openrocket.unit.UnitGroup;
11 import net.sf.openrocket.util.MathUtil;
12 import net.sf.openrocket.util.Prefs;
14 import java.awt.Color;
16 import java.awt.Graphics2D;
17 import java.awt.Rectangle;
18 import java.awt.font.GlyphVector;
19 import java.awt.geom.Rectangle2D;
21 import static net.sf.openrocket.util.Chars.ALPHA;
22 import static net.sf.openrocket.util.Chars.THETA;
26 * A <code>FigureElement</code> that draws text at different positions in the figure
27 * with general data about the rocket.
29 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
31 public class RocketInfo implements FigureElement {
33 private static final Translator trans = Application.getTranslator();
34 // Margin around the figure edges, pixels
35 private static final int MARGIN = 8;
38 private static final Font FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 11);
39 private static final Font SMALLFONT = new Font(Font.SANS_SERIF, Font.PLAIN, 9);
42 private final Caret cpCaret = new CPCaret(0,0);
43 private final Caret cgCaret = new CGCaret(0,0);
45 private final Configuration configuration;
46 private final UnitGroup stabilityUnits;
48 private double cg = 0, cp = 0;
49 private double length = 0, diameter = 0;
50 private double mass = 0;
51 private double aoa = Double.NaN, theta = Double.NaN, mach = Prefs.getDefaultMach();
53 private WarningSet warnings = null;
55 private boolean calculatingData = false;
56 private FlightData flightData = null;
58 private Graphics2D g2 = null;
59 private float line = 0;
60 private float x1, x2, y1, y2;
66 public RocketInfo(Configuration configuration) {
67 this.configuration = configuration;
68 this.stabilityUnits = UnitGroup.stabilityUnits(configuration);
73 public void paint(Graphics2D g2, double scale) {
74 throw new UnsupportedOperationException("paint() must be called with coordinates");
78 public void paint(Graphics2D g2, double scale, Rectangle visible) {
80 this.line = FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
81 g2.getFontRenderContext()).getHeight();
83 x1 = visible.x + MARGIN;
84 x2 = visible.x + visible.width - MARGIN;
85 y1 = visible.y + line ;
86 y2 = visible.y + visible.height - MARGIN;
91 drawFlightInformation();
95 public void setCG(double cg) {
99 public void setCP(double cp) {
103 public void setLength(double length) {
104 this.length = length;
107 public void setDiameter(double diameter) {
108 this.diameter = diameter;
111 public void setMass(double mass) {
115 public void setWarnings(WarningSet warnings) {
116 this.warnings = warnings.clone();
119 public void setAOA(double aoa) {
123 public void setTheta(double theta) {
127 public void setMach(double mach) {
132 public void setFlightData(FlightData data) {
133 this.flightData = data;
136 public void setCalculatingData(boolean calc) {
137 this.calculatingData = calc;
143 private void drawMainInfo() {
144 GlyphVector name = createText(configuration.getRocket().getName());
145 GlyphVector lengthLine = createText(
146 "Length " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(length) +
148 UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter));
151 if (configuration.hasMotors())
152 massText = "Mass with motors ";
154 massText = "Mass with no motors ";
156 massText += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(mass);
158 GlyphVector massLine = createText(massText);
161 g2.setColor(Color.BLACK);
163 g2.drawGlyphVector(name, x1, y1);
164 g2.drawGlyphVector(lengthLine, x1, y1+line);
165 g2.drawGlyphVector(massLine, x1, y1+2*line);
170 private void drawStabilityInfo() {
173 at = "at M="+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(mach);
174 if (!Double.isNaN(aoa)) {
175 at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
177 if (!Double.isNaN(theta)) {
178 at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
181 GlyphVector cgValue = createText(
183 GlyphVector cpValue = createText(
185 GlyphVector stabValue = createText(
188 GlyphVector cgText = createText("CG: ");
189 GlyphVector cpText = createText("CP: ");
190 GlyphVector stabText = createText("Stability: ");
191 GlyphVector atText = createSmallText(at);
193 Rectangle2D cgRect = cgValue.getVisualBounds();
194 Rectangle2D cpRect = cpValue.getVisualBounds();
195 Rectangle2D cgTextRect = cgText.getVisualBounds();
196 Rectangle2D cpTextRect = cpText.getVisualBounds();
197 Rectangle2D stabRect = stabValue.getVisualBounds();
198 Rectangle2D stabTextRect = stabText.getVisualBounds();
199 Rectangle2D atTextRect = atText.getVisualBounds();
201 double unitWidth = MathUtil.max(cpRect.getWidth(), cgRect.getWidth(),
202 stabRect.getWidth());
203 double textWidth = Math.max(cpTextRect.getWidth(), cgTextRect.getWidth());
206 g2.setColor(Color.BLACK);
208 g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1);
209 g2.drawGlyphVector(cgValue, (float)(x2-cgRect.getWidth()), y1+line);
210 g2.drawGlyphVector(cpValue, (float)(x2-cpRect.getWidth()), y1+2*line);
212 g2.drawGlyphVector(stabText, (float)(x2-unitWidth-stabTextRect.getWidth()), y1);
213 g2.drawGlyphVector(cgText, (float)(x2-unitWidth-cgTextRect.getWidth()), y1+line);
214 g2.drawGlyphVector(cpText, (float)(x2-unitWidth-cpTextRect.getWidth()), y1+2*line);
216 cgCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+line-0.3*line);
217 cgCaret.paint(g2, 1.7);
219 cpCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+2*line-0.3*line);
220 cpCaret.paint(g2, 1.7);
223 if (unitWidth + textWidth + 10 > atTextRect.getWidth()) {
224 atPos = (float)(x2-(unitWidth+textWidth+10+atTextRect.getWidth())/2);
226 atPos = (float)(x2 - atTextRect.getWidth());
229 g2.setColor(Color.GRAY);
230 g2.drawGlyphVector(atText, atPos, y1 + 3*line);
235 * Get the mass, in default mass units.
239 public double getMass() {
244 * Get the mass in specified mass units.
246 * @param u UnitGroup.MASS
250 public String getMass(Unit u) {
251 return u.toStringUnit(mass);
255 * Get the stability, in calibers.
257 * @return the current stability margin
259 public String getStability () {
260 return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg);
264 * Get the center of pressure in default length units.
266 * @return the distance from the tip to the center of pressure, in default length units
268 public String getCp () {
269 return getCp(UnitGroup.UNITS_LENGTH.getDefaultUnit());
273 * Get the center of pressure in default length units.
275 * @param u UnitGroup.LENGTH
277 * @return the distance from the tip to the center of pressure, in default length units
279 public String getCp (Unit u) {
280 return u.toStringUnit(cp);
284 * Get the center of gravity in default length units.
286 * @return the distance from the tip to the center of gravity, in default length units
288 public String getCg () {
289 return getCg(UnitGroup.UNITS_LENGTH.getDefaultUnit());
293 * Get the center of gravity in specified length units.
295 * @param u UnitGroup.LENGTH
296 * @return the distance from the tip to the center of gravity, in specified units
298 public String getCg (Unit u) {
299 return u.toStringUnit(cg);
303 * Get the flight data for the current motor configuration.
305 * @return flight data, or null
307 public FlightData getFlightData () {
311 private void drawWarnings() {
312 if (warnings == null || warnings.isEmpty())
315 GlyphVector[] texts = new GlyphVector[warnings.size()+1];
318 texts[0] = createText("Warning:");
320 for (Warning w: warnings) {
321 texts[i] = createText(w.toString());
325 for (GlyphVector v: texts) {
326 Rectangle2D rect = v.getVisualBounds();
327 if (rect.getWidth() > max)
328 max = rect.getWidth();
332 float y = y2 - line * warnings.size();
333 g2.setColor(new Color(255,0,0,130));
335 for (GlyphVector v: texts) {
336 Rectangle2D rect = v.getVisualBounds();
337 g2.drawGlyphVector(v, (float)(x2 - max/2 - rect.getWidth()/2), y);
343 private void drawFlightInformation() {
344 double height = drawFlightData();
346 if (calculatingData) {
347 GlyphVector calculating = createText("Calculating...");
348 g2.setColor(Color.BLACK);
349 g2.drawGlyphVector(calculating, x1, (float)(y2-height));
354 private double drawFlightData() {
355 if (flightData == null)
360 GlyphVector apogee = createText("Apogee: ");
361 GlyphVector maxVelocity = createText("Max. velocity: ");
362 GlyphVector maxAcceleration = createText("Max. acceleration: ");
364 GlyphVector apogeeValue, velocityValue, accelerationValue;
365 if (!Double.isNaN(flightData.getMaxAltitude())) {
366 apogeeValue = createText(
367 UnitGroup.UNITS_DISTANCE.toStringUnit(flightData.getMaxAltitude()));
369 apogeeValue = createText("N/A");
371 if (!Double.isNaN(flightData.getMaxVelocity())) {
372 velocityValue = createText(
373 UnitGroup.UNITS_VELOCITY.toStringUnit(flightData.getMaxVelocity()) +
375 UnitGroup.UNITS_COEFFICIENT.toString(flightData.getMaxMachNumber()) + ")");
377 velocityValue = createText("N/A");
379 if (!Double.isNaN(flightData.getMaxAcceleration())) {
380 accelerationValue = createText(
381 UnitGroup.UNITS_ACCELERATION.toStringUnit(flightData.getMaxAcceleration()));
383 accelerationValue = createText("N/A");
387 rect = apogee.getVisualBounds();
388 width = MathUtil.max(width, rect.getWidth());
390 rect = maxVelocity.getVisualBounds();
391 width = MathUtil.max(width, rect.getWidth());
393 rect = maxAcceleration.getVisualBounds();
394 width = MathUtil.max(width, rect.getWidth());
398 if (!calculatingData)
399 g2.setColor(new Color(0,0,127));
401 g2.setColor(new Color(0,0,127,127));
404 g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line));
405 g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line));
406 g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2));
408 g2.drawGlyphVector(apogeeValue, (float)(x1+width), (float)(y2-2*line));
409 g2.drawGlyphVector(velocityValue, (float)(x1+width), (float)(y2-line));
410 g2.drawGlyphVector(accelerationValue, (float)(x1+width), (float)(y2));
417 private GlyphVector createText(String text) {
418 return FONT.createGlyphVector(g2.getFontRenderContext(), text);
421 private GlyphVector createSmallText(String text) {
422 return SMALLFONT.createGlyphVector(g2.getFontRenderContext(), text);