PrimaryFlightDisplay.cpp 56.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#include "PrimaryFlightDisplay.h"
#include "UASManager.h"

//#include "ui_primaryflightdisplay.h"
#include <QDebug>
#include <QRectF>
#include <cmath>
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
dongfang's avatar
dongfang committed
12 13
#include <QtCore/qmath.h>
//#include <cmath>
14 15 16

/*
 *@TODO:
dongfang's avatar
dongfang committed
17
 * global fixed pens (and painters too?)
18 19 20
 * repaint on demand multiple canvases
 * multi implementation with shared model class
 */
dongfang's avatar
dongfang committed
21
double PrimaryFlightDisplay_round(double value, int digits=0)
22
{
dongfang's avatar
dongfang committed
23
    return floor(value * pow(10, digits) + 0.5) / pow(10, digits);
24
}
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

const int PrimaryFlightDisplay::tickValues[] = {10, 20, 30, 45, 60};
const QString PrimaryFlightDisplay::compassWindNames[] = {
    QString("N"),
    QString("NE"),
    QString("E"),
    QString("SE"),
    QString("S"),
    QString("SW"),
    QString("W"),
    QString("NW")
};

PrimaryFlightDisplay::PrimaryFlightDisplay(int width, int height, QWidget *parent) :
    QWidget(parent),

41
    uas(NULL),
42

43 44 45 46
    altimeterMode(GPS_MAIN),
    altimeterFrame(ASL),
    speedMode(GROUND_MAIN),

47 48 49 50
    roll(UNKNOWN_ATTITUDE),
    pitch(UNKNOWN_ATTITUDE),
    //    heading(NAN),
    heading(UNKNOWN_ATTITUDE),
51
    primaryAltitude(UNKNOWN_ALTITUDE),
52 53
    GPSAltitude(UNKNOWN_ALTITUDE),
    aboveHomeAltitude(UNKNOWN_ALTITUDE),
54
    primarySpeed(UNKNOWN_SPEED),
55
    groundspeed(UNKNOWN_SPEED),
56
    verticalVelocity(UNKNOWN_ALTITUDE),
57

58 59
    font("Bitstream Vera Sans"),
    refreshTimer(new QTimer(this)),
dongfang's avatar
dongfang committed
60

61 62 63
    navigationCrosstrackError(INFINITY),
    navigationTargetBearing(UNKNOWN_ATTITUDE),

64 65
    layout(COMPASS_INTEGRATED),
    style(OVERLAY_HSI),
66

dongfang's avatar
dongfang committed
67
    redColor(QColor::fromHsvF(0, 0.75, 0.9)),
dongfang's avatar
dongfang committed
68 69 70 71 72 73 74
    amberColor(QColor::fromHsvF(0.12, 0.6, 1.0)),
    greenColor(QColor::fromHsvF(0.25, 0.8, 0.8)),

    lineWidth(2),
    fineLineWidth(1),

    instrumentEdgePen(QColor::fromHsvF(0, 0, 0.65, 0.5)),
dongfang's avatar
dongfang committed
75
    //    AIEdgePen(QColor::fromHsvF(0, 0, 0.65, 0.5)),
76

dongfang's avatar
dongfang committed
77 78 79
    instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
    instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0))
{
80 81 82 83 84 85 86 87 88
    setMinimumSize(120, 80);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    // Connect with UAS
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
    if (UASManager::instance()->getActiveUAS() != NULL) setActiveUAS(UASManager::instance()->getActiveUAS());

    // Refresh timer
    refreshTimer->setInterval(updateInterval);
dongfang's avatar
dongfang committed
89
    //    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
90
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
}

PrimaryFlightDisplay::~PrimaryFlightDisplay()
{
    refreshTimer->stop();
}


QSize PrimaryFlightDisplay::sizeHint() const
{
    return QSize(width(), (width()*3.0f)/4);
}

void PrimaryFlightDisplay::showEvent(QShowEvent* event)
{
    // React only to internal (pre-display)
    // events
    QWidget::showEvent(event);
    refreshTimer->start(updateInterval);
    emit visibilityChanged(true);
}

void PrimaryFlightDisplay::hideEvent(QHideEvent* event)
{
    // React only to internal (pre-display)
    // events
    refreshTimer->stop();
    QWidget::hideEvent(event);
    emit visibilityChanged(false);
}

dongfang's avatar
dongfang committed
122 123 124 125 126 127 128 129 130 131
qreal constrain(qreal value, qreal min, qreal max) {
    if (value<min) value=min;
    else if(value>max) value=max;
    return value;
}

void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
    QWidget::resizeEvent(e);

    qreal size = e->size().width();
dongfang's avatar
dongfang committed
132
    //if(e->size().height()<size) size = e->size().height();
dongfang's avatar
dongfang committed
133

dongfang's avatar
dongfang committed
134
    lineWidth = constrain(size*LINEWIDTH, 1, 6);
dongfang's avatar
dongfang committed
135 136 137 138 139 140 141 142 143
    fineLineWidth = constrain(size*LINEWIDTH*2/3, 1, 2);

    instrumentEdgePen.setWidthF(fineLineWidth);
    //AIEdgePen.setWidthF(fineLineWidth);

    smallTestSize = size * SMALL_TEXT_SIZE;
    mediumTextSize = size * MEDIUM_TEXT_SIZE;
    largeTextSize = size * LARGE_TEXT_SIZE;

dongfang's avatar
dongfang committed
144 145
    /*
     * Try without layout Change-O-Matic. It was too complicated.
146 147
    qreal aspect = e->size().width() / e->size().height();
    if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
dongfang's avatar
dongfang committed
148 149
        layout = COMPASS_SEPARATED;
    else
150
        layout = COMPASS_INTEGRATED;
dongfang's avatar
dongfang committed
151
    */
152
    // qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang's avatar
dongfang committed
153
}
154

dongfang's avatar
dongfang committed
155 156 157 158 159 160 161 162 163
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
    // Event is not needed
    // the event is ignored as this widget
    // is refreshed automatically
    Q_UNUSED(event);
    //makeDummyData();
    doPaint();
}
164

dongfang's avatar
dongfang committed
165 166 167
/*
 * Interface towards qgroundcontrol
 */
168 169 170 171 172 173 174 175 176 177
/**
 *
 * @param uas the UAS/MAV to monitor/display with the HUD
 */
void PrimaryFlightDisplay::setActiveUAS(UASInterface* uas)
{
    if (this->uas != NULL) {
        // Disconnect any previously connected active MAV
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
        disconnect(this->uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
178 179 180 181 182 183 184
        disconnect(this->uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
        disconnect(this->uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
        disconnect(this->uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
        disconnect(this->uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
        disconnect(this->uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
        disconnect(this->uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, double, quint64)));
        disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
185

186 187 188 189 190 191
        //disconnect(this->uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, double, int)));
        //disconnect(this->uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
        //disconnect(this->uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
        //disconnect(this->uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
        //disconnect(this->uas, SIGNAL(armingChanged(bool)), this, SLOT(updateArmed(bool)));
        //disconnect(this->uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
192
        //disconnect(this->uas, SIGNAL(localizationChanged(UASInterface* uas, int fix)), this, SLOT(updateGPSFixType(UASInterface*,int)));
193 194 195 196 197 198 199
    }

    if (uas) {
        // Now connect the new UAS
        // Setup communication
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*, double, double, double, quint64)));
        connect(uas, SIGNAL(attitudeChanged(UASInterface*,int,double,double,double,quint64)), this, SLOT(updateAttitude(UASInterface*,int,double, double, double, quint64)));
200

201 202 203 204 205 206
        //connect(uas, SIGNAL(batteryChanged(UASInterface*, double, double, double, int)), this, SLOT(updateBattery(UASInterface*, double, double, double, int)));
        //connect(uas, SIGNAL(statusChanged(UASInterface*,QString,QString)), this, SLOT(updateState(UASInterface*,QString)));
        //connect(uas, SIGNAL(modeChanged(int,QString,QString)), this, SLOT(updateMode(int,QString,QString)));
        //connect(uas, SIGNAL(heartbeat(UASInterface*)), this, SLOT(receiveHeartbeat(UASInterface*)));
        //connect(uas, SIGNAL(armingChanged(bool)), this, SLOT(updateArmed(bool)));
        //connect(uas, SIGNAL(satelliteCountChanged(double, QString)), this, SLOT(updateSatelliteCount(double, QString)));
207

dongfang's avatar
dongfang committed
208
        //connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
209
        //connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
210
        connect(uas, SIGNAL(waypointSelected(int,int)), this, SLOT(selectWaypoint(int, int)));
211 212 213 214 215 216
        connect(uas, SIGNAL(primarySpeedChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimarySpeed(UASInterface*,double,quint64)));
        connect(uas, SIGNAL(gpsSpeedChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSSpeed(UASInterface*,double,quint64)));
        connect(uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this, SLOT(updateClimbRate(UASInterface*, AltitudeMeasurementSource, double, quint64)));
        connect(uas, SIGNAL(primaryAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updatePrimaryAltitude(UASInterface*, double, quint64)));
        connect(uas, SIGNAL(gpsAltitudeChanged(UASInterface*, double, quint64)), this, SLOT(updateGPSAltitude(UASInterface*, double, quint64)));
        connect(uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
217 218 219 220 221 222 223 224 225 226 227 228

        // Set new UAS
        this->uas = uas;
    }
}

void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw))
    {
dongfang's avatar
dongfang committed
229
        // TODO: Units conversion? // Called from UAS.cc l. 646
230 231
        this->roll = roll * (180.0 / M_PI);
        this->pitch = pitch * (180.0 / M_PI);
dongfang's avatar
dongfang committed
232 233 234
        yaw = yaw * (180.0 / M_PI);
        if (yaw<0) yaw+=360;
        this->heading = yaw;
235 236
    }
    // TODO: Else-part. We really should have an "attitude bad or unknown" indication instead of just freezing.
237

238
    //qDebug("r,p,y: %f,%f,%f", roll, pitch, yaw);
239 240 241 242 243 244 245 246 247 248 249
}

/*
 * TODO! Implementation or removal of this.
 * Currently a dummy.
 */
void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(component);
    Q_UNUSED(timestamp);
dongfang's avatar
dongfang committed
250
    // Called from UAS.cc l. 616
dongfang's avatar
dongfang committed
251
    if (!isnan(roll) && !isinf(roll) && !isnan(pitch) && !isinf(pitch) && !isnan(yaw) && !isinf(yaw)) {
dongfang's avatar
dongfang committed
252 253 254 255 256 257
        this->roll = roll * (180.0 / M_PI);
        this->pitch = pitch * (180.0 / M_PI);
        yaw = yaw * (180.0 / M_PI);
        if (yaw<0) yaw+=360;
        this->heading = yaw;
    }
258
    // qDebug("(2) r,p,y: %f,%f,%f", roll, pitch, yaw);
259

260 261 262 263 264 265
}

/*
 * TODO! Examine what data comes with this call, should we consider it airspeed, ground speed or
 * should we not consider it at all?
 */
266 267 268 269 270 271 272 273 274 275
void PrimaryFlightDisplay::updatePrimarySpeed(UASInterface* uas, double speed, quint64 timestamp)
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

    primarySpeed = speed;
    didReceivePrimarySpeed = true;
}

void PrimaryFlightDisplay::updateGPSSpeed(UASInterface* uas, double speed, quint64 timestamp)
276 277 278 279
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

280 281
    groundspeed = speed;
    if (!didReceivePrimarySpeed)
282 283
        primarySpeed = speed;
}
284

285
void PrimaryFlightDisplay::updateClimbRate(UASInterface* uas, double climbRate, quint64 timestamp) {
286 287 288 289 290
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    verticalVelocity = climbRate;
}

291 292 293 294 295 296 297 298
void PrimaryFlightDisplay::updatePrimaryAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    primaryAltitude = altitude;
    didReceivePrimaryAltitude = true;
}

void PrimaryFlightDisplay::updateGPSAltitude(UASInterface* uas, double altitude, quint64 timestamp) {
299 300
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
301 302
    GPSAltitude = altitude;
    if (!didReceivePrimaryAltitude)
303
        primaryAltitude = altitude;
304 305
}

306 307 308 309 310 311 312 313
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
    Q_UNUSED(uas);
    this->navigationAltitudeError = altitudeError;
    this->navigationSpeedError = speedError;
    this->navigationCrosstrackError = xtrackError;
}


314 315 316
/*
 * Private and such
 */
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332

// TODO: Move to UAS. Real working implementation.
bool PrimaryFlightDisplay::isAirplane() {
    if (!this->uas)
        return false;
    switch(this->uas->getSystemType()) {
    case MAV_TYPE_GENERIC:
    case MAV_TYPE_FIXED_WING:
    case MAV_TYPE_AIRSHIP:
    case MAV_TYPE_FLAPPING_WING:
        return true;
    default:
        return false;
    }
}

333 334 335 336 337 338 339
// TODO: Implement. Should return true when navigating.
// That would be (APM) in AUTO and RTL modes.
// This could forward to a virtual on UAS bool isNavigatingAutonomusly() or whatever.
bool PrimaryFlightDisplay::shouldDisplayNavigationData() {
    return true;
}

340 341 342
void PrimaryFlightDisplay::drawTextCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
343
        float pixelSize,
344 345 346
        float x,
        float y)
{
dongfang's avatar
dongfang committed
347
    font.setPixelSize(pixelSize);
348 349 350 351 352 353 354 355 356 357 358
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter |  Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextLeftCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
359
        float pixelSize,
360 361 362
        float x,
        float y)
{
dongfang's avatar
dongfang committed
363
    font.setPixelSize(pixelSize);
364 365 366 367 368 369 370 371 372 373 374
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignLeft | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/, y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextRightCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
375
        float pixelSize,
376 377 378
        float x,
        float y)
{
dongfang's avatar
dongfang committed
379
    font.setPixelSize(pixelSize);
380 381 382 383 384 385 386 387 388 389 390
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignRight | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width(), y /*+bounds.y()*/ -bounds.height()/2, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextCenterTop (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
391
        float pixelSize,
392 393 394
        float x,
        float y)
{
dongfang's avatar
dongfang committed
395
    font.setPixelSize(pixelSize);
396 397 398 399 400 401 402 403 404 405 406
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter | Qt::TextDontClip; // For some reason the bounds rect is too small!
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y+bounds.height() /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawTextCenterBottom (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
407
        float pixelSize,
408 409 410
        float x,
        float y)
{
dongfang's avatar
dongfang committed
411
    font.setPixelSize(pixelSize);
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    painter.setFont(font);

    QFontMetrics metrics = QFontMetrics(font);
    QRect bounds = metrics.boundingRect(text);
    int flags = Qt::AlignCenter;
    painter.drawText(x /*+bounds.x()*/ -bounds.width()/2, y /*+bounds.y()*/, bounds.width(), bounds.height(), flags, text);
}

void PrimaryFlightDisplay::drawInstrumentBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.drawRect(edge);
}

void PrimaryFlightDisplay::fillInstrumentBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

dongfang's avatar
dongfang committed
432 433 434 435 436 437 438
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentOpagueBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

439
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
440
    return pitch * viewHeight / PITCHTRANSLATION;
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
}

void PrimaryFlightDisplay::drawAIAirframeFixedFeatures(QPainter& painter, QRectF area) {
    // red line from -7/10 to -5/10 half-width
    // red line from 7/10 to 5/10 half-width
    // red slanted line from -2/10 half-width to 0
    // red slanted line from 2/10 half-width to 0
    // red arrow thing under roll scale
    // prepareTransform(painter, width, height);
    painter.resetTransform();
    painter.translate(area.center());

    qreal w = area.width();
    qreal h = area.height();

dongfang's avatar
dongfang committed
456
    QPen pen;
dongfang's avatar
dongfang committed
457
    pen.setWidthF(lineWidth * 1.5);
dongfang's avatar
dongfang committed
458 459
    pen.setColor(redColor);
    painter.setPen(pen);
460

dongfang's avatar
dongfang committed
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
    float length = 0.15;
    float side = 0.5;
    // The 2 lines at sides.
    painter.drawLine(QPointF(-side*w, 0), QPointF(-(side-length)*w, 0));
    painter.drawLine(QPointF(side*w, 0), QPointF((side-length)*w, 0));

    float rel = length/qSqrt(2);
    // The gull
    painter.drawLine(QPointF(rel*w, rel*w/2), QPoint(0, 0));
    painter.drawLine(QPointF(-rel*w, rel*w/2), QPoint(0, 0));

    // The roll scale marker.
    QPainterPath markerPath(QPointF(0, -w*ROLL_SCALE_RADIUS+1));
    markerPath.lineTo(-h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
    markerPath.lineTo(h*ROLL_SCALE_MARKERWIDTH/2, -w*(ROLL_SCALE_RADIUS-ROLL_SCALE_MARKERHEIGHT)+1);
476 477 478 479
    markerPath.closeSubpath();
    painter.drawPath(markerPath);
}

480 481 482 483 484 485 486 487 488 489 490 491 492 493
inline qreal min4(qreal a, qreal b, qreal c, qreal d) {
    if(b<a) a=b;
    if(c<a) a=c;
    if(d<a) a=d;
    return a;
}

inline qreal max4(qreal a, qreal b, qreal c, qreal d) {
    if(b>a) a=b;
    if(c>a) a=c;
    if(d>a) a=d;
    return a;
}

494 495
void PrimaryFlightDisplay::drawAIGlobalFeatures(
        QPainter& painter,
496 497
        QRectF mainArea,
        QRectF paintArea) {
498 499

    painter.resetTransform();
500
    painter.translate(mainArea.center());
501

502 503
    qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
    qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
504

505 506
    //painter.rotate(-roll);
    //painter.translate(0, pitchPixels);
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
    //    QTransform forwardTransform;
    //forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
    painter.rotate(-roll);
    painter.translate(0, pitchPixels);

    // Calculate the radius of area we need to paint to cover all.
    QTransform rtx = painter.transform().inverted();

    QPointF topLeft = rtx.map(paintArea.topLeft());
    QPointF topRight = rtx.map(paintArea.topRight());
    QPointF bottomLeft = rtx.map(paintArea.bottomLeft());
    QPointF bottomRight = rtx.map(paintArea.bottomRight());
    // Just KISS... make a rectangluar basis.
    qreal minx = min4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
    qreal maxx = max4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x());
    qreal miny = min4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());
    qreal maxy = max4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y());

    QPointF hzonLeft = QPoint(minx, 0);
    QPointF hzonRight = QPoint(maxx, 0);

    QPainterPath skyPath(hzonLeft);
    skyPath.lineTo(QPointF(minx, miny));
    skyPath.lineTo(QPointF(maxx, miny));
    skyPath.lineTo(hzonRight);
533 534
    skyPath.closeSubpath();

535 536
    // TODO: The gradient is wrong now.
    QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
537 538 539 540 541
    skyGradient.setColorAt(0, QColor::fromHsvF(0.6, 1.0, 0.7));
    skyGradient.setColorAt(1, QColor::fromHsvF(0.6, 0.25, 0.9));
    QBrush skyBrush(skyGradient);
    painter.fillPath(skyPath, skyBrush);

542 543 544 545
    QPainterPath groundPath(hzonRight);
    groundPath.lineTo(maxx, maxy);
    groundPath.lineTo(minx, maxy);
    groundPath.lineTo(hzonLeft);
546 547
    groundPath.closeSubpath();

548
    QLinearGradient groundGradient(0, gradientEnd, 0, 0);
549 550 551 552 553
    groundGradient.setColorAt(0, QColor::fromHsvF(0.25, 1, 0.5));
    groundGradient.setColorAt(1, QColor::fromHsvF(0.25, 0.25, 0.5));
    QBrush groundBrush(groundGradient);
    painter.fillPath(groundPath, groundBrush);

dongfang's avatar
dongfang committed
554 555 556 557 558
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(greenColor);
    painter.setPen(pen);

559 560
    QPointF start(-mainArea.width(), 0);
    QPoint end(mainArea.width(), 0);
561 562 563 564 565 566
    painter.drawLine(start, end);
}

void PrimaryFlightDisplay::drawPitchScale(
        QPainter& painter,
        QRectF area,
dongfang's avatar
dongfang committed
567
        float intrusion,
568 569 570 571
        bool drawNumbersLeft,
        bool drawNumbersRight
        ) {

dongfang's avatar
dongfang committed
572 573 574
    // The area should be quadratic but if not width is the major size.
    qreal w = area.width();
    if (w<area.height()) w = area.height();
575

dongfang's avatar
dongfang committed
576 577 578 579
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
580 581 582 583 584 585 586 587 588

    QTransform savedTransform = painter.transform();

    // find the mark nearest center
    int snap = round(pitch/PITCH_SCALE_RESOLUTION)*PITCH_SCALE_RESOLUTION;
    int _min = snap-PITCH_SCALE_HALFRANGE;
    int _max = snap+PITCH_SCALE_HALFRANGE;
    for (int degrees=_min; degrees<=_max; degrees+=PITCH_SCALE_RESOLUTION) {
        bool isMajor = degrees % (PITCH_SCALE_RESOLUTION*2) == 0;
589 590 591 592 593 594 595 596 597 598 599 600
        float linewidth =  isMajor ? PITCH_SCALE_MAJORWIDTH : PITCH_SCALE_MINORWIDTH;
        if (abs(degrees) > PITCH_SCALE_WIDTHREDUCTION_FROM) {
            // we want: 1 at PITCH_SCALE_WIDTHREDUCTION_FROM and PITCH_SCALE_WIDTHREDUCTION at 90.
            // That is PITCH_SCALE_WIDTHREDUCTION + (1-PITCH_SCALE_WIDTHREDUCTION) * f(pitch)
            // where f(90)=0 and f(PITCH_SCALE_WIDTHREDUCTION_FROM)=1
            // f(p) = (90-p) * 1/(90-PITCH_SCALE_WIDTHREDUCTION_FROM)
            // or PITCH_SCALE_WIDTHREDUCTION + f(pitch) - f(pitch) * PITCH_SCALE_WIDTHREDUCTION
            // or PITCH_SCALE_WIDTHREDUCTION (1-f(pitch)) + f(pitch)
            int fromVertical = abs(pitch>=0 ? 90-pitch : -90-pitch);
            float temp = fromVertical * 1/(90.0f-PITCH_SCALE_WIDTHREDUCTION_FROM);
            linewidth *= (PITCH_SCALE_WIDTHREDUCTION * (1-temp) + temp);
        }
dongfang's avatar
dongfang committed
601 602 603 604 605 606

        float shift = pitchAngleToTranslation(w, pitch-degrees);

        // TODO: Intrusion detection and evasion. That is, don't draw
        // where the compass has intruded.

607
        painter.translate(0, shift);
dongfang's avatar
dongfang committed
608 609
        QPointF start(-linewidth*w, 0);
        QPointF end(linewidth*w, 0);
610 611 612 613 614 615 616 617 618
        painter.drawLine(start, end);

        if (isMajor && (drawNumbersLeft||drawNumbersRight)) {
            int displayDegrees = degrees;
            if(displayDegrees>90) displayDegrees = 180-displayDegrees;
            else if (displayDegrees<-90) displayDegrees = -180 - displayDegrees;
            if (SHOW_ZERO_ON_SCALES || degrees) {
                QString s_number; //= QString("%d").arg(degrees);
                s_number.sprintf("%d", displayDegrees);
dongfang's avatar
dongfang committed
619 620
                if (drawNumbersLeft)  drawTextRightCenter(painter, s_number, mediumTextSize, -PITCH_SCALE_MAJORWIDTH * w-10, 0);
                if (drawNumbersRight) drawTextLeftCenter(painter, s_number, mediumTextSize, PITCH_SCALE_MAJORWIDTH * w+10, 0);
621 622 623 624 625 626 627 628 629 630 631 632 633
            }
        }

        painter.setTransform(savedTransform);
    }
}

void PrimaryFlightDisplay::drawRollScale(
        QPainter& painter,
        QRectF area,
        bool drawTicks,
        bool drawNumbers) {

634
    qreal w = area.width();
dongfang's avatar
dongfang committed
635
    if (w<area.height()) w = area.height();
636

dongfang's avatar
dongfang committed
637 638 639 640
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
641 642 643 644 645 646

    // We should really do these transforms but they are assumed done by caller.
    // painter.resetTransform();
    // painter.translate(area.center());
    // painter.rotate(roll);

647
    qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang's avatar
dongfang committed
648
    QRectF arcArea(-_size/2, - _size/2, _size, _size);
649
    painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang's avatar
dongfang committed
650
    // painter.drawEllipse(QPoint(0,0),200,200);
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
    if (drawTicks) {
        int length = sizeof(tickValues)/sizeof(int);
        qreal previousRotation = 0;
        for (int i=0; i<length*2+1; i++) {
            int degrees = (i==length) ? 0 : (i>length) ?-tickValues[i-length-1] : tickValues[i];
            //degrees = 180 - degrees;
            painter.rotate(degrees - previousRotation);
            previousRotation = degrees;

            QPointF start(0, -_size/2);
            QPointF end(0, -(1.0+ROLL_SCALE_TICKMARKLENGTH)*_size/2);

            painter.drawLine(start, end);

            QString s_number; //= QString("%d").arg(degrees);
            if (SHOW_ZERO_ON_SCALES || degrees)
                s_number.sprintf("%d", abs(degrees));

            if (drawNumbers) {
670
                drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
671 672 673 674 675 676 677
            }
        }
    }
}

void PrimaryFlightDisplay::drawAIAttitudeScales(
        QPainter& painter,
dongfang's avatar
dongfang committed
678 679
        QRectF area,
        float intrusion
dongfang's avatar
dongfang committed
680
        ) {
681 682 683
    // To save computations, we do these transformations once for both scales:
    painter.resetTransform();
    painter.translate(area.center());
684
    painter.rotate(-roll);
685 686 687 688
    QTransform saved = painter.transform();

    drawRollScale(painter, area, true, true);
    painter.setTransform(saved);
dongfang's avatar
dongfang committed
689
    drawPitchScale(painter, area, intrusion, true, true);
690 691
}

dongfang's avatar
dongfang committed
692 693 694
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
    float start = heading - halfspan;
    float end = heading + halfspan;
dongfang's avatar
dongfang committed
695

dongfang's avatar
dongfang committed
696 697
    int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
    int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
698 699 700 701

    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();
dongfang's avatar
dongfang committed
702 703
    painter.setBrush(instrumentBackground);
    painter.setPen(instrumentEdgePen);
704 705 706
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
707 708
    QPen scalePen(Qt::black);
    scalePen.setWidthF(fineLineWidth);
709

dongfang's avatar
dongfang committed
710
    for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
711 712 713 714 715 716 717
        int displayTick = tickYaw;
        if (displayTick < 0) displayTick+=360;
        else if (displayTick>=360) displayTick-=360;

        // yaw is in center.
        float off = tickYaw - heading;
        // wrap that to ]-180..180]
dongfang's avatar
dongfang committed
718
        if (off<=-180) off+= 360; else if (off>180) off -= 360;
719 720 721 722

        painter.translate(area.center());
        painter.rotate(off);
        bool drewArrow = false;
dongfang's avatar
dongfang committed
723
        bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;
724 725

        if (displayTick==30 || displayTick==60 ||
dongfang's avatar
dongfang committed
726 727
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
728 729 730 731
                displayTick==300 || displayTick==330) {
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
dongfang's avatar
dongfang committed
732 733
            painter.setPen(scalePen);
            drawTextCenter(painter, s_number, /*COMPASS_SCALE_TEXT_SIZE*radius*/ smallTestSize, 0, -innerRadius*0.75);
734
        } else {
dongfang's avatar
dongfang committed
735
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
736 737 738 739 740
                if (displayTick!=0) {
                    QPainterPath markerPath(QPointF(0, -innerRadius*(1-COMPASS_DISK_MARKERHEIGHT/2)));
                    markerPath.lineTo(innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.lineTo(-innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.closeSubpath();
dongfang's avatar
dongfang committed
741
                    painter.setPen(scalePen);
742 743 744 745 746 747 748
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
                if (displayTick%90 == 0) {
                    // Also draw a label
dongfang's avatar
dongfang committed
749 750 751
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
752
                }
dongfang's avatar
dongfang committed
753
            }
754
        }
dongfang's avatar
dongfang committed
755
        // draw the scale lines. If an arrow was drawn, stay off from it.
756

dongfang's avatar
dongfang committed
757 758
        QPointF p_start = drewArrow ? QPoint(0, -innerRadius*0.94) : QPoint(0, -innerRadius);
        QPoint p_end = isMajor ? QPoint(0, -innerRadius*0.86) : QPoint(0, -innerRadius*0.90);
759

dongfang's avatar
dongfang committed
760
        painter.setPen(scalePen);
761 762 763 764
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

dongfang's avatar
dongfang committed
765
    painter.setPen(scalePen);
766 767 768 769 770 771 772 773
    //painter.setBrush(Qt::SolidPattern);
    painter.translate(area.center());
    QPainterPath markerPath(QPointF(0, -radius-2));
    markerPath.lineTo(radius*COMPASS_DISK_MARKERWIDTH/2,  -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.lineTo(-radius*COMPASS_DISK_MARKERWIDTH/2, -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.closeSubpath();
    painter.drawPath(markerPath);

dongfang's avatar
dongfang committed
774 775 776 777 778 779 780 781 782
    qreal digitalCompassYCenter = -radius*0.52;
    qreal digitalCompassHeight = radius*0.28;

    QPointF digitalCompassBottom(0, digitalCompassYCenter+digitalCompassHeight);
    QPointF  digitalCompassAbsoluteBottom = painter.transform().map(digitalCompassBottom);

    qreal digitalCompassUpshift = digitalCompassAbsoluteBottom.y()>height() ? digitalCompassAbsoluteBottom.y()-height() : 0;

    QRectF digitalCompassRect(-radius/3, -radius*0.52-digitalCompassUpshift, radius*2/3, radius*0.28);
783
    painter.setPen(instrumentEdgePen);
dongfang's avatar
dongfang committed
784
    painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
785 786

    /* final safeguard for really stupid systems */
dongfang's avatar
dongfang committed
787
    int digitalCompassValue = static_cast<int>(round(heading)) % 360;
788

dongfang's avatar
dongfang committed
789 790
    QString s_digitalCompass;
    s_digitalCompass.sprintf("%03d", digitalCompassValue);
791

dongfang's avatar
dongfang committed
792 793 794 795
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
796

dongfang's avatar
dongfang committed
797
    drawTextCenter(painter, s_digitalCompass, largeTextSize, 0, -radius*0.38-digitalCompassUpshift);
798

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
//  dummy
//  navigationTargetBearing = 10;
//  navigationCrosstrackError = 500;

    // The CDI
    if (shouldDisplayNavigationData() && navigationTargetBearing != UNKNOWN_ATTITUDE && !isinf(navigationCrosstrackError)) {
        painter.resetTransform();
        painter.translate(area.center());
        // TODO : Sign might be wrong?
        // TODO : The case where error exceeds max. Truncate to max. and make that visible somehow.
        bool errorBeyondRadius = false;
        if (abs(navigationCrosstrackError) > CROSSTRACK_MAX) {
            errorBeyondRadius = true;
            navigationCrosstrackError = navigationCrosstrackError>0 ? CROSSTRACK_MAX : -CROSSTRACK_MAX;
        }

        float r = radius * CROSSTRACK_RADIUS;
        float x = navigationCrosstrackError / CROSSTRACK_MAX * r;
        float y = qSqrt(r*r - x*x); // the positive y, there is also a negative.
dongfang's avatar
dongfang committed
818

819 820 821 822 823 824 825 826 827 828 829 830 831 832
        float sillyHeading = 0;
        float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
        painter.rotate(-angle);

        QPen pen;
        pen.setWidthF(lineWidth);
        pen.setColor(Qt::black);
        painter.setPen(pen);

        painter.drawLine(QPointF(x, y), QPointF(x, -y));
    }
}

/*
dongfang's avatar
dongfang committed
833 834 835 836 837 838 839 840 841 842 843 844 845
void PrimaryFlightDisplay::drawSeparateCompassDisk(QPainter& painter, QRectF area) {
    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();

    painter.setBrush(instrumentOpagueBackground);
    painter.setPen(instrumentEdgePen);
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

    QPen scalePen(Qt::white);
    scalePen.setWidthF(fineLineWidth);

846
    for (int displayTick=0; displayTick<360; displayTick+=COMPASS_SEPARATE_DISK_RESOLUTION) {
dongfang's avatar
dongfang committed
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
        // yaw is in center.
        float off = displayTick - heading;
        // wrap that to ]-180..180]
        if (off<=-180) off+= 360; else if (off>180) off -= 360;

        painter.translate(area.center());
        painter.rotate(off);
        bool drewArrow = false;
        bool isMajor = displayTick % COMPASS_DISK_MAJORTICK == 0;

        if (displayTick==30 || displayTick==60 ||
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
                displayTick==300 || displayTick==330) {
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
            painter.setPen(scalePen);
            drawTextCenter(painter, s_number, mediumTextSize, 0, -innerRadius*0.75);
        } else {
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
                if (displayTick!=0) {
                    QPainterPath markerPath(QPointF(0, -innerRadius*(1-COMPASS_DISK_MARKERHEIGHT/2)));
                    markerPath.lineTo(innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.lineTo(-innerRadius*COMPASS_DISK_MARKERWIDTH/4, -innerRadius);
                    markerPath.closeSubpath();
                    painter.setPen(scalePen);
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
                if (displayTick%90 == 0) {
                    // Also draw a label
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
                }
            }
        }
        // draw the scale lines. If an arrow was drawn, stay off from it.

        QPointF p_start = drewArrow ? QPoint(0, -innerRadius*0.94) : QPoint(0, -innerRadius);
        QPoint p_end = isMajor ? QPoint(0, -innerRadius*0.86) : QPoint(0, -innerRadius*0.90);

        painter.setPen(scalePen);
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

    painter.setPen(scalePen);
    //painter.setBrush(Qt::SolidPattern);
    painter.translate(area.center());
    QPainterPath markerPath(QPointF(0, -radius-2));
    markerPath.lineTo(radius*COMPASS_DISK_MARKERWIDTH/2,  -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.lineTo(-radius*COMPASS_DISK_MARKERWIDTH/2, -radius-radius*COMPASS_DISK_MARKERHEIGHT-2);
    markerPath.closeSubpath();
    painter.drawPath(markerPath);

dongfang's avatar
dongfang committed
906
    QRectF headingNumberRect(-radius/3, radius*0.12, radius*2/3, radius*0.28);
dongfang's avatar
dongfang committed
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
    painter.setPen(instrumentEdgePen);
    painter.drawRoundedRect(headingNumberRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);

    //    if (heading < 0) heading += 360;
    //    else if (heading >= 360) heading -= 360;
    int yawCompass = static_cast<int>(heading) % 360;

    QString yawAngle;
    yawAngle.sprintf("%03d", yawCompass);

    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);

    drawTextCenter(painter, yawAngle, largeTextSize, 0, radius/4);
}
924
*/
dongfang's avatar
dongfang committed
925

926 927 928
void PrimaryFlightDisplay::drawAltimeter(
        QPainter& painter,
        QRectF area, // the area where to draw the tape.
dongfang's avatar
dongfang committed
929
        float primaryAltitude,
930
        float secondaryAltitude,
931
        float vv
932
    ) {
933 934

    painter.resetTransform();
935
    fillInstrumentBackground(painter, area);
936

dongfang's avatar
dongfang committed
937 938
    QPen pen;
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
939

dongfang's avatar
dongfang committed
940 941
    pen.setColor(Qt::white);
    painter.setPen(pen);
942

dongfang's avatar
dongfang committed
943 944
    float h = area.height();
    float w = area.width();
945
    float secondaryAltitudeBoxHeight = mediumTextSize * 2;
dongfang's avatar
dongfang committed
946
    // The height where we being with new tickmarks.
dongfang's avatar
dongfang committed
947
    float effectiveHalfHeight = h*0.45;
948 949 950 951 952

    // not yet implemented: Display of secondary altitude.
    // if (isAirplane())
    //    effectiveHalfHeight-= secondaryAltitudeBoxHeight;

dongfang's avatar
dongfang committed
953 954 955 956 957 958
    float markerHalfHeight = mediumTextSize*0.8;
    float leftEdge = instrumentEdgePen.widthF()*2;
    float rightEdge = w-leftEdge;
    float tickmarkLeft = leftEdge;
    float tickmarkRightMajor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MAJOR*w;
    float tickmarkRightMinor = tickmarkLeft+TAPE_GAUGES_TICKWIDTH_MINOR*w;
dongfang's avatar
dongfang committed
959
    float numbersLeft = 0.42*w;
dongfang's avatar
dongfang committed
960 961
    float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
    float scaleCenterAltitude = primaryAltitude == UNKNOWN_ALTITUDE ? 0 : primaryAltitude;
962 963 964 965

    // altitude scale
#ifdef ALTIMETER_PROJECTED
    float range = 1.2;
dongfang's avatar
dongfang committed
966 967
    float start = scaleCenterAltitude - ALTIMETER_PROJECTED_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_PROJECTED_SPAN/2;
968 969 970 971 972 973 974 975 976 977
    int firstTick = ceil(start / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
    int lastTick = floor(end / ALTIMETER_PROJECTED_RESOLUTION) * ALTIMETER_PROJECTED_RESOLUTION;
    for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_PROJECTED_RESOLUTION) {
        // a number between 0 and 1. Use as radians directly.
        float r = range*(tickAlt-altitude)/(ALTIMETER_PROJECTED_SPAN/2);
        float y = effectiveHalfHeight * sin(r);
        scale = cos(r);
        if (scale<0) scale = -scale;
        bool hasText = tickAlt % ALTIMETER_PROJECTED_MAJOR_RESOLUTION == 0;
#else
dongfang's avatar
dongfang committed
978 979
    float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
980 981 982
    int firstTick = ceil(start / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
    int lastTick = floor(end / ALTIMETER_LINEAR_RESOLUTION) * ALTIMETER_LINEAR_RESOLUTION;
    for (int tickAlt = firstTick; tickAlt <= lastTick; tickAlt += ALTIMETER_LINEAR_RESOLUTION) {
dongfang's avatar
dongfang committed
983 984
        float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
        bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
985 986 987
#endif
        painter.resetTransform();
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
988 989 990 991
        pen.setColor(tickAlt<0 ? redColor : Qt::white);
        painter.setPen(pen);
        if (isMajor) {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
992
            QString s_alt;
dongfang's avatar
dongfang committed
993
            s_alt.sprintf("%d", abs(tickAlt));
dongfang's avatar
dongfang committed
994
            drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
dongfang's avatar
dongfang committed
995 996
        } else {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
    markerPath.lineTo(markerTip+markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(rightEdge, markerHalfHeight);
    markerPath.lineTo(rightEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip+markerHalfHeight, -markerHalfHeight);
    markerPath.closeSubpath();

    painter.resetTransform();
    painter.translate(area.left(), area.center().y());
dongfang's avatar
dongfang committed
1009

dongfang's avatar
dongfang committed
1010
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
1011 1012 1013
    pen.setColor(Qt::white);
    painter.setPen(pen);

1014 1015 1016 1017
    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
1018 1019
    pen.setColor(Qt::white);
    painter.setPen(pen);
dongfang's avatar
dongfang committed
1020

1021
    QString s_alt;
dongfang's avatar
dongfang committed
1022 1023 1024 1025 1026
    if(primaryAltitude == UNKNOWN_ALTITUDE)
        s_alt.sprintf("---");
    else
        s_alt.sprintf("%3.0f", primaryAltitude);

1027
    float xCenter = (markerTip+rightEdge)/2;
dongfang's avatar
dongfang committed
1028
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
dongfang's avatar
dongfang committed
1029 1030

    if (vv == UNKNOWN_ALTITUDE) return;
1031 1032
    float vvPixHeight = -vv/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
    if (abs (vvPixHeight)<markerHalfHeight) return; // hidden behind marker.
dongfang's avatar
dongfang committed
1033

1034
    float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
dongfang's avatar
dongfang committed
1035 1036 1037

    // QRectF vvRect(rightEdge - w*ALTIMETER_VVI_WIDTH, markerHalfHeight*vvSign, w*ALTIMETER_VVI_WIDTH, abs(vvPixHeight)*vvSign);
    QPointF vvArrowBegin(rightEdge - w*ALTIMETER_VVI_WIDTH/2, markerHalfHeight*vvSign);
1038
    QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
dongfang's avatar
dongfang committed
1039 1040
    painter.drawLine(vvArrowBegin, vvArrowEnd);

1041 1042 1043 1044 1045 1046 1047
    // Yeah this is a repitition of above code but we are goigd to trash it all anyway, so no fix.
    float vvArowHeadSize = abs(vvPixHeight - markerHalfHeight*vvSign);
    if (vvArowHeadSize > w*ALTIMETER_VVI_WIDTH/3) vvArowHeadSize = w*ALTIMETER_VVI_WIDTH/3;

    float xcenter = rightEdge-w*ALTIMETER_VVI_WIDTH/2;

    QPointF vvArrowHead(xcenter+vvArowHeadSize, vvPixHeight - vvSign *vvArowHeadSize);
dongfang's avatar
dongfang committed
1048 1049
    painter.drawLine(vvArrowHead, vvArrowEnd);

1050
    vvArrowHead = QPointF(xcenter-vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize);
dongfang's avatar
dongfang committed
1051
    painter.drawLine(vvArrowHead, vvArrowEnd);
1052 1053
}

1054 1055
void PrimaryFlightDisplay::drawVelocityMeter(
        QPainter& painter,
1056 1057 1058
        QRectF area,
        float speed,
        float secondarySpeed
1059 1060 1061
        ) {

    painter.resetTransform();
1062
    fillInstrumentBackground(painter, area);
1063 1064 1065 1066 1067 1068 1069

    QPen pen;
    pen.setWidthF(lineWidth);

    float h = area.height();
    float w = area.width();
    float effectiveHalfHeight = h*0.45;
dongfang's avatar
dongfang committed
1070
    float markerHalfHeight = mediumTextSize;
dongfang's avatar
dongfang committed
1071
    float leftEdge = instrumentEdgePen.widthF()*2;
dongfang's avatar
dongfang committed
1072 1073 1074 1075 1076 1077 1078 1079 1080
    float tickmarkRight = w-leftEdge;
    float tickmarkLeftMajor = tickmarkRight-w*TAPE_GAUGES_TICKWIDTH_MAJOR;
    float tickmarkLeftMinor = tickmarkRight-w*TAPE_GAUGES_TICKWIDTH_MINOR;
    float numbersRight = 0.42*w;
    float markerTip = (tickmarkLeftMajor+tickmarkRight*2)/3;

    // Select between air and ground speed:

    float centerScaleSpeed =
1081
            speed == UNKNOWN_SPEED ? 0 : speed;
dongfang's avatar
dongfang committed
1082 1083 1084

    float start = centerScaleSpeed - AIRSPEED_LINEAR_SPAN/2;
    float end = centerScaleSpeed + AIRSPEED_LINEAR_SPAN/2;
1085 1086 1087 1088

    int firstTick = ceil(start / AIRSPEED_LINEAR_RESOLUTION) * AIRSPEED_LINEAR_RESOLUTION;
    int lastTick = floor(end / AIRSPEED_LINEAR_RESOLUTION) * AIRSPEED_LINEAR_RESOLUTION;
    for (int tickSpeed = firstTick; tickSpeed <= lastTick; tickSpeed += AIRSPEED_LINEAR_RESOLUTION) {
dongfang's avatar
dongfang committed
1089 1090 1091 1092
        pen.setColor(tickSpeed<0 ? redColor : Qt::white);
        painter.setPen(pen);

        float y = (tickSpeed-centerScaleSpeed)*effectiveHalfHeight/(AIRSPEED_LINEAR_SPAN/2);
1093 1094
        bool hasText = tickSpeed % AIRSPEED_LINEAR_MAJOR_RESOLUTION == 0;
        painter.resetTransform();
dongfang's avatar
dongfang committed
1095

1096
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
1097

1098
        if (hasText) {
dongfang's avatar
dongfang committed
1099
            painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0);
1100
            QString s_speed;
dongfang's avatar
dongfang committed
1101
            s_speed.sprintf("%d", abs(tickSpeed));
dongfang's avatar
dongfang committed
1102
            drawTextRightCenter(painter, s_speed, mediumTextSize, numbersRight, 0);
dongfang's avatar
dongfang committed
1103 1104
        } else {
            painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0);
1105 1106 1107 1108
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
dongfang's avatar
dongfang committed
1109 1110 1111 1112
    markerPath.lineTo(markerTip-markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(leftEdge, markerHalfHeight);
    markerPath.lineTo(leftEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip-markerHalfHeight, -markerHalfHeight);
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
    markerPath.closeSubpath();

    painter.resetTransform();
    painter.translate(area.left(), area.center().y());

    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);

    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

    pen.setColor(Qt::white);
    painter.setPen(pen);
    QString s_alt;
1129
    if (speed == UNKNOWN_SPEED)
dongfang's avatar
dongfang committed
1130 1131
        s_alt.sprintf("---");
    else
1132
        s_alt.sprintf("%3.1f", speed);
dongfang's avatar
dongfang committed
1133
    float xCenter = (markerTip+leftEdge)/2;
1134 1135 1136
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
}

dongfang's avatar
dongfang committed
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191

#define TOP         (1<<0)
#define BOTTOM      (1<<1)
#define LEFT        (1<<2)
#define RIGHT       (1<<3)

#define TOP_2       (1<<4)
#define BOTTOM_2    (1<<5)
#define LEFT_2      (1<<6)
#define RIGHT_2     (1<<7)

void applyMargin(QRectF& area, float margin, int where) {
    if (margin < 0.01) return;

    QRectF save(area);
    qreal consumed;

    if (where & LEFT) {
        area.setX(save.x() + (consumed = margin));
    } else if (where & LEFT_2) {
        area.setX(save.x() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & RIGHT) {
        area.setWidth(save.width()-consumed-margin);
    } else if (where & RIGHT_2) {
        area.setWidth(save.width()-consumed-margin/2);
    } else {
        area.setWidth(save.width()-consumed);
    }

    if (where & TOP) {
        area.setY(save.y() + (consumed = margin));
    } else if (where & TOP_2) {
        area.setY(save.y() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & BOTTOM) {
        area.setHeight(save.height()-consumed-margin);
    } else if (where & BOTTOM_2) {
        area.setHeight(save.height()-consumed-margin/2);
    } else {
        area.setHeight(save.height()-consumed);
    }
}

void setMarginsForInlineLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_2);
    applyMargin(panel2, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel3, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel4, margin, BOTTOM|LEFT_2|RIGHT);
1192 1193
}

dongfang's avatar
dongfang committed
1194 1195 1196 1197 1198 1199 1200 1201 1202
void setMarginsForCornerLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_2);
    applyMargin(panel2, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel3, margin, BOTTOM|LEFT_2|RIGHT_2);
    applyMargin(panel4, margin, BOTTOM|LEFT_2|RIGHT);
}

inline qreal tapesGaugeWidthFor(qreal containerWidth, qreal preferredAIWidth) {
    qreal result = (containerWidth - preferredAIWidth) / 2.0f;
dongfang's avatar
dongfang committed
1203
    qreal minimum = containerWidth / 5.5f;
dongfang's avatar
dongfang committed
1204 1205 1206 1207 1208
    if (result < minimum) result = minimum;
    return result;
}

void PrimaryFlightDisplay::doPaint() {
1209 1210 1211 1212 1213
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

dongfang's avatar
dongfang committed
1214
    qreal margin = height()/100.0f;
1215

1216 1217 1218 1219 1220
    // The AI centers on this area.
    QRectF AIMainArea;
    // The AI paints on this area. It should contain the AIMainArea.
    QRectF AIPaintArea;

dongfang's avatar
dongfang committed
1221 1222 1223 1224 1225 1226 1227
    QRectF compassArea;
    QRectF altimeterArea;
    QRectF velocityMeterArea;
    QRectF sensorsStatsArea;
    QRectF linkStatsArea;
    QRectF sysStatsArea;
    QRectF missionStatsArea;
1228 1229

    painter.fillRect(rect(), Qt::black);
dongfang's avatar
dongfang committed
1230 1231
    qreal tapeGaugeWidth;

dongfang's avatar
dongfang committed
1232 1233 1234
    qreal compassHalfSpan = 180;
    float compassAIIntrusion = 0;

dongfang's avatar
dongfang committed
1235
    switch(layout) {
1236
    /*
dongfang's avatar
dongfang committed
1237 1238 1239 1240 1241 1242
    case FEATUREPANELS_IN_CORNERS: {
        tapeGaugeWidth = tapesGaugeWidthFor(width(), height());

        // A layout optimal for a container wider than it is high.
        // The AI gets full height and if tapes are transparent, also full width. If tapes are opague, then
        // the AI gets a width to be perfectly square.
1243 1244
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1245
                    0,
1246
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth * 2: width(),
dongfang's avatar
dongfang committed
1247 1248
                    height());

1249 1250
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1251 1252 1253 1254
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.

        qreal sidePanelsHeight = height();

1255
        altimeterArea = QRectF(AIMainArea.right(), height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);
dongfang's avatar
dongfang committed
1256 1257 1258
        velocityMeterArea = QRectF (0, height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);

        sensorsStatsArea = QRectF(0, 0, tapeGaugeWidth, sidePanelsHeight/5);
1259
        linkStatsArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1260
        sysStatsArea = QRectF(0, sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
1261
        missionStatsArea =QRectF(AIMainArea.right(), sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1262

1263 1264
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1265 1266 1267 1268
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1269

dongfang's avatar
dongfang committed
1270
        // Compass is inside the AI ans within its margins also.
1271 1272
        compassArea = QRectF(AIMainArea.x()+AIMainArea.width()*0.60, AIMainArea.y()+AIMainArea.height()*0.80,
                             AIMainArea.width()/2, AIMainArea.width()/2);
dongfang's avatar
dongfang committed
1273 1274
        break;
    }
1275

dongfang's avatar
dongfang committed
1276 1277 1278 1279
    case FEATUREPANELS_AT_BOTTOM: {
        // A layout for containers with about the same width and height.
        // qreal minor = min(width(), height());
        // qreal major = max(width(), height());
1280

dongfang's avatar
dongfang committed
1281
        qreal aiheight = height()*4.0f/5;
1282

dongfang's avatar
dongfang committed
1283
        tapeGaugeWidth = tapesGaugeWidthFor(width(), aiheight);
1284

1285 1286
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1287
                    0,
1288
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth*2 : width(),
dongfang's avatar
dongfang committed
1289
                    aiheight);
1290

1291 1292
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1293
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.
1294
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1295
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1296

dongfang's avatar
dongfang committed
1297 1298
        qreal panelsHeight = height() / 5.0f;
        qreal panelsWidth = width() / 4.0f;
1299

1300 1301 1302 1303
        sensorsStatsArea = QRectF(0, AIMainArea.bottom(), panelsWidth, panelsHeight);
        linkStatsArea = QRectF(panelsWidth, AIMainArea.bottom(), panelsWidth, panelsHeight);
        sysStatsArea = QRectF(panelsWidth*2, AIMainArea.bottom(), panelsWidth, panelsHeight);
        missionStatsArea =QRectF(panelsWidth*3, AIMainArea.bottom(), panelsWidth, panelsHeight);
1304

1305 1306
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1307 1308 1309 1310
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1311

dongfang's avatar
dongfang committed
1312
        // Compass is inside the AI ans within its margins also.
1313 1314
        compassArea = QRectF(AIMainArea.x()+AIMainArea.width()*0.60, AIMainArea.y()+AIMainArea.height()*0.80,
                             AIMainArea.width()/2, AIMainArea.width()/2);
dongfang's avatar
dongfang committed
1315 1316
        break;
    }
1317

1318 1319 1320
    */

    case COMPASS_INTEGRATED: {
dongfang's avatar
dongfang committed
1321
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());
1322
        qreal aiheight = height();
dongfang's avatar
dongfang committed
1323 1324
        qreal aiwidth = width()-tapeGaugeWidth*2;
        if (aiheight > aiwidth) aiheight = aiwidth;
1325 1326 1327 1328

        AIMainArea = QRectF(
                    tapeGaugeWidth,
                    0,
dongfang's avatar
dongfang committed
1329
                    aiwidth,
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348
                    aiheight);

        AIPaintArea = QRectF(
                    0,
                    0,
                    width(),
                    height());

        // Tape gauges get so much width that the AI area not covered by them is perfectly square.
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);

        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }

dongfang's avatar
dongfang committed
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
        qreal compassRelativeWidth = 0.75;
        qreal compassBottomMargin = 0.78;

        qreal compassSize = compassRelativeWidth  * AIMainArea.width();  // Diameter is this times the width.

        qreal compassCenterY;
        //if (heightSurplus >= 0) compassCenterY =  AIMainArea.bottom() + compassSize/2;
        compassCenterY = AIMainArea.bottom() + compassSize / 4;

        if (height() - compassCenterY > AIMainArea.width()/2*compassBottomMargin)
            compassCenterY = height()-AIMainArea.width()/2*compassBottomMargin;

        // TODO: This is bad style...
        compassCenterY = (compassCenterY * 2 + AIMainArea.bottom() + compassSize / 4) / 3;

        compassArea = QRectF(AIMainArea.x()+(1-compassRelativeWidth)/2*AIMainArea.width(),
                             compassCenterY-compassSize/2,
                             compassSize,
                             compassSize);

        if (height()-compassCenterY < compassSize/2) {
            compassHalfSpan = acos((compassCenterY-height())*2/compassSize) * 180/M_PI + COMPASS_DISK_RESOLUTION;
            if (compassHalfSpan > 180) compassHalfSpan = 180;
        }

        compassAIIntrusion = compassSize/2 + AIMainArea.bottom() - compassCenterY;
        if (compassAIIntrusion<0) compassAIIntrusion = 0;

1377 1378
        break;
    }
dongfang's avatar
dongfang committed
1379 1380 1381 1382
    case COMPASS_SEPARATED: {
        // A layout for containers higher than their width.
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());

1383 1384
        qreal aiheight = width() - tapeGaugeWidth*2;
        qreal panelsHeight = 0;
dongfang's avatar
dongfang committed
1385

1386 1387
        AIMainArea = QRectF(
                    tapeGaugeWidth,
dongfang's avatar
dongfang committed
1388
                    0,
1389
                    width()-tapeGaugeWidth*2,
dongfang's avatar
dongfang committed
1390 1391
                    aiheight);

1392 1393 1394 1395 1396 1397 1398
        AIPaintArea = style == OVERLAY_HSI ?
                    QRectF(
                    0,
                    0,
                    width(),
                    height() - panelsHeight) : AIMainArea;

dongfang's avatar
dongfang committed
1399
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1400
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1401

1402
        /*
dongfang's avatar
dongfang committed
1403
        qreal panelsWidth = width() / 4.0f;
dongfang's avatar
dongfang committed
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
        if(remainingHeight > width()) {
            // very tall layout, place panels below compass.
            sensorsStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
            linkStatsArea = QRectF(panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
            sysStatsArea = QRectF(panelsWidth*2, height()-panelsHeight, panelsWidth, panelsHeight);
            missionStatsArea =QRectF(panelsWidth*3, height()-panelsHeight, panelsWidth, panelsHeight);
            if (style == OPAGUE_TAPES) {
                setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
            }
            compassCenter = QPoint(width()/2, (AIArea.bottom()+height()-panelsHeight)/2);
dongfang's avatar
dongfang committed
1414
            maxCompassDiam = fmin(width(), height()-AIArea.height()-panelsHeight);
dongfang's avatar
dongfang committed
1415 1416
        } else {
            // Remaining part is wider than high; place panels in corners around compass
1417
            // Naaah it is really ugly, do not do that.
dongfang's avatar
dongfang committed
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
            sensorsStatsArea = QRectF(0, AIArea.bottom(), panelsWidth, panelsHeight);
            linkStatsArea = QRectF(width()-panelsWidth, AIArea.bottom(), panelsWidth, panelsHeight);
            sysStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
            missionStatsArea =QRectF(width()-panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
            if (style == OPAGUE_TAPES) {
                setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
            }
            compassCenter = QPoint(width()/2, (AIArea.bottom()+height())/2);
            // diagonal between 2 panel corners
            qreal xd = width()-panelsWidth*2;
            qreal yd = height()-panelsHeight - AIArea.bottom();
dongfang's avatar
dongfang committed
1429 1430 1431 1432 1433 1434 1435 1436
            maxCompassDiam = qSqrt(xd*xd + yd*yd);
            if (maxCompassDiam > remainingHeight) {
                maxCompassDiam = width() - panelsWidth*2;
                if (maxCompassDiam > remainingHeight) {
                    // If still too large, lower.
                    // compassCenter.setY();
                }
            }
dongfang's avatar
dongfang committed
1437
        }
1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
        */
        /*
        sensorsStatsArea = QRectF(0, height()-panelsHeight, panelsWidth, panelsHeight);
        linkStatsArea = QRectF(panelsWidth, height()-panelsHeight, panelsWidth, panelsHeight);
        sysStatsArea = QRectF(panelsWidth*2, height()-panelsHeight, panelsWidth, panelsHeight);
        missionStatsArea =QRectF(panelsWidth*3, height()-panelsHeight, panelsWidth, panelsHeight);
        */

        QPoint compassCenter = QPoint(width()/2, AIMainArea.bottom()+width()/2);
        qreal compassDiam = width() * 0.8;
dongfang's avatar
dongfang committed
1448
        compassArea = QRectF(compassCenter.x()-compassDiam/2, compassCenter.y()-compassDiam/2, compassDiam, compassDiam);
dongfang's avatar
dongfang committed
1449 1450 1451
        break;
    }
    }
1452

dongfang's avatar
dongfang committed
1453
    bool hadClip = painter.hasClipping();
dongfang's avatar
dongfang committed
1454

dongfang's avatar
dongfang committed
1455
    painter.setClipping(true);
1456
    painter.setClipRect(AIPaintArea);
1457

1458
    drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea);
dongfang's avatar
dongfang committed
1459
    drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion);
1460
    drawAIAirframeFixedFeatures(painter, AIMainArea);
1461

1462 1463 1464
   // if(layout ==COMPASS_SEPARATED)
        //drawSeparateCompassDisk(painter, compassArea);
   // else
dongfang's avatar
dongfang committed
1465
        drawAICompassDisk(painter, compassArea, compassHalfSpan);
1466

dongfang's avatar
dongfang committed
1467
    painter.setClipping(hadClip);
1468

1469
    drawAltimeter(painter, altimeterArea, primaryAltitude, GPSAltitude, verticalVelocity);
1470

1471
    drawVelocityMeter(painter, velocityMeterArea, primarySpeed, groundspeed);
1472

1473
    /*
dongfang's avatar
dongfang committed
1474 1475 1476 1477
    drawSensorsStatsPanel(painter, sensorsStatsArea);
    drawLinkStatsPanel(painter, linkStatsArea);
    drawSysStatsPanel(painter, sysStatsArea);
    drawMissionStatsPanel(painter, missionStatsArea);
1478 1479
    */
    /*
dongfang's avatar
dongfang committed
1480 1481 1482
    if (style == OPAGUE_TAPES) {
        drawInstrumentBackground(painter, AIArea);
    }
1483
    */
dongfang's avatar
dongfang committed
1484

dongfang's avatar
dongfang committed
1485
    painter.end();
1486
}
1487 1488

void PrimaryFlightDisplay:: createActions() {}