1 package net.sf.openrocket.gui.figureelements;
3 import static net.sf.openrocket.util.Chars.ALPHA;
4 import static net.sf.openrocket.util.Chars.THETA;
8 import java.awt.Graphics2D;
9 import java.awt.Rectangle;
10 import java.awt.font.GlyphVector;
11 import java.awt.geom.Rectangle2D;
13 import net.sf.openrocket.aerodynamics.Warning;
14 import net.sf.openrocket.aerodynamics.WarningSet;
15 import net.sf.openrocket.l10n.Translator;
16 import net.sf.openrocket.rocketcomponent.Configuration;
17 import net.sf.openrocket.simulation.FlightData;
18 import net.sf.openrocket.startup.Application;
19 import net.sf.openrocket.unit.Unit;
20 import net.sf.openrocket.unit.UnitGroup;
21 import net.sf.openrocket.util.MathUtil;
25 * A <code>FigureElement</code> that draws text at different positions in the figure
26 * with general data about the rocket.
28 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
30 public class RocketInfo implements FigureElement {
32 private static final Translator trans = Application.getTranslator();
33 // Margin around the figure edges, pixels
34 private static final int MARGIN = 8;
37 private static final Font FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 11);
38 private static final Font SMALLFONT = new Font(Font.SANS_SERIF, Font.PLAIN, 9);
41 private final Caret cpCaret = new CPCaret(0,0);
42 private final Caret cgCaret = new CGCaret(0,0);
44 private final Configuration configuration;
45 private final UnitGroup stabilityUnits;
47 private double cg = 0, cp = 0;
48 private double length = 0, diameter = 0;
49 private double mass = 0;
50 private double aoa = Double.NaN, theta = Double.NaN, mach = Application.getPreferences().getDefaultMach();
52 private WarningSet warnings = null;
54 private boolean calculatingData = false;
55 private FlightData flightData = null;
57 private Graphics2D g2 = null;
58 private float line = 0;
59 private float x1, x2, y1, y2;
65 public RocketInfo(Configuration configuration) {
66 this.configuration = configuration;
67 this.stabilityUnits = UnitGroup.stabilityUnits(configuration);
72 public void paint(Graphics2D g2, double scale) {
73 throw new UnsupportedOperationException("paint() must be called with coordinates");
77 public void paint(Graphics2D g2, double scale, Rectangle visible) {
79 this.line = FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
80 g2.getFontRenderContext()).getHeight();
82 x1 = visible.x + MARGIN;
83 x2 = visible.x + visible.width - MARGIN;
84 y1 = visible.y + line ;
85 y2 = visible.y + visible.height - MARGIN;
90 drawFlightInformation();
94 public void setCG(double cg) {
98 public void setCP(double cp) {
102 public void setLength(double length) {
103 this.length = length;
106 public void setDiameter(double diameter) {
107 this.diameter = diameter;
110 public void setMass(double mass) {
114 public void setWarnings(WarningSet warnings) {
115 this.warnings = warnings.clone();
118 public void setAOA(double aoa) {
122 public void setTheta(double theta) {
126 public void setMach(double mach) {
131 public void setFlightData(FlightData data) {
132 this.flightData = data;
135 public void setCalculatingData(boolean calc) {
136 this.calculatingData = calc;
142 private void drawMainInfo() {
143 GlyphVector name = createText(configuration.getRocket().getName());
144 GlyphVector lengthLine = createText(
146 trans.get("RocketInfo.lengthLine.Length") +" " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(length) +
148 trans.get("RocketInfo.lengthLine.maxdiameter") +" " +
149 UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter));
152 if (configuration.hasMotors())
153 //// Mass with motors
154 massText = trans.get("RocketInfo.massText1") +" ";
156 //// Mass with no motors
157 massText = trans.get("RocketInfo.massText2") +" ";
159 massText += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(mass);
161 GlyphVector massLine = createText(massText);
164 g2.setColor(Color.BLACK);
166 g2.drawGlyphVector(name, x1, y1);
167 g2.drawGlyphVector(lengthLine, x1, y1+line);
168 g2.drawGlyphVector(massLine, x1, y1+2*line);
173 private void drawStabilityInfo() {
176 at = trans.get("RocketInfo.at")+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(mach);
177 if (!Double.isNaN(aoa)) {
178 at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
180 if (!Double.isNaN(theta)) {
181 at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
184 GlyphVector cgValue = createText(
186 GlyphVector cpValue = createText(
188 GlyphVector stabValue = createText(
191 GlyphVector cgText = createText(trans.get("RocketInfo.cgText") +" ");
193 GlyphVector cpText = createText(trans.get("RocketInfo.cpText") +" ");
195 GlyphVector stabText = createText(trans.get("RocketInfo.stabText") + " ");
196 GlyphVector atText = createSmallText(at);
198 Rectangle2D cgRect = cgValue.getVisualBounds();
199 Rectangle2D cpRect = cpValue.getVisualBounds();
200 Rectangle2D cgTextRect = cgText.getVisualBounds();
201 Rectangle2D cpTextRect = cpText.getVisualBounds();
202 Rectangle2D stabRect = stabValue.getVisualBounds();
203 Rectangle2D stabTextRect = stabText.getVisualBounds();
204 Rectangle2D atTextRect = atText.getVisualBounds();
206 double unitWidth = MathUtil.max(cpRect.getWidth(), cgRect.getWidth(),
207 stabRect.getWidth());
208 double textWidth = Math.max(cpTextRect.getWidth(), cgTextRect.getWidth());
211 g2.setColor(Color.BLACK);
213 g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1);
214 g2.drawGlyphVector(cgValue, (float)(x2-cgRect.getWidth()), y1+line);
215 g2.drawGlyphVector(cpValue, (float)(x2-cpRect.getWidth()), y1+2*line);
217 g2.drawGlyphVector(stabText, (float)(x2-unitWidth-stabTextRect.getWidth()), y1);
218 g2.drawGlyphVector(cgText, (float)(x2-unitWidth-cgTextRect.getWidth()), y1+line);
219 g2.drawGlyphVector(cpText, (float)(x2-unitWidth-cpTextRect.getWidth()), y1+2*line);
221 cgCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+line-0.3*line);
222 cgCaret.paint(g2, 1.7);
224 cpCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+2*line-0.3*line);
225 cpCaret.paint(g2, 1.7);
228 if (unitWidth + textWidth + 10 > atTextRect.getWidth()) {
229 atPos = (float)(x2-(unitWidth+textWidth+10+atTextRect.getWidth())/2);
231 atPos = (float)(x2 - atTextRect.getWidth());
234 g2.setColor(Color.GRAY);
235 g2.drawGlyphVector(atText, atPos, y1 + 3*line);
240 * Get the mass, in default mass units.
244 public double getMass() {
249 * Get the mass in specified mass units.
251 * @param u UnitGroup.MASS
255 public String getMass(Unit u) {
256 return u.toStringUnit(mass);
260 * Get the stability, in calibers.
262 * @return the current stability margin
264 public String getStability () {
265 return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg);
269 * Get the center of pressure in default length units.
271 * @return the distance from the tip to the center of pressure, in default length units
273 public String getCp () {
274 return getCp(UnitGroup.UNITS_LENGTH.getDefaultUnit());
278 * Get the center of pressure in default length units.
280 * @param u UnitGroup.LENGTH
282 * @return the distance from the tip to the center of pressure, in default length units
284 public String getCp (Unit u) {
285 return u.toStringUnit(cp);
289 * Get the center of gravity in default length units.
291 * @return the distance from the tip to the center of gravity, in default length units
293 public String getCg () {
294 return getCg(UnitGroup.UNITS_LENGTH.getDefaultUnit());
298 * Get the center of gravity in specified length units.
300 * @param u UnitGroup.LENGTH
301 * @return the distance from the tip to the center of gravity, in specified units
303 public String getCg (Unit u) {
304 return u.toStringUnit(cg);
308 * Get the flight data for the current motor configuration.
310 * @return flight data, or null
312 public FlightData getFlightData () {
316 private void drawWarnings() {
317 if (warnings == null || warnings.isEmpty())
320 GlyphVector[] texts = new GlyphVector[warnings.size()+1];
324 texts[0] = createText(trans.get("RocketInfo.Warning"));
326 for (Warning w: warnings) {
327 texts[i] = createText(w.toString());
331 for (GlyphVector v: texts) {
332 Rectangle2D rect = v.getVisualBounds();
333 if (rect.getWidth() > max)
334 max = rect.getWidth();
338 float y = y2 - line * warnings.size();
339 g2.setColor(new Color(255,0,0,130));
341 for (GlyphVector v: texts) {
342 Rectangle2D rect = v.getVisualBounds();
343 g2.drawGlyphVector(v, (float)(x2 - max/2 - rect.getWidth()/2), y);
349 private void drawFlightInformation() {
350 double height = drawFlightData();
352 if (calculatingData) {
354 GlyphVector calculating = createText(trans.get("RocketInfo.Calculating"));
355 g2.setColor(Color.BLACK);
356 g2.drawGlyphVector(calculating, x1, (float)(y2-height));
361 private double drawFlightData() {
362 if (flightData == null)
368 GlyphVector apogee = createText(trans.get("RocketInfo.Apogee")+" ");
370 GlyphVector maxVelocity = createText(trans.get("RocketInfo.Maxvelocity") +" ");
371 //// Max. acceleration:
372 GlyphVector maxAcceleration = createText(trans.get("RocketInfo.Maxacceleration") + " ");
374 GlyphVector apogeeValue, velocityValue, accelerationValue;
375 if (!Double.isNaN(flightData.getMaxAltitude())) {
376 apogeeValue = createText(
377 UnitGroup.UNITS_DISTANCE.toStringUnit(flightData.getMaxAltitude()));
380 apogeeValue = createText(trans.get("RocketInfo.apogeeValue"));
382 if (!Double.isNaN(flightData.getMaxVelocity())) {
383 velocityValue = createText(
384 UnitGroup.UNITS_VELOCITY.toStringUnit(flightData.getMaxVelocity()) +
386 " " +trans.get("RocketInfo.Mach") +" " +
387 UnitGroup.UNITS_COEFFICIENT.toString(flightData.getMaxMachNumber()) + ")");
390 velocityValue = createText(trans.get("RocketInfo.velocityValue"));
392 if (!Double.isNaN(flightData.getMaxAcceleration())) {
393 accelerationValue = createText(
394 UnitGroup.UNITS_ACCELERATION.toStringUnit(flightData.getMaxAcceleration()));
397 accelerationValue = createText(trans.get("RocketInfo.accelerationValue"));
401 rect = apogee.getVisualBounds();
402 width = MathUtil.max(width, rect.getWidth());
404 rect = maxVelocity.getVisualBounds();
405 width = MathUtil.max(width, rect.getWidth());
407 rect = maxAcceleration.getVisualBounds();
408 width = MathUtil.max(width, rect.getWidth());
412 if (!calculatingData)
413 g2.setColor(new Color(0,0,127));
415 g2.setColor(new Color(0,0,127,127));
418 g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line));
419 g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line));
420 g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2));
422 g2.drawGlyphVector(apogeeValue, (float)(x1+width), (float)(y2-2*line));
423 g2.drawGlyphVector(velocityValue, (float)(x1+width), (float)(y2-line));
424 g2.drawGlyphVector(accelerationValue, (float)(x1+width), (float)(y2));
431 private GlyphVector createText(String text) {
432 return FONT.createGlyphVector(g2.getFontRenderContext(), text);
435 private GlyphVector createSmallText(String text) {
436 return SMALLFONT.createGlyphVector(g2.getFontRenderContext(), text);