package net.sf.openrocket.gui.figureelements; import static net.sf.openrocket.util.Chars.ALPHA; import static net.sf.openrocket.util.Chars.THETA; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.MathUtil; /** * A <code>FigureElement</code> that draws text at different positions in the figure * with general data about the rocket. * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public class RocketInfo implements FigureElement { private static final Translator trans = Application.getTranslator(); // Margin around the figure edges, pixels private static final int MARGIN = 8; // Font to use private static final Font FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 11); private static final Font SMALLFONT = new Font(Font.SANS_SERIF, Font.PLAIN, 9); private final Caret cpCaret = new CPCaret(0,0); private final Caret cgCaret = new CGCaret(0,0); private final Configuration configuration; private final UnitGroup stabilityUnits; private double cg = 0, cp = 0; private double length = 0, diameter = 0; private double mass = 0; private double massWithoutMotors = 0; private double aoa = Double.NaN, theta = Double.NaN, mach = Application.getPreferences().getDefaultMach(); private WarningSet warnings = null; private boolean calculatingData = false; private FlightData flightData = null; private Graphics2D g2 = null; private float line = 0; private float x1, x2, y1, y2; public RocketInfo(Configuration configuration) { this.configuration = configuration; this.stabilityUnits = UnitGroup.stabilityUnits(configuration); } @Override public void paint(Graphics2D myG2, double scale) { throw new UnsupportedOperationException("paint() must be called with coordinates"); } @Override public void paint(Graphics2D myG2, double scale, Rectangle visible) { this.g2 = myG2; this.line = FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", myG2.getFontRenderContext()).getHeight() + FONT.getLineMetrics("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", myG2.getFontRenderContext()).getDescent(); x1 = visible.x + MARGIN; x2 = visible.x + visible.width - MARGIN; y1 = visible.y + line ; y2 = visible.y + visible.height - MARGIN; drawMainInfo(); drawStabilityInfo(); drawWarnings(); drawFlightInformation(); } public void setCG(double cg) { this.cg = cg; } public void setCP(double cp) { this.cp = cp; } public void setLength(double length) { this.length = length; } public void setDiameter(double diameter) { this.diameter = diameter; } public void setMass(double mass) { this.mass = mass; } public void setMassWithoutMotors(double mass) { this.massWithoutMotors = mass; } public void setWarnings(WarningSet warnings) { this.warnings = warnings.clone(); } public void setAOA(double aoa) { this.aoa = aoa; } public void setTheta(double theta) { this.theta = theta; } public void setMach(double mach) { this.mach = mach; } public void setFlightData(FlightData data) { this.flightData = data; } public void setCalculatingData(boolean calc) { this.calculatingData = calc; } private void drawMainInfo() { GlyphVector name = createText(configuration.getRocket().getName()); GlyphVector lengthLine = createText( //// Length trans.get("RocketInfo.lengthLine.Length") +" " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(length) + //// , max. diameter trans.get("RocketInfo.lengthLine.maxdiameter") +" " + UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter)); String massTextWithMotors; String massTextWithoutMotors; /// Mass with no motors massTextWithoutMotors = trans.get("RocketInfo.massWithoutMotors") +" "; massTextWithoutMotors += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(massWithoutMotors); GlyphVector massLineWithoutMotors = createText(massTextWithoutMotors); g2.setColor(Color.BLACK); g2.drawGlyphVector(name, x1, y1); g2.drawGlyphVector(lengthLine, x1, y1+line); g2.drawGlyphVector(massLineWithoutMotors, x1, y1+2*line); if( configuration.hasMotors() ) { //// Mass with motors massTextWithMotors = trans.get("RocketInfo.massWithMotors") + " "; massTextWithMotors += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(mass); GlyphVector massLineWithMotors = createText(massTextWithMotors); g2.drawGlyphVector(massLineWithMotors, x1, y1+3*line); } } private void drawStabilityInfo() { String at; //// at M= at = trans.get("RocketInfo.at")+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(Application.getPreferences().getDefaultMach()); if (!Double.isNaN(aoa)) { at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa); } if (!Double.isNaN(theta)) { at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta); } GlyphVector cgValue = createText( getCg()); GlyphVector cpValue = createText( getCp()); GlyphVector stabValue = createText( getStability()); //// CG: GlyphVector cgText = createText(trans.get("RocketInfo.cgText") +" "); //// CP: GlyphVector cpText = createText(trans.get("RocketInfo.cpText") +" "); //// Stability: GlyphVector stabText = createText(trans.get("RocketInfo.stabText") + " "); GlyphVector atText = createSmallText(at); Rectangle2D cgRect = cgValue.getVisualBounds(); Rectangle2D cpRect = cpValue.getVisualBounds(); Rectangle2D cgTextRect = cgText.getVisualBounds(); Rectangle2D cpTextRect = cpText.getVisualBounds(); Rectangle2D stabRect = stabValue.getVisualBounds(); Rectangle2D stabTextRect = stabText.getVisualBounds(); Rectangle2D atTextRect = atText.getVisualBounds(); double unitWidth = MathUtil.max(cpRect.getWidth(), cgRect.getWidth(), stabRect.getWidth()); double textWidth = Math.max(cpTextRect.getWidth(), cgTextRect.getWidth()); g2.setColor(Color.BLACK); g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1); g2.drawGlyphVector(cgValue, (float)(x2-cgRect.getWidth()), y1+line); g2.drawGlyphVector(cpValue, (float)(x2-cpRect.getWidth()), y1+2*line); g2.drawGlyphVector(stabText, (float)(x2-unitWidth-stabTextRect.getWidth()), y1); g2.drawGlyphVector(cgText, (float)(x2-unitWidth-cgTextRect.getWidth()), y1+line); g2.drawGlyphVector(cpText, (float)(x2-unitWidth-cpTextRect.getWidth()), y1+2*line); cgCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+line-0.3*line); cgCaret.paint(g2, 1.7); cpCaret.setPosition(x2 - unitWidth - textWidth - 10, y1+2*line-0.3*line); cpCaret.paint(g2, 1.7); float atPos; if (unitWidth + textWidth + 10 > atTextRect.getWidth()) { atPos = (float)(x2-(unitWidth+textWidth+10+atTextRect.getWidth())/2); } else { atPos = (float)(x2 - atTextRect.getWidth()); } g2.setColor(Color.GRAY); g2.drawGlyphVector(atText, atPos, y1 + 3*line); } /** * Get the mass, in default mass units. * * @return the mass */ public double getMass() { return mass; } /** * Get the mass in specified mass units. * * @param u UnitGroup.MASS * * @return the mass */ public String getMass(Unit u) { return u.toStringUnit(mass); } /** * Get the stability, in calibers. * * @return the current stability margin */ public String getStability () { return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg); } /** * Get the center of pressure in default length units. * * @return the distance from the tip to the center of pressure, in default length units */ public String getCp () { return getCp(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } /** * Get the center of pressure in default length units. * * @param u UnitGroup.LENGTH * * @return the distance from the tip to the center of pressure, in default length units */ public String getCp (Unit u) { return u.toStringUnit(cp); } /** * Get the center of gravity in default length units. * * @return the distance from the tip to the center of gravity, in default length units */ public String getCg () { return getCg(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } /** * Get the center of gravity in specified length units. * * @param u UnitGroup.LENGTH * @return the distance from the tip to the center of gravity, in specified units */ public String getCg (Unit u) { return u.toStringUnit(cg); } /** * Get the flight data for the current motor configuration. * * @return flight data, or null */ public FlightData getFlightData () { return flightData; } private void drawWarnings() { if (warnings == null || warnings.isEmpty()) return; GlyphVector[] texts = new GlyphVector[warnings.size()+1]; double max = 0; //// Warning: texts[0] = createText(trans.get("RocketInfo.Warning")); int i=1; for (Warning w: warnings) { texts[i] = createText(w.toString()); i++; } for (GlyphVector v: texts) { Rectangle2D rect = v.getVisualBounds(); if (rect.getWidth() > max) max = rect.getWidth(); } float y = y2 - line * warnings.size(); g2.setColor(new Color(255,0,0,130)); for (GlyphVector v: texts) { Rectangle2D rect = v.getVisualBounds(); g2.drawGlyphVector(v, (float)(x2 - max/2 - rect.getWidth()/2), y); y += line; } } private void drawFlightInformation() { double height = drawFlightData(); if (calculatingData) { //// Calculating... GlyphVector calculating = createText(trans.get("RocketInfo.Calculating")); g2.setColor(Color.BLACK); g2.drawGlyphVector(calculating, x1, (float)(y2-height)); } } private double drawFlightData() { if (flightData == null) return 0; double width=0; //// Apogee: GlyphVector apogee = createText(trans.get("RocketInfo.Apogee")+" "); //// Max. velocity: GlyphVector maxVelocity = createText(trans.get("RocketInfo.Maxvelocity") +" "); //// Max. acceleration: GlyphVector maxAcceleration = createText(trans.get("RocketInfo.Maxacceleration") + " "); GlyphVector apogeeValue, velocityValue, accelerationValue; if (!Double.isNaN(flightData.getMaxAltitude())) { apogeeValue = createText( UnitGroup.UNITS_DISTANCE.toStringUnit(flightData.getMaxAltitude())); } else { //// N/A apogeeValue = createText(trans.get("RocketInfo.apogeeValue")); } if (!Double.isNaN(flightData.getMaxVelocity())) { velocityValue = createText( UnitGroup.UNITS_VELOCITY.toStringUnit(flightData.getMaxVelocity()) + //// (Mach " " +trans.get("RocketInfo.Mach") +" " + UnitGroup.UNITS_COEFFICIENT.toString(flightData.getMaxMachNumber()) + ")"); } else { //// N/A velocityValue = createText(trans.get("RocketInfo.velocityValue")); } if (!Double.isNaN(flightData.getMaxAcceleration())) { accelerationValue = createText( UnitGroup.UNITS_ACCELERATION.toStringUnit(flightData.getMaxAcceleration())); } else { //// N/A accelerationValue = createText(trans.get("RocketInfo.accelerationValue")); } Rectangle2D rect; rect = apogee.getVisualBounds(); width = MathUtil.max(width, rect.getWidth()); rect = maxVelocity.getVisualBounds(); width = MathUtil.max(width, rect.getWidth()); rect = maxAcceleration.getVisualBounds(); width = MathUtil.max(width, rect.getWidth()); width += 5; if (!calculatingData) g2.setColor(new Color(0,0,127)); else g2.setColor(new Color(0,0,127,127)); g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line)); g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line)); g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2)); g2.drawGlyphVector(apogeeValue, (float)(x1+width), (float)(y2-2*line)); g2.drawGlyphVector(velocityValue, (float)(x1+width), (float)(y2-line)); g2.drawGlyphVector(accelerationValue, (float)(x1+width), (float)(y2)); return 3*line; } private GlyphVector createText(String text) { float size=Application.getPreferences().getRocketInfoFontSize(); return (FONT.deriveFont(size)).createGlyphVector(g2.getFontRenderContext(), text); } private GlyphVector createSmallText(String text) { float size=(float) (Application.getPreferences().getRocketInfoFontSize()-2.0); return (SMALLFONT.deriveFont(size)).createGlyphVector(g2.getFontRenderContext(), text); } }