create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / figureelements / RocketInfo.java
1 package net.sf.openrocket.gui.figureelements;
2
3 import static net.sf.openrocket.util.Chars.ALPHA;
4 import static net.sf.openrocket.util.Chars.THETA;
5
6 import java.awt.Color;
7 import java.awt.Font;
8 import java.awt.Graphics2D;
9 import java.awt.Rectangle;
10 import java.awt.font.GlyphVector;
11 import java.awt.geom.Rectangle2D;
12
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;
22
23
24 /**
25  * A <code>FigureElement</code> that draws text at different positions in the figure
26  * with general data about the rocket.
27  * 
28  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
29  */
30 public class RocketInfo implements FigureElement {
31         
32         private static final Translator trans = Application.getTranslator();
33         // Margin around the figure edges, pixels
34         private static final int MARGIN = 8;
35
36         // Font to use
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);
39
40         
41         private final Caret cpCaret = new CPCaret(0,0);
42         private final Caret cgCaret = new CGCaret(0,0);
43         
44         private final Configuration configuration;
45         private final UnitGroup stabilityUnits;
46         
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();
51         
52         private WarningSet warnings = null;
53         
54         private boolean calculatingData = false;
55         private FlightData flightData = null;
56         
57         private Graphics2D g2 = null;
58         private float line = 0;
59         private float x1, x2, y1, y2;
60         
61         
62         
63         
64         
65         public RocketInfo(Configuration configuration) {
66                 this.configuration = configuration;
67                 this.stabilityUnits = UnitGroup.stabilityUnits(configuration);
68         }
69         
70         
71         @Override
72         public void paint(Graphics2D g2, double scale) {
73                 throw new UnsupportedOperationException("paint() must be called with coordinates");
74         }
75
76         @Override
77         public void paint(Graphics2D g2, double scale, Rectangle visible) {
78                 this.g2 = g2;
79                 this.line = FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
80                                 g2.getFontRenderContext()).getHeight();
81                 
82                 x1 = visible.x + MARGIN;
83                 x2 = visible.x + visible.width - MARGIN;
84                 y1 = visible.y + line ;
85                 y2 = visible.y + visible.height - MARGIN;
86
87                 drawMainInfo();
88                 drawStabilityInfo();
89                 drawWarnings();
90                 drawFlightInformation();
91         }
92         
93         
94         public void setCG(double cg) {
95                 this.cg = cg;
96         }
97         
98         public void setCP(double cp) {
99                 this.cp = cp;
100         }
101         
102         public void setLength(double length) {
103                 this.length = length;
104         }
105         
106         public void setDiameter(double diameter) {
107                 this.diameter = diameter;
108         }
109         
110         public void setMass(double mass) {
111                 this.mass = mass;
112         }
113         
114         public void setWarnings(WarningSet warnings) {
115                 this.warnings = warnings.clone();
116         }
117         
118         public void setAOA(double aoa) {
119                 this.aoa = aoa;
120         }
121         
122         public void setTheta(double theta) {
123                 this.theta = theta;
124         }
125         
126         public void setMach(double mach) {
127                 this.mach = mach;
128         }
129         
130         
131         public void setFlightData(FlightData data) {
132                 this.flightData = data;
133         }
134         
135         public void setCalculatingData(boolean calc) {
136                 this.calculatingData = calc;
137         }
138         
139         
140         
141         
142         private void drawMainInfo() {
143                 GlyphVector name = createText(configuration.getRocket().getName());
144                 GlyphVector lengthLine = createText(
145                                 //// Length
146                                 trans.get("RocketInfo.lengthLine.Length") +" " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(length) +
147                                 //// , max. diameter
148                                 trans.get("RocketInfo.lengthLine.maxdiameter") +" " + 
149                                 UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter));
150                 
151                 String massText;
152                 if (configuration.hasMotors())
153                         //// Mass with motors 
154                         massText = trans.get("RocketInfo.massText1") +" ";
155                 else
156                         //// Mass with no motors 
157                         massText = trans.get("RocketInfo.massText2") +" ";
158                 
159                 massText += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(mass);
160                 
161                 GlyphVector massLine = createText(massText);
162
163                 
164                 g2.setColor(Color.BLACK);
165
166                 g2.drawGlyphVector(name, x1, y1);
167                 g2.drawGlyphVector(lengthLine, x1, y1+line);
168                 g2.drawGlyphVector(massLine, x1, y1+2*line);
169
170         }
171         
172         
173         private void drawStabilityInfo() {
174                 String at;
175                 //// at M=
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);
179                 }
180                 if (!Double.isNaN(theta)) {
181                         at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
182                 }
183                 
184                 GlyphVector cgValue = createText(
185                 getCg());
186                 GlyphVector cpValue = createText(
187                 getCp());
188                 GlyphVector stabValue = createText(
189                 getStability());
190                 //// CG:                
191                 GlyphVector cgText = createText(trans.get("RocketInfo.cgText") +"  ");
192                 //// CP:
193                 GlyphVector cpText = createText(trans.get("RocketInfo.cpText") +"  ");
194                 //// Stability:
195                 GlyphVector stabText = createText(trans.get("RocketInfo.stabText") + "  ");
196                 GlyphVector atText = createSmallText(at);
197
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();
205                 
206                 double unitWidth = MathUtil.max(cpRect.getWidth(), cgRect.getWidth(),
207                                 stabRect.getWidth());
208                 double textWidth = Math.max(cpTextRect.getWidth(), cgTextRect.getWidth());
209                 
210
211                 g2.setColor(Color.BLACK);
212
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);
216
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);
220                                 
221                 cgCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+line-0.3*line);
222                 cgCaret.paint(g2, 1.7);
223
224                 cpCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+2*line-0.3*line);
225                 cpCaret.paint(g2, 1.7);
226                 
227                 float atPos;
228                 if (unitWidth + textWidth + 10 > atTextRect.getWidth()) {
229                         atPos = (float)(x2-(unitWidth+textWidth+10+atTextRect.getWidth())/2);
230                 } else {
231                         atPos = (float)(x2 - atTextRect.getWidth());
232                 }
233                 
234                 g2.setColor(Color.GRAY);
235                 g2.drawGlyphVector(atText, atPos, y1 + 3*line);
236
237         }
238
239     /**
240      * Get the mass, in default mass units.
241      * 
242      * @return the mass
243      */
244     public double getMass() {
245         return mass;
246     }
247
248     /**
249      * Get the mass in specified mass units.
250      * 
251      * @param u UnitGroup.MASS
252      * 
253      * @return the mass
254      */
255     public String getMass(Unit u) {
256         return u.toStringUnit(mass);
257     }
258     
259     /**
260      * Get the stability, in calibers.
261      * 
262      * @return  the current stability margin
263      */
264     public String getStability () {
265         return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg);
266     }
267
268     /**
269      * Get the center of pressure in default length units.
270      * 
271      * @return  the distance from the tip to the center of pressure, in default length units
272      */
273     public String getCp () {
274         return getCp(UnitGroup.UNITS_LENGTH.getDefaultUnit());
275     }
276
277     /**
278      * Get the center of pressure in default length units.
279      * 
280      * @param u UnitGroup.LENGTH 
281      * 
282      * @return  the distance from the tip to the center of pressure, in default length units
283      */
284     public String getCp (Unit u) {
285         return u.toStringUnit(cp);
286     }
287
288     /**
289      * Get the center of gravity in default length units.
290      * 
291      * @return  the distance from the tip to the center of gravity, in default length units
292      */
293     public String getCg () {
294         return getCg(UnitGroup.UNITS_LENGTH.getDefaultUnit());
295     }
296
297     /**
298      * Get the center of gravity in specified length units.
299      * 
300      * @param u UnitGroup.LENGTH 
301      * @return  the distance from the tip to the center of gravity, in specified units
302      */
303     public String getCg (Unit u) {
304         return u.toStringUnit(cg);
305     }
306
307     /**
308      * Get the flight data for the current motor configuration.
309      * 
310      * @return flight data, or null
311      */
312     public FlightData getFlightData () {
313         return flightData;
314     }
315     
316     private void drawWarnings() {
317                 if (warnings == null || warnings.isEmpty())
318                         return;
319                 
320                 GlyphVector[] texts = new GlyphVector[warnings.size()+1];
321                 double max = 0;
322                 
323                 //// Warning:
324                 texts[0] = createText(trans.get("RocketInfo.Warning"));
325                 int i=1;
326                 for (Warning w: warnings) {
327                         texts[i] = createText(w.toString());
328                         i++;
329                 }
330                 
331                 for (GlyphVector v: texts) {
332                         Rectangle2D rect = v.getVisualBounds();
333                         if (rect.getWidth() > max)
334                                 max = rect.getWidth();
335                 }
336                 
337
338                 float y = y2 - line * warnings.size();
339                 g2.setColor(new Color(255,0,0,130));
340
341                 for (GlyphVector v: texts) {
342                         Rectangle2D rect = v.getVisualBounds();
343                         g2.drawGlyphVector(v, (float)(x2 - max/2 - rect.getWidth()/2), y);
344                         y += line;
345                 }
346         }
347         
348         
349         private void drawFlightInformation() {
350                 double height = drawFlightData();
351                 
352                 if (calculatingData) {
353                         //// Calculating...
354                         GlyphVector calculating = createText(trans.get("RocketInfo.Calculating"));
355                         g2.setColor(Color.BLACK);
356                         g2.drawGlyphVector(calculating, x1, (float)(y2-height));
357                 }
358         }
359         
360         
361         private double drawFlightData() {
362                 if (flightData == null)
363                         return 0;
364                 
365                 double width=0;
366                 
367                 //// Apogee: 
368                 GlyphVector apogee = createText(trans.get("RocketInfo.Apogee")+" ");
369                 //// Max. velocity:
370                 GlyphVector maxVelocity = createText(trans.get("RocketInfo.Maxvelocity") +" ");
371                 //// Max. acceleration: 
372                 GlyphVector maxAcceleration = createText(trans.get("RocketInfo.Maxacceleration") + " ");
373
374                 GlyphVector apogeeValue, velocityValue, accelerationValue;
375                 if (!Double.isNaN(flightData.getMaxAltitude())) {
376                         apogeeValue = createText(
377                                         UnitGroup.UNITS_DISTANCE.toStringUnit(flightData.getMaxAltitude()));
378                 } else {
379                         //// N/A
380                         apogeeValue = createText(trans.get("RocketInfo.apogeeValue"));
381                 }
382                 if (!Double.isNaN(flightData.getMaxVelocity())) {
383                         velocityValue = createText(
384                                         UnitGroup.UNITS_VELOCITY.toStringUnit(flightData.getMaxVelocity()) +
385                                         //// (Mach
386                                         "  " +trans.get("RocketInfo.Mach") +" " + 
387                                         UnitGroup.UNITS_COEFFICIENT.toString(flightData.getMaxMachNumber()) + ")");
388                 } else {
389                         //// N/A
390                         velocityValue = createText(trans.get("RocketInfo.velocityValue"));
391                 }
392                 if (!Double.isNaN(flightData.getMaxAcceleration())) {
393                         accelerationValue = createText(
394                                         UnitGroup.UNITS_ACCELERATION.toStringUnit(flightData.getMaxAcceleration()));
395                 } else {
396                         //// N/A
397                         accelerationValue = createText(trans.get("RocketInfo.accelerationValue"));
398                 }
399                 
400                 Rectangle2D rect;
401                 rect = apogee.getVisualBounds();
402                 width = MathUtil.max(width, rect.getWidth());
403                 
404                 rect = maxVelocity.getVisualBounds();
405                 width = MathUtil.max(width, rect.getWidth());
406                 
407                 rect = maxAcceleration.getVisualBounds();
408                 width = MathUtil.max(width, rect.getWidth());
409                 
410                 width += 5;
411
412                 if (!calculatingData) 
413                         g2.setColor(new Color(0,0,127));
414                 else
415                         g2.setColor(new Color(0,0,127,127));
416
417                 
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));
421
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));
425                 
426                 return 3*line;
427         }
428         
429         
430         
431         private GlyphVector createText(String text) {
432                 return FONT.createGlyphVector(g2.getFontRenderContext(), text);
433         }
434
435         private GlyphVector createSmallText(String text) {
436                 return SMALLFONT.createGlyphVector(g2.getFontRenderContext(), text);
437         }
438
439 }