PrimaryFlightDisplay.cc 52.5 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 17 18 19
static const float SEPARATE_COMPASS_ASPECTRATIO = 3.0f/4.0f;
static const float LINEWIDTH = 0.0036f;
static const float SMALL_TEXT_SIZE = 0.028f;
static const float MEDIUM_TEXT_SIZE = SMALL_TEXT_SIZE*1.2f;
static const float LARGE_TEXT_SIZE = MEDIUM_TEXT_SIZE*1.2f;
20

21
static const bool SHOW_ZERO_ON_SCALES = true;
22 23

// all in units of display height
24 25 26 27
static const float ROLL_SCALE_RADIUS = 0.42f;
static const float ROLL_SCALE_TICKMARKLENGTH = 0.04f;
static const float ROLL_SCALE_MARKERWIDTH = 0.06f;
static const float ROLL_SCALE_MARKERHEIGHT = 0.04f;
28
// scale max. degrees
29
static const int ROLL_SCALE_RANGE = 60;
30 31

// fraction of height to translate for each degree of pitch.
32 33 34 35 36
static const float PITCHTRANSLATION = 65;
// 5 degrees for each line
static const int PITCH_SCALE_RESOLUTION = 5;
static const float PITCH_SCALE_MAJORWIDTH = 0.1f;
static const float PITCH_SCALE_MINORWIDTH = 0.066;
37 38 39 40

// Beginning from PITCH_SCALE_WIDTHREDUCTION_FROM degrees of +/- pitch, the
// width of the lines is reduced, down to PITCH_SCALE_WIDTHREDUCTION times
// the normal width. This helps keep orientation in extreme attitudes.
41 42
static const int PITCH_SCALE_WIDTHREDUCTION_FROM = 30;
static const float PITCH_SCALE_WIDTHREDUCTION = 0.3f;
43

44
static const int PITCH_SCALE_HALFRANGE = 15;
45 46 47 48 49

// The number of degrees to either side of the heading to draw the compass disk.
// 180 is valid, this will draw a complete disk. If the disk is partly clipped
// away, less will do.

50 51 52 53 54 55 56 57
static const int  COMPASS_DISK_MAJORTICK = 10;
static const int  COMPASS_DISK_ARROWTICK = 45;
static const float COMPASS_DISK_MAJORLINEWIDTH = 0.006;
static const float COMPASS_DISK_MINORLINEWIDTH = 0.004;
static const int  COMPASS_DISK_RESOLUTION = 10;
static const float COMPASS_SEPARATE_DISK_RESOLUTION = 5;
static const float COMPASS_DISK_MARKERWIDTH = 0.2;
static const float COMPASS_DISK_MARKERHEIGHT = 0.133;
58

59 60
static const int  CROSSTRACK_MAX = 1000;
static const float CROSSTRACK_RADIUS = 0.6;
61

62 63
static const float TAPE_GAUGES_TICKWIDTH_MAJOR = 0.25;
static const float TAPE_GAUGES_TICKWIDTH_MINOR = 0.15;
64 65

// The altitude difference between top and bottom of scale
66
static const int ALTIMETER_LINEAR_SPAN = 50;
67
// every 5 meters there is a tick mark
68
static const int ALTIMETER_LINEAR_RESOLUTION = 5;
69
// every 10 meters there is a number
70
static const int ALTIMETER_LINEAR_MAJOR_RESOLUTION = 10;
71 72 73

// Projected: An experiment. Make tape appear projected from a cylinder, like a French "drum" style gauge.
// The altitude difference between top and bottom of scale
74
static const int ALTIMETER_PROJECTED_SPAN = 50;
75
// every 5 meters there is a tick mark
76
static const int ALTIMETER_PROJECTED_RESOLUTION = 5;
77
// every 10 meters there is a number
78
static const int ALTIMETER_PROJECTED_MAJOR_RESOLUTION = 10;
79 80 81
// min. and max. vertical velocity

// min. and max. vertical velocity
82 83
static const int ALTIMETER_VVI_SPAN = 5;
static const float ALTIMETER_VVI_WIDTH = 0.2f;
84 85

// Now the same thing for airspeed!
86 87 88
static const int AIRSPEED_LINEAR_SPAN = 15;
static const int AIRSPEED_LINEAR_RESOLUTION = 1;
static const int AIRSPEED_LINEAR_MAJOR_RESOLUTION = 5;
89

90 91
/*
 *@TODO:
dongfang's avatar
dongfang committed
92
 * global fixed pens (and painters too?)
93 94 95
 * repaint on demand multiple canvases
 * multi implementation with shared model class
 */
dongfang's avatar
dongfang committed
96
double PrimaryFlightDisplay_round(double value, int digits=0)
97
{
98
    return floor(value * pow(10.0, digits) + 0.5) / pow(10.0, digits);
99
}
100

101 102 103 104 105 106
qreal PrimaryFlightDisplay_constrain(qreal value, qreal min, qreal max) {
    if (value<min) value=min;
    else if(value>max) value=max;
    return value;
}

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
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),

122
    uas(NULL),
123

124 125 126
    roll(0),
    pitch(0),
    heading(0),
127

Don Gagne's avatar
Don Gagne committed
128 129
    altitudeAMSL(std::numeric_limits<double>::quiet_NaN()),
    altitudeRelative(std::numeric_limits<double>::quiet_NaN()),
130

Don Gagne's avatar
Don Gagne committed
131 132 133
    groundSpeed(std::numeric_limits<double>::quiet_NaN()),
    airSpeed(std::numeric_limits<double>::quiet_NaN()),
    climbRate(std::numeric_limits<double>::quiet_NaN()),
134

135
    navigationCrosstrackError(0),
Don Gagne's avatar
Don Gagne committed
136
    navigationTargetBearing(std::numeric_limits<double>::quiet_NaN()),
137

138 139
    layout(COMPASS_INTEGRATED),
    style(OVERLAY_HSI),
140

dongfang's avatar
dongfang committed
141
    redColor(QColor::fromHsvF(0, 0.75, 0.9)),
dongfang's avatar
dongfang committed
142 143 144 145 146 147 148
    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
149
    instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
150 151 152 153
    instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0)),

    font("Bitstream Vera Sans"),
    refreshTimer(new QTimer(this))
dongfang's avatar
dongfang committed
154
{
155 156
    Q_UNUSED(width);
    Q_UNUSED(height);
157

158 159 160
    setMinimumSize(120, 80);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

Lorenz Meier's avatar
Lorenz Meier committed
161 162
    setActiveUAS(UASManager::instance()->getActiveUAS());

163
    // Connect with UAS signal
164 165
    //connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(addUAS(UASInterface*)));
    connect(UASManager::instance(), SIGNAL(UASDeleted(UASInterface*)), this, SLOT(forgetUAS(UASInterface*)));
166
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
167

168 169
    // Refresh timer
    refreshTimer->setInterval(updateInterval);
dongfang's avatar
dongfang committed
170
    //    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
171
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
}

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
203 204 205 206
void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
    QWidget::resizeEvent(e);

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

209 210
    lineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH, 1, 6);
    fineLineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH*2/3, 1, 2);
dongfang's avatar
dongfang committed
211 212 213 214

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

215
    smallTextSize = size * SMALL_TEXT_SIZE;
dongfang's avatar
dongfang committed
216 217 218
    mediumTextSize = size * MEDIUM_TEXT_SIZE;
    largeTextSize = size * LARGE_TEXT_SIZE;

dongfang's avatar
dongfang committed
219 220
    /*
     * Try without layout Change-O-Matic. It was too complicated.
221 222
    qreal aspect = e->size().width() / e->size().height();
    if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
dongfang's avatar
dongfang committed
223 224
        layout = COMPASS_SEPARATED;
    else
225
        layout = COMPASS_INTEGRATED;
dongfang's avatar
dongfang committed
226
    */
227
    // qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang's avatar
dongfang committed
228
}
229

dongfang's avatar
dongfang committed
230 231 232 233 234
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    doPaint();
}
235

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
///*
// * Interface towards qgroundcontrol
// */
//void PrimaryFlightDisplay::addUAS(UASInterface* uas)
//{
//    if (uas)
//    {
//        if (!this->uas)
//        {
//            setActiveUAS(uas);
//        }
//    }
//}

void PrimaryFlightDisplay::forgetUAS(UASInterface* uas)
251
{
252
    if (this->uas != NULL && this->uas == uas) {
253
        // Disconnect any previously connected active MAV
254 255 256 257 258
        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)));
        disconnect(this->uas, SIGNAL(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
        disconnect(this->uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
        disconnect(this->uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
259
    }
260 261 262 263 264 265 266 267
}

/**
 *
 * @param uas the UAS/MAV to monitor/display with the HUD
 */
void PrimaryFlightDisplay::setActiveUAS(UASInterface* uas)
{
tstellanova's avatar
tstellanova committed
268 269 270
    if (uas == this->uas)
        return; //no need to rewire

271 272
    // Disconnect the previous one (if any)
    forgetUAS(this->uas);
273 274 275 276 277 278

    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)));
279 280
        connect(uas, SIGNAL(speedChanged(UASInterface*, double, double, quint64)), this, SLOT(updateSpeed(UASInterface*, double, double, quint64)));
        connect(uas, SIGNAL(altitudeChanged(UASInterface*, double, double, double, quint64)), this, SLOT(updateAltitude(UASInterface*, double, double, double, quint64)));
281
        connect(uas, SIGNAL(navigationControllerErrorsChanged(UASInterface*, double, double, double)), this, SLOT(updateNavigationControllerErrors(UASInterface*, double, double, double)));
282 283 284 285 286 287 288 289 290 291

        // 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);
292
        // Called from UAS.cc l. 616
293
        if (isinf(roll)) {
Don Gagne's avatar
Don Gagne committed
294
            this->roll = std::numeric_limits<double>::quiet_NaN();
295 296 297
        } else {
            this->roll = roll * (180.0 / M_PI);
        }
298

299
        if (isinf(pitch)) {
Don Gagne's avatar
Don Gagne committed
300
            this->pitch = std::numeric_limits<double>::quiet_NaN();
301 302 303 304
        } else {
            this->pitch = pitch * (180.0 / M_PI);
        }

305
        if (isinf(yaw)) {
Don Gagne's avatar
Don Gagne committed
306
            this->heading = std::numeric_limits<double>::quiet_NaN();
307 308 309 310 311
        } else {
            yaw = yaw * (180.0 / M_PI);
            if (yaw<0) yaw+=360;
            this->heading = yaw;
        }
Lorenz Meier's avatar
Lorenz Meier committed
312

313 314 315 316 317
}

void PrimaryFlightDisplay::updateAttitude(UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp)
{
    Q_UNUSED(component);
318
    this->updateAttitude(uas, roll, pitch, yaw, timestamp);
319 320
}

321
void PrimaryFlightDisplay::updateSpeed(UASInterface* uas, double _groundSpeed, double _airSpeed, quint64 timestamp)
322 323 324 325
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

326 327
    groundSpeed = _groundSpeed;
    airSpeed = _airSpeed;
328
}
329

330
void PrimaryFlightDisplay::updateAltitude(UASInterface* uas, double _altitudeAMSL, double _altitudeRelative, double _climbRate, quint64 timestamp) {
331 332
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
333 334 335
    altitudeAMSL = _altitudeAMSL;
    altitudeRelative = _altitudeRelative;
    climbRate = _climbRate;
336 337
}

338 339 340 341 342 343 344 345
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
    Q_UNUSED(uas);
    this->navigationAltitudeError = altitudeError;
    this->navigationSpeedError = speedError;
    this->navigationCrosstrackError = xtrackError;
}


346 347 348
/*
 * Private and such
 */
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

// 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;
    }
}

365 366 367 368 369 370 371
// 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;
}

372 373 374
void PrimaryFlightDisplay::drawTextCenter (
        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::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
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::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
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
    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
423
        float pixelSize,
424 425 426
        float x,
        float y)
{
dongfang's avatar
dongfang committed
427
    font.setPixelSize(pixelSize);
428 429 430 431 432 433 434 435 436 437 438
    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
439
        float pixelSize,
440 441 442
        float x,
        float y)
{
dongfang's avatar
dongfang committed
443
    font.setPixelSize(pixelSize);
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
    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
464 465 466 467 468 469 470
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentOpagueBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

471
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
472
    if (isnan(pitch))
473
        return 0;
474
    return pitch * viewHeight / PITCHTRANSLATION;
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
}

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
490
    QPen pen;
491
    pen.setWidthF(lineWidth * 1.5f);
dongfang's avatar
dongfang committed
492 493
    pen.setColor(redColor);
    painter.setPen(pen);
494

495 496
    float length = 0.15f;
    float side = 0.5f;
dongfang's avatar
dongfang committed
497 498 499 500
    // 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));

501
    float rel = length/qSqrt(2.0f);
dongfang's avatar
dongfang committed
502 503 504 505 506 507 508 509
    // 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);
510 511 512 513
    markerPath.closeSubpath();
    painter.drawPath(markerPath);
}

514 515 516 517 518 519 520 521 522 523 524 525 526 527
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;
}

528 529
void PrimaryFlightDisplay::drawAIGlobalFeatures(
        QPainter& painter,
530 531
        QRectF mainArea,
        QRectF paintArea) {
532

533
    float displayRoll = this->roll;
534
    if (isnan(displayRoll))
535 536
        displayRoll = 0;

537
    painter.resetTransform();
538
    painter.translate(mainArea.center());
539

540 541
    qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
    qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
542

543 544
    //painter.rotate(-roll);
    //painter.translate(0, pitchPixels);
545

546 547
    //    QTransform forwardTransform;
    //forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
548
    painter.rotate(-displayRoll);
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    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);
571 572
    skyPath.closeSubpath();

573
    QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
574 575 576 577 578
    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);

579 580 581 582
    QPainterPath groundPath(hzonRight);
    groundPath.lineTo(maxx, maxy);
    groundPath.lineTo(minx, maxy);
    groundPath.lineTo(hzonLeft);
583 584
    groundPath.closeSubpath();

585
    QLinearGradient groundGradient(0, gradientEnd, 0, 0);
586 587 588 589 590
    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
591 592 593 594 595
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(greenColor);
    painter.setPen(pen);

596 597
    QPointF start(-mainArea.width(), 0);
    QPoint end(mainArea.width(), 0);
598 599 600 601 602 603
    painter.drawLine(start, end);
}

void PrimaryFlightDisplay::drawPitchScale(
        QPainter& painter,
        QRectF area,
dongfang's avatar
dongfang committed
604
        float intrusion,
605 606 607 608
        bool drawNumbersLeft,
        bool drawNumbersRight
        ) {

609
    float displayPitch = this->pitch;
610
    if (isnan(displayPitch))
611 612
        displayPitch = 0;

dongfang's avatar
dongfang committed
613 614 615
    // The area should be quadratic but if not width is the major size.
    qreal w = area.width();
    if (w<area.height()) w = area.height();
616

dongfang's avatar
dongfang committed
617 618 619 620
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
621 622 623 624

    QTransform savedTransform = painter.transform();

    // find the mark nearest center
625
    int snap = qRound((double)(displayPitch/PITCH_SCALE_RESOLUTION))*PITCH_SCALE_RESOLUTION;
626 627 628 629
    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;
630 631 632 633 634 635 636 637 638 639 640 641
        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
642

643
        float shift = pitchAngleToTranslation(w, displayPitch-degrees);
dongfang's avatar
dongfang committed
644 645 646 647

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

648
        painter.translate(0, shift);
dongfang's avatar
dongfang committed
649 650
        QPointF start(-linewidth*w, 0);
        QPointF end(linewidth*w, 0);
651 652 653 654 655 656 657
        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) {
658
                QString s_number;
659
                if (isnan(this->pitch))
660 661 662
                    s_number.sprintf("-");
                else
                    s_number.sprintf("%d", displayDegrees);
dongfang's avatar
dongfang committed
663 664
                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);
665 666 667 668 669 670 671 672 673 674 675 676 677
            }
        }

        painter.setTransform(savedTransform);
    }
}

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

678
    qreal w = area.width();
dongfang's avatar
dongfang committed
679
    if (w<area.height()) w = area.height();
680

dongfang's avatar
dongfang committed
681 682 683 684
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
685 686 687 688 689 690

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

691
    qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang's avatar
dongfang committed
692
    QRectF arcArea(-_size/2, - _size/2, _size, _size);
693
    painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang's avatar
dongfang committed
694
    // painter.drawEllipse(QPoint(0,0),200,200);
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
    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) {
714
                drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
715 716 717 718 719 720 721
            }
        }
    }
}

void PrimaryFlightDisplay::drawAIAttitudeScales(
        QPainter& painter,
dongfang's avatar
dongfang committed
722 723
        QRectF area,
        float intrusion
dongfang's avatar
dongfang committed
724
        ) {
725
    float displayRoll = this->roll;
726
    if (isnan(displayRoll))
727
        displayRoll = 0;
728 729 730
    // To save computations, we do these transformations once for both scales:
    painter.resetTransform();
    painter.translate(area.center());
731
    painter.rotate(-displayRoll);
732 733 734 735
    QTransform saved = painter.transform();

    drawRollScale(painter, area, true, true);
    painter.setTransform(saved);
dongfang's avatar
dongfang committed
736
    drawPitchScale(painter, area, intrusion, true, true);
737 738
}

dongfang's avatar
dongfang committed
739
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
740
    float displayHeading = this->heading;
741
    if (isnan(displayHeading))
742 743 744 745
        displayHeading = 0;

    float start = displayHeading - halfspan;
    float end = displayHeading + halfspan;
dongfang's avatar
dongfang committed
746

dongfang's avatar
dongfang committed
747 748
    int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
    int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
749 750 751 752

    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();
dongfang's avatar
dongfang committed
753 754
    painter.setBrush(instrumentBackground);
    painter.setPen(instrumentEdgePen);
755 756 757
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
758 759
    QPen scalePen(Qt::black);
    scalePen.setWidthF(fineLineWidth);
760

dongfang's avatar
dongfang committed
761
    for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
762 763 764 765 766
        int displayTick = tickYaw;
        if (displayTick < 0) displayTick+=360;
        else if (displayTick>=360) displayTick-=360;

        // yaw is in center.
767
        float off = tickYaw - displayHeading;
768
        // wrap that to ]-180..180]
dongfang's avatar
dongfang committed
769
        if (off<=-180) off+= 360; else if (off>180) off -= 360;
770 771 772 773

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

776
        // If heading unknown, still draw marks but no numbers.
777
        if (!isnan(this->heading) &&
778
                (displayTick==30 || displayTick==60 ||
dongfang's avatar
dongfang committed
779 780
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
781 782
                displayTick==300 || displayTick==330)
        ) {
783 784 785
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
dongfang's avatar
dongfang committed
786
            painter.setPen(scalePen);
787
            drawTextCenter(painter, s_number, smallTextSize, 0, -innerRadius*0.75);
788
        } else {
dongfang's avatar
dongfang committed
789
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
790 791 792 793 794
                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
795
                    painter.setPen(scalePen);
796 797 798 799 800
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
801
                // If heading unknown, still draw marks but no N S E W.
802
                if (!isnan(this->heading) && displayTick%90 == 0) {
803
                    // Also draw a label
dongfang's avatar
dongfang committed
804 805 806
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
807
                }
dongfang's avatar
dongfang committed
808
            }
809
        }
dongfang's avatar
dongfang committed
810
        // draw the scale lines. If an arrow was drawn, stay off from it.
811

dongfang's avatar
dongfang committed
812 813
        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);
814

dongfang's avatar
dongfang committed
815
        painter.setPen(scalePen);
816 817 818 819
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

dongfang's avatar
dongfang committed
820
    painter.setPen(scalePen);
821 822 823 824 825 826 827 828
    //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
829 830 831 832 833 834 835 836 837
    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);
838
    painter.setPen(instrumentEdgePen);
dongfang's avatar
dongfang committed
839
    painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
840

dongfang's avatar
dongfang committed
841
    QString s_digitalCompass;
842

843
    if (isnan(this->heading))
844 845 846 847 848 849
        s_digitalCompass.sprintf("---");
    else {
    /* final safeguard for really stupid systems */
        int digitalCompassValue = static_cast<int>(qRound((double)heading)) % 360;
        s_digitalCompass.sprintf("%03d", digitalCompassValue);
    }
850

dongfang's avatar
dongfang committed
851 852 853 854
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
855

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

858 859 860 861 862
//  dummy
//  navigationTargetBearing = 10;
//  navigationCrosstrackError = 500;

    // The CDI
863
    if (shouldDisplayNavigationData() && !isnan(navigationTargetBearing) && !isinf(navigationCrosstrackError)) {
864 865 866 867 868 869 870 871 872 873 874 875 876
        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
877

878 879 880 881 882 883 884
        float sillyHeading = 0;
        float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
        painter.rotate(-angle);

        QPen pen;
        pen.setWidthF(lineWidth);
        pen.setColor(Qt::black);
885 886 887
        if(errorBeyondRadius) {
            pen.setStyle(Qt::DotLine);
        }
888 889 890 891 892 893
        painter.setPen(pen);

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

894 895
void PrimaryFlightDisplay::drawAltimeter(
        QPainter& painter,
896
        QRectF area
897
    ) {
898

899 900 901
    float primaryAltitude = altitudeAMSL;
    float secondaryAltitude = 0;

902
    painter.resetTransform();
903
    fillInstrumentBackground(painter, area);
904

dongfang's avatar
dongfang committed
905 906
    QPen pen;
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
907

dongfang's avatar
dongfang committed
908 909
    pen.setColor(Qt::white);
    painter.setPen(pen);
910

dongfang's avatar
dongfang committed
911 912
    float h = area.height();
    float w = area.width();
913
    float secondaryAltitudeBoxHeight = mediumTextSize * 2;
dongfang's avatar
dongfang committed
914
    // The height where we being with new tickmarks.
dongfang's avatar
dongfang committed
915
    float effectiveHalfHeight = h*0.45;
916 917

    // not yet implemented: Display of secondary altitude.
918
    if (!isnan(secondaryAltitude)) {
919 920
        effectiveHalfHeight-= secondaryAltitudeBoxHeight;
    }
921

dongfang's avatar
dongfang committed
922 923 924 925 926 927
    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
928
    float numbersLeft = 0.42*w;
dongfang's avatar
dongfang committed
929
    float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
930
    float scaleCenterAltitude = isnan(primaryAltitude) ? 0 : primaryAltitude;
931 932

    // altitude scale
dongfang's avatar
dongfang committed
933 934
    float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
935 936 937
    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
938 939
        float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
        bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
940

941 942
        painter.resetTransform();
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
943 944 945 946
        pen.setColor(tickAlt<0 ? redColor : Qt::white);
        painter.setPen(pen);
        if (isMajor) {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
947
            QString s_alt;
dongfang's avatar
dongfang committed
948
            s_alt.sprintf("%d", abs(tickAlt));
dongfang's avatar
dongfang committed
949
            drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
dongfang's avatar
dongfang committed
950 951
        } else {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);
952 953 954 955 956 957 958 959 960 961 962 963
        }
    }

    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
964

dongfang's avatar
dongfang committed
965
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
966 967 968
    pen.setColor(Qt::white);
    painter.setPen(pen);

969 970 971 972
    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
973 974
    pen.setColor(Qt::white);
    painter.setPen(pen);
dongfang's avatar
dongfang committed
975

976
    QString s_alt;
977
    if (isnan(primaryAltitude))
dongfang's avatar
dongfang committed
978 979 980 981
        s_alt.sprintf("---");
    else
        s_alt.sprintf("%3.0f", primaryAltitude);

982
    float xCenter = (markerTip+rightEdge)/2;
983
    drawTextCenter(painter, s_alt, mediumTextSize, xCenter, 0);
dongfang's avatar
dongfang committed
984

985
    // draw simple in-tape VVI.
986 987 988 989
    if (!isnan(climbRate)) {
        float vvPixHeight = -climbRate/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
        if (abs (vvPixHeight) < markerHalfHeight)
            return; // hidden behind marker.
dongfang's avatar
dongfang committed
990

991
        float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
dongfang's avatar
dongfang committed
992

993 994 995 996
        // 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);
        QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
        painter.drawLine(vvArrowBegin, vvArrowEnd);
dongfang's avatar
dongfang committed
997

998 999 1000
        // 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;
1001

1002
        float xcenter = rightEdge-w*ALTIMETER_VVI_WIDTH/2;
1003

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

1007 1008
        vvArrowHead = QPointF(xcenter-vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize);
        painter.drawLine(vvArrowHead, vvArrowEnd);
1009 1010 1011
    }

    // print secondary altitude
1012
    if (!isnan(secondaryAltitude)) {
1013 1014 1015 1016 1017 1018 1019 1020 1021
        QRectF saBox(area.x(), area.y()-secondaryAltitudeBoxHeight, w, secondaryAltitudeBoxHeight);
        painter.resetTransform();
        painter.translate(saBox.center());
        QString s_salt;
        s_salt.sprintf("%3.0f", secondaryAltitude);
        drawTextCenter(painter, s_salt, mediumTextSize, 0, 0);
    }

    // print target altitude (if applicable)
1022 1023
}

1024 1025
void PrimaryFlightDisplay::drawVelocityMeter(
        QPainter& painter,
1026
        QRectF area
1027 1028 1029
        ) {

    painter.resetTransform();
1030
    fillInstrumentBackground(painter, area);
1031 1032 1033 1034 1035 1036 1037

    QPen pen;
    pen.setWidthF(lineWidth);

    float h = area.height();
    float w = area.width();
    float effectiveHalfHeight = h*0.45;
dongfang's avatar
dongfang committed
1038
    float markerHalfHeight = mediumTextSize;
dongfang's avatar
dongfang committed
1039
    float leftEdge = instrumentEdgePen.widthF()*2;
dongfang's avatar
dongfang committed
1040 1041 1042 1043 1044 1045 1046 1047
    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:

1048 1049
    float speed = (isAirplane() && isnan(airSpeed)) ? airSpeed : groundSpeed;
    float centerScaleSpeed = isnan(speed) ? 0 : speed;
dongfang's avatar
dongfang committed
1050 1051 1052

    float start = centerScaleSpeed - AIRSPEED_LINEAR_SPAN/2;
    float end = centerScaleSpeed + AIRSPEED_LINEAR_SPAN/2;
1053 1054 1055 1056

    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
1057 1058 1059 1060
        pen.setColor(tickSpeed<0 ? redColor : Qt::white);
        painter.setPen(pen);

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

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

1066
        if (hasText) {
dongfang's avatar
dongfang committed
1067
            painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0);
1068
            QString s_speed;
dongfang's avatar
dongfang committed
1069
            s_speed.sprintf("%d", abs(tickSpeed));
dongfang's avatar
dongfang committed
1070
            drawTextRightCenter(painter, s_speed, mediumTextSize, numbersRight, 0);
dongfang's avatar
dongfang committed
1071 1072
        } else {
            painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0);
1073 1074 1075 1076
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
dongfang's avatar
dongfang committed
1077 1078 1079 1080
    markerPath.lineTo(markerTip-markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(leftEdge, markerHalfHeight);
    markerPath.lineTo(leftEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip-markerHalfHeight, -markerHalfHeight);
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
    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;
1097
    if (isnan(speed))
dongfang's avatar
dongfang committed
1098 1099
        s_alt.sprintf("---");
    else
1100
        s_alt.sprintf("%3.1f", speed);
dongfang's avatar
dongfang committed
1101
    float xCenter = (markerTip+leftEdge)/2;
1102 1103 1104
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
}

1105 1106 1107 1108
static const int TOP = (1<<0);
static const int BOTTOM = (1<<1);
static const int LEFT = (1<<2);
static const int RIGHT = (1<<3);
dongfang's avatar
dongfang committed
1109

1110 1111 1112 1113
static const int TOP_HALF = (1<<4);
static const int BOTTOM_HALF = (1<<5);
static const int LEFT_HALF = (1<<6);
static const int RIGHT_HALF = (1<<7);
dongfang's avatar
dongfang committed
1114 1115 1116 1117 1118 1119 1120 1121 1122

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));
1123
    } else if (where & LEFT_HALF) {
dongfang's avatar
dongfang committed
1124 1125 1126 1127 1128 1129 1130
        area.setX(save.x() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & RIGHT) {
        area.setWidth(save.width()-consumed-margin);
1131
    } else if (where & RIGHT_HALF) {
dongfang's avatar
dongfang committed
1132 1133 1134 1135 1136 1137 1138
        area.setWidth(save.width()-consumed-margin/2);
    } else {
        area.setWidth(save.width()-consumed);
    }

    if (where & TOP) {
        area.setY(save.y() + (consumed = margin));
1139
    } else if (where & TOP_HALF) {
dongfang's avatar
dongfang committed
1140 1141 1142 1143 1144 1145 1146
        area.setY(save.y() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & BOTTOM) {
        area.setHeight(save.height()-consumed-margin);
1147
    } else if (where & BOTTOM_HALF) {
dongfang's avatar
dongfang committed
1148 1149 1150 1151 1152 1153 1154
        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) {
1155 1156 1157 1158
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_HALF);
    applyMargin(panel2, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel3, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel4, margin, BOTTOM|LEFT_HALF|RIGHT);
1159 1160
}

dongfang's avatar
dongfang committed
1161
void setMarginsForCornerLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
1162 1163 1164 1165
    applyMargin(panel1, margin, BOTTOM|LEFT|RIGHT_HALF);
    applyMargin(panel2, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel3, margin, BOTTOM|LEFT_HALF|RIGHT_HALF);
    applyMargin(panel4, margin, BOTTOM|LEFT_HALF|RIGHT);
dongfang's avatar
dongfang committed
1166 1167 1168 1169
}

inline qreal tapesGaugeWidthFor(qreal containerWidth, qreal preferredAIWidth) {
    qreal result = (containerWidth - preferredAIWidth) / 2.0f;
dongfang's avatar
dongfang committed
1170
    qreal minimum = containerWidth / 5.5f;
dongfang's avatar
dongfang committed
1171 1172 1173 1174 1175
    if (result < minimum) result = minimum;
    return result;
}

void PrimaryFlightDisplay::doPaint() {
1176 1177 1178 1179 1180
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

dongfang's avatar
dongfang committed
1181
    qreal margin = height()/100.0f;
1182

1183 1184 1185 1186 1187
    // 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
1188 1189 1190 1191 1192 1193 1194
    QRectF compassArea;
    QRectF altimeterArea;
    QRectF velocityMeterArea;
    QRectF sensorsStatsArea;
    QRectF linkStatsArea;
    QRectF sysStatsArea;
    QRectF missionStatsArea;
1195 1196

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

dongfang's avatar
dongfang committed
1199 1200 1201
    qreal compassHalfSpan = 180;
    float compassAIIntrusion = 0;

dongfang's avatar
dongfang committed
1202
    switch(layout) {
1203
    /*
dongfang's avatar
dongfang committed
1204 1205 1206 1207 1208 1209
    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.
1210 1211
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1212
                    0,
1213
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth * 2: width(),
dongfang's avatar
dongfang committed
1214 1215
                    height());

1216 1217
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1218 1219 1220 1221
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.

        qreal sidePanelsHeight = height();

1222
        altimeterArea = QRectF(AIMainArea.right(), height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);
dongfang's avatar
dongfang committed
1223 1224 1225
        velocityMeterArea = QRectF (0, height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);

        sensorsStatsArea = QRectF(0, 0, tapeGaugeWidth, sidePanelsHeight/5);
1226
        linkStatsArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1227
        sysStatsArea = QRectF(0, sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
1228
        missionStatsArea =QRectF(AIMainArea.right(), sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1229

1230 1231
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1232 1233 1234 1235
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForCornerLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1236

dongfang's avatar
dongfang committed
1237
        // Compass is inside the AI ans within its margins also.
1238 1239
        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
1240 1241
        break;
    }
1242

dongfang's avatar
dongfang committed
1243 1244 1245 1246
    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());
1247

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

dongfang's avatar
dongfang committed
1250
        tapeGaugeWidth = tapesGaugeWidthFor(width(), aiheight);
1251

1252 1253
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1254
                    0,
1255
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth*2 : width(),
dongfang's avatar
dongfang committed
1256
                    aiheight);
1257

1258 1259
        AIPaintArea = AIMainArea;

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

dongfang's avatar
dongfang committed
1264 1265
        qreal panelsHeight = height() / 5.0f;
        qreal panelsWidth = width() / 4.0f;
1266

1267 1268 1269 1270
        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);
1271

1272 1273
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1274 1275 1276 1277
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1278

dongfang's avatar
dongfang committed
1279
        // Compass is inside the AI ans within its margins also.
1280 1281
        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
1282 1283
        break;
    }
1284

1285 1286 1287
    */

    case COMPASS_INTEGRATED: {
dongfang's avatar
dongfang committed
1288
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());
1289
        qreal aiheight = height();
dongfang's avatar
dongfang committed
1290 1291
        qreal aiwidth = width()-tapeGaugeWidth*2;
        if (aiheight > aiwidth) aiheight = aiwidth;
1292 1293 1294 1295

        AIMainArea = QRectF(
                    tapeGaugeWidth,
                    0,
dongfang's avatar
dongfang committed
1296
                    aiwidth,
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315
                    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
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
        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;

1344 1345
        break;
    }
dongfang's avatar
dongfang committed
1346 1347 1348 1349
    case COMPASS_SEPARATED: {
        // A layout for containers higher than their width.
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());

1350 1351
        qreal aiheight = width() - tapeGaugeWidth*2;
        qreal panelsHeight = 0;
dongfang's avatar
dongfang committed
1352

1353 1354
        AIMainArea = QRectF(
                    tapeGaugeWidth,
dongfang's avatar
dongfang committed
1355
                    0,
1356
                    width()-tapeGaugeWidth*2,
dongfang's avatar
dongfang committed
1357 1358
                    aiheight);

1359 1360 1361 1362 1363 1364 1365
        AIPaintArea = style == OVERLAY_HSI ?
                    QRectF(
                    0,
                    0,
                    width(),
                    height() - panelsHeight) : AIMainArea;

dongfang's avatar
dongfang committed
1366
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1367
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1368

1369
        /*
dongfang's avatar
dongfang committed
1370
        qreal panelsWidth = width() / 4.0f;
dongfang's avatar
dongfang committed
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
        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
1381
            maxCompassDiam = fmin(width(), height()-AIArea.height()-panelsHeight);
dongfang's avatar
dongfang committed
1382 1383
        } else {
            // Remaining part is wider than high; place panels in corners around compass
1384
            // Naaah it is really ugly, do not do that.
dongfang's avatar
dongfang committed
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
            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
1396 1397 1398 1399 1400 1401 1402 1403
            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
1404
        }
1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
        */
        /*
        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
1415
        compassArea = QRectF(compassCenter.x()-compassDiam/2, compassCenter.y()-compassDiam/2, compassDiam, compassDiam);
dongfang's avatar
dongfang committed
1416 1417 1418
        break;
    }
    }
1419

dongfang's avatar
dongfang committed
1420
    bool hadClip = painter.hasClipping();
dongfang's avatar
dongfang committed
1421

dongfang's avatar
dongfang committed
1422
    painter.setClipping(true);
1423
    painter.setClipRect(AIPaintArea);
1424

1425
    drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea);
dongfang's avatar
dongfang committed
1426
    drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion);
1427
    drawAIAirframeFixedFeatures(painter, AIMainArea);
1428

1429 1430 1431
   // if(layout ==COMPASS_SEPARATED)
        //drawSeparateCompassDisk(painter, compassArea);
   // else
dongfang's avatar
dongfang committed
1432
        drawAICompassDisk(painter, compassArea, compassHalfSpan);
1433

dongfang's avatar
dongfang committed
1434
    painter.setClipping(hadClip);
1435

1436
    drawAltimeter(painter, altimeterArea);
1437

1438
    drawVelocityMeter(painter, velocityMeterArea);
1439

1440
    /*
dongfang's avatar
dongfang committed
1441 1442 1443 1444
    drawSensorsStatsPanel(painter, sensorsStatsArea);
    drawLinkStatsPanel(painter, linkStatsArea);
    drawSysStatsPanel(painter, sysStatsArea);
    drawMissionStatsPanel(painter, missionStatsArea);
1445 1446
    */
    /*
dongfang's avatar
dongfang committed
1447 1448 1449
    if (style == OPAGUE_TAPES) {
        drawInstrumentBackground(painter, AIArea);
    }
1450
    */
dongfang's avatar
dongfang committed
1451

dongfang's avatar
dongfang committed
1452
    painter.end();
1453
}
1454 1455

void PrimaryFlightDisplay:: createActions() {}