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