PrimaryFlightDisplay.cc 56.7 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 92
static const int UNKNOWN_ATTITUDE = -1000;
static const int UNKNOWN_ALTITUDE = -1000;
static const int UNKNOWN_SPEED = -1;
93

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

105 106 107 108 109 110
qreal PrimaryFlightDisplay_constrain(qreal value, qreal min, qreal max) {
    if (value<min) value=min;
    else if(value>max) value=max;
    return value;
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
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),

126
    uas(NULL),
127

128
    /*
129 130 131
    altimeterMode(GPS_MAIN),
    altimeterFrame(ASL),
    speedMode(GROUND_MAIN),
132
    */
133

134 135 136
    roll(UNKNOWN_ATTITUDE),
    pitch(UNKNOWN_ATTITUDE),
    heading(UNKNOWN_ATTITUDE),
137

138
    primaryAltitude(UNKNOWN_ALTITUDE),
139 140
    GPSAltitude(UNKNOWN_ALTITUDE),
    aboveHomeAltitude(UNKNOWN_ALTITUDE),
141

142
    primarySpeed(UNKNOWN_SPEED),
143
    groundspeed(UNKNOWN_SPEED),
144
    verticalVelocity(UNKNOWN_ALTITUDE),
145

146
    navigationCrosstrackError(0),
147 148
    navigationTargetBearing(UNKNOWN_ATTITUDE),

149 150
    layout(COMPASS_INTEGRATED),
    style(OVERLAY_HSI),
151

dongfang's avatar
dongfang committed
152
    redColor(QColor::fromHsvF(0, 0.75, 0.9)),
dongfang's avatar
dongfang committed
153 154 155 156 157 158 159
    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
160
    instrumentBackground(QColor::fromHsvF(0, 0, 0.3, 0.3)),
161 162 163 164
    instrumentOpagueBackground(QColor::fromHsvF(0, 0, 0.3, 1.0)),

    font("Bitstream Vera Sans"),
    refreshTimer(new QTimer(this))
dongfang's avatar
dongfang committed
165
{
166 167
    Q_UNUSED(width);
    Q_UNUSED(height);
168

169 170 171
    setMinimumSize(120, 80);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

172
    // Connect with UAS signal
173 174
    //connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(addUAS(UASInterface*)));
    connect(UASManager::instance(), SIGNAL(UASDeleted(UASInterface*)), this, SLOT(forgetUAS(UASInterface*)));
175
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
176

177 178 179 180
//    // Get a list of all existing UAS and - well attach to one of them. The first one.
//    foreach (UASInterface* uas, UASManager::instance()->getUASList()) {
//        addUAS(uas);
//    }
181

182
    setActiveUAS(UASManager::instance()->getActiveUAS());
183 184 185

    // Refresh timer
    refreshTimer->setInterval(updateInterval);
dongfang's avatar
dongfang committed
186
    //    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(paintHUD()));
187
    connect(refreshTimer, SIGNAL(timeout()), this, SLOT(update()));
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
}

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
219 220 221 222
void PrimaryFlightDisplay::resizeEvent(QResizeEvent *e) {
    QWidget::resizeEvent(e);

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

225 226
    lineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH, 1, 6);
    fineLineWidth = PrimaryFlightDisplay_constrain(size*LINEWIDTH*2/3, 1, 2);
dongfang's avatar
dongfang committed
227 228 229 230

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

231
    smallTextSize = size * SMALL_TEXT_SIZE;
dongfang's avatar
dongfang committed
232 233 234
    mediumTextSize = size * MEDIUM_TEXT_SIZE;
    largeTextSize = size * LARGE_TEXT_SIZE;

dongfang's avatar
dongfang committed
235 236
    /*
     * Try without layout Change-O-Matic. It was too complicated.
237 238
    qreal aspect = e->size().width() / e->size().height();
    if (aspect <= SEPARATE_COMPASS_ASPECTRATIO)
dongfang's avatar
dongfang committed
239 240
        layout = COMPASS_SEPARATED;
    else
241
        layout = COMPASS_INTEGRATED;
dongfang's avatar
dongfang committed
242
    */
243
    // qDebug("Width %d height %d decision %d", e->size().width(), e->size().height(), layout);
dongfang's avatar
dongfang committed
244
}
245

dongfang's avatar
dongfang committed
246 247 248 249 250
void PrimaryFlightDisplay::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    doPaint();
}
251

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
///*
// * Interface towards qgroundcontrol
// */
//void PrimaryFlightDisplay::addUAS(UASInterface* uas)
//{
//    if (uas)
//    {
//        if (!this->uas)
//        {
//            setActiveUAS(uas);
//        }
//    }
//}

void PrimaryFlightDisplay::forgetUAS(UASInterface* uas)
267
{
268
    if (this->uas != NULL && this->uas == uas) {
269
        // Disconnect any previously connected active MAV
tstellanova's avatar
tstellanova committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
        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(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*, 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)));
288

289 290 291 292 293 294
        //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)));
295
        //disconnect(this->uas, SIGNAL(localizationChanged(UASInterface* uas, int fix)), this, SLOT(updateGPSFixType(UASInterface*,int)));
296
    }
297 298 299 300 301 302 303 304
}

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

308 309
    // Disconnect the previous one (if any)
    forgetUAS(this->uas);
310 311 312 313 314 315

    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)));
316

317 318 319 320 321 322
        //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)));
323

dongfang's avatar
dongfang committed
324
        //connect(uas, SIGNAL(localPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateLocalPosition(UASInterface*,double,double,double,quint64)));
325
        //connect(uas, SIGNAL(globalPositionChanged(UASInterface*,double,double,double,quint64)), this, SLOT(updateGlobalPosition(UASInterface*,double,double,double,quint64)));
tstellanova's avatar
tstellanova committed
326 327
        //connect(uas, SIGNAL(waypointSelected(int,int)), this,
        //        SLOT(selectWaypoint(int, int)));
328 329
        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)));
tstellanova's avatar
tstellanova committed
330 331
        connect(uas, SIGNAL(climbRateChanged(UASInterface*, double, quint64)), this,
                SLOT(updateClimbRate(UASInterface*, double, quint64)));
332 333 334
        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)));
335 336 337 338 339 340 341 342 343 344

        // 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);
345 346 347 348 349 350
        // Called from UAS.cc l. 616
        if (isnan(roll) || isinf(roll)) {
            this->roll = UNKNOWN_ATTITUDE;
        } else {
            this->roll = roll * (180.0 / M_PI);
        }
351

352 353 354 355 356 357 358 359 360 361 362 363 364
        if (isnan(pitch) || isinf(pitch)) {
            this->pitch = UNKNOWN_ATTITUDE;
        } else {
            this->pitch = pitch * (180.0 / M_PI);
        }

        if (isnan(yaw) || isinf(yaw)) {
            this->heading = UNKNOWN_ATTITUDE;
        } else {
            yaw = yaw * (180.0 / M_PI);
            if (yaw<0) yaw+=360;
            this->heading = yaw;
        }
365 366 367 368 369
}

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

373 374 375 376 377 378 379 380 381 382
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)
383 384 385 386
{
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);

387 388
    groundspeed = speed;
    if (!didReceivePrimarySpeed)
389 390
        primarySpeed = speed;
}
391

392
void PrimaryFlightDisplay::updateClimbRate(UASInterface* uas, double climbRate, quint64 timestamp) {
393 394 395 396 397
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
    verticalVelocity = climbRate;
}

398 399 400 401 402 403 404 405
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) {
406 407
    Q_UNUSED(uas);
    Q_UNUSED(timestamp);
408 409
    GPSAltitude = altitude;
    if (!didReceivePrimaryAltitude)
410
        primaryAltitude = altitude;
411 412
}

413 414 415 416 417 418 419 420
void PrimaryFlightDisplay::updateNavigationControllerErrors(UASInterface* uas, double altitudeError, double speedError, double xtrackError) {
    Q_UNUSED(uas);
    this->navigationAltitudeError = altitudeError;
    this->navigationSpeedError = speedError;
    this->navigationCrosstrackError = xtrackError;
}


421 422 423
/*
 * Private and such
 */
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

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

440 441 442 443 444 445 446
// 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;
}

447 448 449
void PrimaryFlightDisplay::drawTextCenter (
        QPainter& painter,
        QString text,
dongfang's avatar
dongfang committed
450
        float pixelSize,
451 452 453
        float x,
        float y)
{
dongfang's avatar
dongfang committed
454
    font.setPixelSize(pixelSize);
455 456 457 458 459 460 461 462 463 464 465
    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
466
        float pixelSize,
467 468 469
        float x,
        float y)
{
dongfang's avatar
dongfang committed
470
    font.setPixelSize(pixelSize);
471 472 473 474 475 476 477 478 479 480 481
    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
482
        float pixelSize,
483 484 485
        float x,
        float y)
{
dongfang's avatar
dongfang committed
486
    font.setPixelSize(pixelSize);
487 488 489 490 491 492 493 494 495 496 497
    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
498
        float pixelSize,
499 500 501
        float x,
        float y)
{
dongfang's avatar
dongfang committed
502
    font.setPixelSize(pixelSize);
503 504 505 506 507 508 509 510 511 512 513
    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
514
        float pixelSize,
515 516 517
        float x,
        float y)
{
dongfang's avatar
dongfang committed
518
    font.setPixelSize(pixelSize);
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    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
539 540 541 542 543 544 545
void PrimaryFlightDisplay::fillInstrumentOpagueBackground(QPainter& painter, QRectF edge) {
    painter.setPen(instrumentEdgePen);
    painter.setBrush(instrumentOpagueBackground);
    painter.drawRect(edge);
    painter.setBrush(Qt::NoBrush);
}

546
qreal pitchAngleToTranslation(qreal viewHeight, float pitch) {
547 548
    if (pitch == UNKNOWN_ATTITUDE)
        return 0;
549
    return pitch * viewHeight / PITCHTRANSLATION;
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
}

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
565
    QPen pen;
566
    pen.setWidthF(lineWidth * 1.5f);
dongfang's avatar
dongfang committed
567 568
    pen.setColor(redColor);
    painter.setPen(pen);
569

570 571
    float length = 0.15f;
    float side = 0.5f;
dongfang's avatar
dongfang committed
572 573 574 575
    // 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));

576
    float rel = length/qSqrt(2.0f);
dongfang's avatar
dongfang committed
577 578 579 580 581 582 583 584
    // 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);
585 586 587 588
    markerPath.closeSubpath();
    painter.drawPath(markerPath);
}

589 590 591 592 593 594 595 596 597 598 599 600 601 602
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;
}

603 604
void PrimaryFlightDisplay::drawAIGlobalFeatures(
        QPainter& painter,
605 606
        QRectF mainArea,
        QRectF paintArea) {
607

608 609 610 611
    float displayRoll = this->roll;
    if(displayRoll == UNKNOWN_ATTITUDE)
        displayRoll = 0;

612
    painter.resetTransform();
613
    painter.translate(mainArea.center());
614

615 616
    qreal pitchPixels = pitchAngleToTranslation(mainArea.height(), pitch);
    qreal gradientEnd = pitchAngleToTranslation(mainArea.height(), 60);
617

618 619
    //painter.rotate(-roll);
    //painter.translate(0, pitchPixels);
620

621 622
    //    QTransform forwardTransform;
    //forwardTransform.translate(mainArea.center().x(), mainArea.center().y());
623
    painter.rotate(-displayRoll);
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    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);
646 647
    skyPath.closeSubpath();

648
    QLinearGradient skyGradient(0, -gradientEnd, 0, 0);
649 650 651 652 653
    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);

654 655 656 657
    QPainterPath groundPath(hzonRight);
    groundPath.lineTo(maxx, maxy);
    groundPath.lineTo(minx, maxy);
    groundPath.lineTo(hzonLeft);
658 659
    groundPath.closeSubpath();

660
    QLinearGradient groundGradient(0, gradientEnd, 0, 0);
661 662 663 664 665
    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
666 667 668 669 670
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(greenColor);
    painter.setPen(pen);

671 672
    QPointF start(-mainArea.width(), 0);
    QPoint end(mainArea.width(), 0);
673 674 675 676 677 678
    painter.drawLine(start, end);
}

void PrimaryFlightDisplay::drawPitchScale(
        QPainter& painter,
        QRectF area,
dongfang's avatar
dongfang committed
679
        float intrusion,
680 681 682 683
        bool drawNumbersLeft,
        bool drawNumbersRight
        ) {

684 685 686 687
    float displayPitch = this->pitch;
    if (displayPitch == UNKNOWN_ATTITUDE)
        displayPitch = 0;

dongfang's avatar
dongfang committed
688 689 690
    // The area should be quadratic but if not width is the major size.
    qreal w = area.width();
    if (w<area.height()) w = area.height();
691

dongfang's avatar
dongfang committed
692 693 694 695
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
696 697 698 699

    QTransform savedTransform = painter.transform();

    // find the mark nearest center
700
    int snap = qRound((double)(displayPitch/PITCH_SCALE_RESOLUTION))*PITCH_SCALE_RESOLUTION;
701 702 703 704
    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;
705 706 707 708 709 710 711 712 713 714 715 716
        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
717

718
        float shift = pitchAngleToTranslation(w, displayPitch-degrees);
dongfang's avatar
dongfang committed
719 720 721 722

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

723
        painter.translate(0, shift);
dongfang's avatar
dongfang committed
724 725
        QPointF start(-linewidth*w, 0);
        QPointF end(linewidth*w, 0);
726 727 728 729 730 731 732
        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) {
733 734 735 736 737
                QString s_number;
                if (this->pitch == UNKNOWN_ATTITUDE)
                    s_number.sprintf("-");
                else
                    s_number.sprintf("%d", displayDegrees);
dongfang's avatar
dongfang committed
738 739
                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);
740 741 742 743 744 745 746 747 748 749 750 751 752
            }
        }

        painter.setTransform(savedTransform);
    }
}

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

753
    qreal w = area.width();
dongfang's avatar
dongfang committed
754
    if (w<area.height()) w = area.height();
755

dongfang's avatar
dongfang committed
756 757 758 759
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
760 761 762 763 764 765

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

766
    qreal _size = w * ROLL_SCALE_RADIUS*2;
dongfang's avatar
dongfang committed
767
    QRectF arcArea(-_size/2, - _size/2, _size, _size);
768
    painter.drawArc(arcArea, (90-ROLL_SCALE_RANGE)*16, ROLL_SCALE_RANGE*2*16);
dongfang's avatar
dongfang committed
769
    // painter.drawEllipse(QPoint(0,0),200,200);
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    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) {
789
                drawTextCenterBottom(painter, s_number, mediumTextSize, 0, -(ROLL_SCALE_RADIUS+ROLL_SCALE_TICKMARKLENGTH*1.7)*w);
790 791 792 793 794 795 796
            }
        }
    }
}

void PrimaryFlightDisplay::drawAIAttitudeScales(
        QPainter& painter,
dongfang's avatar
dongfang committed
797 798
        QRectF area,
        float intrusion
dongfang's avatar
dongfang committed
799
        ) {
800 801 802
    float displayRoll = this->roll;
    if (displayRoll == UNKNOWN_ATTITUDE)
        displayRoll = 0;
803 804 805
    // To save computations, we do these transformations once for both scales:
    painter.resetTransform();
    painter.translate(area.center());
806
    painter.rotate(-displayRoll);
807 808 809 810
    QTransform saved = painter.transform();

    drawRollScale(painter, area, true, true);
    painter.setTransform(saved);
dongfang's avatar
dongfang committed
811
    drawPitchScale(painter, area, intrusion, true, true);
812 813
}

dongfang's avatar
dongfang committed
814
void PrimaryFlightDisplay::drawAICompassDisk(QPainter& painter, QRectF area, float halfspan) {
815 816 817 818 819 820
    float displayHeading = this->heading;
    if(displayHeading == UNKNOWN_ATTITUDE)
        displayHeading = 0;

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

dongfang's avatar
dongfang committed
822 823
    int firstTick = ceil(start / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
    int lastTick = floor(end / COMPASS_DISK_RESOLUTION) * COMPASS_DISK_RESOLUTION;
824 825 826 827

    float radius = area.width()/2;
    float innerRadius = radius * 0.96;
    painter.resetTransform();
dongfang's avatar
dongfang committed
828 829
    painter.setBrush(instrumentBackground);
    painter.setPen(instrumentEdgePen);
830 831 832
    painter.drawEllipse(area);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
833 834
    QPen scalePen(Qt::black);
    scalePen.setWidthF(fineLineWidth);
835

dongfang's avatar
dongfang committed
836
    for (int tickYaw = firstTick; tickYaw <= lastTick; tickYaw += COMPASS_DISK_RESOLUTION) {
837 838 839 840 841
        int displayTick = tickYaw;
        if (displayTick < 0) displayTick+=360;
        else if (displayTick>=360) displayTick-=360;

        // yaw is in center.
842
        float off = tickYaw - displayHeading;
843
        // wrap that to ]-180..180]
dongfang's avatar
dongfang committed
844
        if (off<=-180) off+= 360; else if (off>180) off -= 360;
845 846 847 848

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

851 852 853
        // If heading unknown, still draw marks but no numbers.
        if (this->heading != UNKNOWN_ATTITUDE &&
                (displayTick==30 || displayTick==60 ||
dongfang's avatar
dongfang committed
854 855
                displayTick==120 || displayTick==150 ||
                displayTick==210 || displayTick==240 ||
856 857
                displayTick==300 || displayTick==330)
        ) {
858 859 860
            // draw a number
            QString s_number;
            s_number.sprintf("%d", displayTick/10);
dongfang's avatar
dongfang committed
861
            painter.setPen(scalePen);
862
            drawTextCenter(painter, s_number, smallTextSize, 0, -innerRadius*0.75);
863
        } else {
dongfang's avatar
dongfang committed
864
            if (displayTick % COMPASS_DISK_ARROWTICK == 0) {
865 866 867 868 869
                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
870
                    painter.setPen(scalePen);
871 872 873 874 875
                    painter.setBrush(Qt::SolidPattern);
                    painter.drawPath(markerPath);
                    painter.setBrush(Qt::NoBrush);
                    drewArrow = true;
                }
876 877
                // If heading unknown, still draw marks but no N S E W.
                if (this->heading != UNKNOWN_ATTITUDE && displayTick%90 == 0) {
878
                    // Also draw a label
dongfang's avatar
dongfang committed
879 880 881
                    QString name = compassWindNames[displayTick / 45];
                    painter.setPen(scalePen);
                    drawTextCenter(painter, name, mediumTextSize, 0, -innerRadius*0.75);
882
                }
dongfang's avatar
dongfang committed
883
            }
884
        }
dongfang's avatar
dongfang committed
885
        // draw the scale lines. If an arrow was drawn, stay off from it.
886

dongfang's avatar
dongfang committed
887 888
        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);
889

dongfang's avatar
dongfang committed
890
        painter.setPen(scalePen);
891 892 893 894
        painter.drawLine(p_start, p_end);
        painter.resetTransform();
    }

dongfang's avatar
dongfang committed
895
    painter.setPen(scalePen);
896 897 898 899 900 901 902 903
    //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
904 905 906 907 908 909 910 911 912
    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);
913
    painter.setPen(instrumentEdgePen);
dongfang's avatar
dongfang committed
914
    painter.drawRoundedRect(digitalCompassRect, instrumentEdgePen.widthF()*2/3, instrumentEdgePen.widthF()*2/3);
915

dongfang's avatar
dongfang committed
916
    QString s_digitalCompass;
917 918 919 920 921 922 923 924

    if (this->heading == UNKNOWN_ATTITUDE)
        s_digitalCompass.sprintf("---");
    else {
    /* final safeguard for really stupid systems */
        int digitalCompassValue = static_cast<int>(qRound((double)heading)) % 360;
        s_digitalCompass.sprintf("%03d", digitalCompassValue);
    }
925

dongfang's avatar
dongfang committed
926 927 928 929
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(Qt::white);
    painter.setPen(pen);
930

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

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
//  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
952

953 954 955 956 957 958 959
        float sillyHeading = 0;
        float angle = sillyHeading - navigationTargetBearing; // TODO: sign.
        painter.rotate(-angle);

        QPen pen;
        pen.setWidthF(lineWidth);
        pen.setColor(Qt::black);
960 961 962
        if(errorBeyondRadius) {
            pen.setStyle(Qt::DotLine);
        }
963 964 965 966 967 968
        painter.setPen(pen);

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

969 970 971
void PrimaryFlightDisplay::drawAltimeter(
        QPainter& painter,
        QRectF area, // the area where to draw the tape.
dongfang's avatar
dongfang committed
972
        float primaryAltitude,
973
        float secondaryAltitude,
974
        float vv
975
    ) {
976 977

    painter.resetTransform();
978
    fillInstrumentBackground(painter, area);
979

dongfang's avatar
dongfang committed
980 981
    QPen pen;
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
982

dongfang's avatar
dongfang committed
983 984
    pen.setColor(Qt::white);
    painter.setPen(pen);
985

dongfang's avatar
dongfang committed
986 987
    float h = area.height();
    float w = area.width();
988
    float secondaryAltitudeBoxHeight = mediumTextSize * 2;
dongfang's avatar
dongfang committed
989
    // The height where we being with new tickmarks.
dongfang's avatar
dongfang committed
990
    float effectiveHalfHeight = h*0.45;
991 992

    // not yet implemented: Display of secondary altitude.
993 994 995
    if (secondaryAltitude != UNKNOWN_ALTITUDE) {
        effectiveHalfHeight-= secondaryAltitudeBoxHeight;
    }
996

dongfang's avatar
dongfang committed
997 998 999 1000 1001 1002
    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
1003
    float numbersLeft = 0.42*w;
dongfang's avatar
dongfang committed
1004 1005
    float markerTip = (tickmarkLeft*2+tickmarkRightMajor)/3;
    float scaleCenterAltitude = primaryAltitude == UNKNOWN_ALTITUDE ? 0 : primaryAltitude;
1006 1007

    // altitude scale
dongfang's avatar
dongfang committed
1008 1009
    float start = scaleCenterAltitude - ALTIMETER_LINEAR_SPAN/2;
    float end = scaleCenterAltitude + ALTIMETER_LINEAR_SPAN/2;
1010 1011 1012
    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
1013 1014
        float y = (tickAlt-scaleCenterAltitude)*effectiveHalfHeight/(ALTIMETER_LINEAR_SPAN/2);
        bool isMajor = tickAlt % ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0;
1015

1016 1017
        painter.resetTransform();
        painter.translate(area.left(), area.center().y() - y);
dongfang's avatar
dongfang committed
1018 1019 1020 1021
        pen.setColor(tickAlt<0 ? redColor : Qt::white);
        painter.setPen(pen);
        if (isMajor) {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0);
1022
            QString s_alt;
dongfang's avatar
dongfang committed
1023
            s_alt.sprintf("%d", abs(tickAlt));
dongfang's avatar
dongfang committed
1024
            drawTextLeftCenter(painter, s_alt, mediumTextSize, numbersLeft, 0);
dongfang's avatar
dongfang committed
1025 1026
        } else {
            painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0);
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
        }
    }

    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
1039

dongfang's avatar
dongfang committed
1040
    pen.setWidthF(lineWidth);
dongfang's avatar
dongfang committed
1041 1042 1043
    pen.setColor(Qt::white);
    painter.setPen(pen);

1044 1045 1046 1047
    painter.setBrush(Qt::SolidPattern);
    painter.drawPath(markerPath);
    painter.setBrush(Qt::NoBrush);

dongfang's avatar
dongfang committed
1048 1049
    pen.setColor(Qt::white);
    painter.setPen(pen);
dongfang's avatar
dongfang committed
1050

1051
    QString s_alt;
dongfang's avatar
dongfang committed
1052 1053 1054 1055 1056
    if(primaryAltitude == UNKNOWN_ALTITUDE)
        s_alt.sprintf("---");
    else
        s_alt.sprintf("%3.0f", primaryAltitude);

1057
    float xCenter = (markerTip+rightEdge)/2;
1058
    drawTextCenter(painter, s_alt, mediumTextSize, xCenter, 0);
dongfang's avatar
dongfang committed
1059

1060 1061
    // draw simple in-tape VVI.
    if (vv != UNKNOWN_ALTITUDE) {
1062 1063
    float vvPixHeight = -vv/ALTIMETER_VVI_SPAN * effectiveHalfHeight;
    if (abs (vvPixHeight)<markerHalfHeight) return; // hidden behind marker.
dongfang's avatar
dongfang committed
1064

1065
    float vvSign = vvPixHeight>0 ? 1 : -1; // reverse y sign
dongfang's avatar
dongfang committed
1066 1067 1068

    // 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);
1069
    QPointF vvArrowEnd(rightEdge - w*ALTIMETER_VVI_WIDTH/2, vvPixHeight);
dongfang's avatar
dongfang committed
1070 1071
    painter.drawLine(vvArrowBegin, vvArrowEnd);

1072 1073 1074 1075 1076 1077 1078
    // 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
1079 1080
    painter.drawLine(vvArrowHead, vvArrowEnd);

1081
    vvArrowHead = QPointF(xcenter-vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize);
dongfang's avatar
dongfang committed
1082
    painter.drawLine(vvArrowHead, vvArrowEnd);
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    }

    // print secondary altitude
    if (secondaryAltitude != UNKNOWN_ALTITUDE) {
        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)
1096 1097
}

1098 1099
void PrimaryFlightDisplay::drawVelocityMeter(
        QPainter& painter,
1100 1101 1102
        QRectF area,
        float speed,
        float secondarySpeed
1103 1104 1105
        ) {

    painter.resetTransform();
1106
    fillInstrumentBackground(painter, area);
1107 1108 1109 1110 1111 1112 1113

    QPen pen;
    pen.setWidthF(lineWidth);

    float h = area.height();
    float w = area.width();
    float effectiveHalfHeight = h*0.45;
dongfang's avatar
dongfang committed
1114
    float markerHalfHeight = mediumTextSize;
dongfang's avatar
dongfang committed
1115
    float leftEdge = instrumentEdgePen.widthF()*2;
dongfang's avatar
dongfang committed
1116 1117 1118 1119 1120 1121 1122 1123 1124
    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 =
1125
            speed == UNKNOWN_SPEED ? 0 : speed;
dongfang's avatar
dongfang committed
1126 1127 1128

    float start = centerScaleSpeed - AIRSPEED_LINEAR_SPAN/2;
    float end = centerScaleSpeed + AIRSPEED_LINEAR_SPAN/2;
1129 1130 1131 1132

    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
1133 1134 1135 1136
        pen.setColor(tickSpeed<0 ? redColor : Qt::white);
        painter.setPen(pen);

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

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

1142
        if (hasText) {
dongfang's avatar
dongfang committed
1143
            painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0);
1144
            QString s_speed;
dongfang's avatar
dongfang committed
1145
            s_speed.sprintf("%d", abs(tickSpeed));
dongfang's avatar
dongfang committed
1146
            drawTextRightCenter(painter, s_speed, mediumTextSize, numbersRight, 0);
dongfang's avatar
dongfang committed
1147 1148
        } else {
            painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0);
1149 1150 1151 1152
        }
    }

    QPainterPath markerPath(QPoint(markerTip, 0));
dongfang's avatar
dongfang committed
1153 1154 1155 1156
    markerPath.lineTo(markerTip-markerHalfHeight, markerHalfHeight);
    markerPath.lineTo(leftEdge, markerHalfHeight);
    markerPath.lineTo(leftEdge, -markerHalfHeight);
    markerPath.lineTo(markerTip-markerHalfHeight, -markerHalfHeight);
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    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;
1173
    if (speed == UNKNOWN_SPEED)
dongfang's avatar
dongfang committed
1174 1175
        s_alt.sprintf("---");
    else
1176
        s_alt.sprintf("%3.1f", speed);
dongfang's avatar
dongfang committed
1177
    float xCenter = (markerTip+leftEdge)/2;
1178 1179 1180
    drawTextCenter(painter, s_alt, /* TAPES_TEXT_SIZE*width()*/ mediumTextSize, xCenter, 0);
}

1181 1182 1183 1184
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
1185

1186 1187 1188 1189
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
1190 1191 1192 1193 1194 1195 1196 1197 1198

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));
1199
    } else if (where & LEFT_HALF) {
dongfang's avatar
dongfang committed
1200 1201 1202 1203 1204 1205 1206
        area.setX(save.x() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & RIGHT) {
        area.setWidth(save.width()-consumed-margin);
1207
    } else if (where & RIGHT_HALF) {
dongfang's avatar
dongfang committed
1208 1209 1210 1211 1212 1213 1214
        area.setWidth(save.width()-consumed-margin/2);
    } else {
        area.setWidth(save.width()-consumed);
    }

    if (where & TOP) {
        area.setY(save.y() + (consumed = margin));
1215
    } else if (where & TOP_HALF) {
dongfang's avatar
dongfang committed
1216 1217 1218 1219 1220 1221 1222
        area.setY(save.y() + (consumed = margin/2));
    } else {
        consumed = 0;
    }

    if (where & BOTTOM) {
        area.setHeight(save.height()-consumed-margin);
1223
    } else if (where & BOTTOM_HALF) {
dongfang's avatar
dongfang committed
1224 1225 1226 1227 1228 1229 1230
        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) {
1231 1232 1233 1234
    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);
1235 1236
}

dongfang's avatar
dongfang committed
1237
void setMarginsForCornerLayout(qreal margin, QRectF& panel1, QRectF& panel2, QRectF& panel3, QRectF& panel4) {
1238 1239 1240 1241
    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
1242 1243 1244 1245
}

inline qreal tapesGaugeWidthFor(qreal containerWidth, qreal preferredAIWidth) {
    qreal result = (containerWidth - preferredAIWidth) / 2.0f;
dongfang's avatar
dongfang committed
1246
    qreal minimum = containerWidth / 5.5f;
dongfang's avatar
dongfang committed
1247 1248 1249 1250 1251
    if (result < minimum) result = minimum;
    return result;
}

void PrimaryFlightDisplay::doPaint() {
1252 1253 1254 1255 1256
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

dongfang's avatar
dongfang committed
1257
    qreal margin = height()/100.0f;
1258

1259 1260 1261 1262 1263
    // 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
1264 1265 1266 1267 1268 1269 1270
    QRectF compassArea;
    QRectF altimeterArea;
    QRectF velocityMeterArea;
    QRectF sensorsStatsArea;
    QRectF linkStatsArea;
    QRectF sysStatsArea;
    QRectF missionStatsArea;
1271 1272

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

dongfang's avatar
dongfang committed
1275 1276 1277
    qreal compassHalfSpan = 180;
    float compassAIIntrusion = 0;

dongfang's avatar
dongfang committed
1278
    switch(layout) {
1279
    /*
dongfang's avatar
dongfang committed
1280 1281 1282 1283 1284 1285
    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.
1286 1287
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1288
                    0,
1289
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth * 2: width(),
dongfang's avatar
dongfang committed
1290 1291
                    height());

1292 1293
        AIPaintArea = AIMainArea;

dongfang's avatar
dongfang committed
1294 1295 1296 1297
        // Tape gauges get so much width that the AI area not covered by them is perfectly square.

        qreal sidePanelsHeight = height();

1298
        altimeterArea = QRectF(AIMainArea.right(), height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);
dongfang's avatar
dongfang committed
1299 1300 1301
        velocityMeterArea = QRectF (0, height()/5, tapeGaugeWidth, sidePanelsHeight*3/5);

        sensorsStatsArea = QRectF(0, 0, tapeGaugeWidth, sidePanelsHeight/5);
1302
        linkStatsArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1303
        sysStatsArea = QRectF(0, sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
1304
        missionStatsArea =QRectF(AIMainArea.right(), sidePanelsHeight*4/5, tapeGaugeWidth, sidePanelsHeight/5);
dongfang's avatar
dongfang committed
1305

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

dongfang's avatar
dongfang committed
1313
        // Compass is inside the AI ans within its margins also.
1314 1315
        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
1316 1317
        break;
    }
1318

dongfang's avatar
dongfang committed
1319 1320 1321 1322
    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());
1323

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

dongfang's avatar
dongfang committed
1326
        tapeGaugeWidth = tapesGaugeWidthFor(width(), aiheight);
1327

1328 1329
        AIMainArea = QRectF(
                    style == NO_OVERLAYS ? tapeGaugeWidth : 0,
dongfang's avatar
dongfang committed
1330
                    0,
1331
                    style == NO_OVERLAYS ? width() - tapeGaugeWidth*2 : width(),
dongfang's avatar
dongfang committed
1332
                    aiheight);
1333

1334 1335
        AIPaintArea = AIMainArea;

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

dongfang's avatar
dongfang committed
1340 1341
        qreal panelsHeight = height() / 5.0f;
        qreal panelsWidth = width() / 4.0f;
1342

1343 1344 1345 1346
        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);
1347

1348 1349
        if (style == NO_OVERLAYS) {
            applyMargin(AIMainArea, margin, TOP|BOTTOM);
dongfang's avatar
dongfang committed
1350 1351 1352 1353
            applyMargin(altimeterArea, margin, TOP|BOTTOM|RIGHT);
            applyMargin(velocityMeterArea, margin, TOP|BOTTOM|LEFT);
            setMarginsForInlineLayout(margin, sensorsStatsArea, linkStatsArea, sysStatsArea, missionStatsArea);
        }
1354

dongfang's avatar
dongfang committed
1355
        // Compass is inside the AI ans within its margins also.
1356 1357
        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
1358 1359
        break;
    }
1360

1361 1362 1363
    */

    case COMPASS_INTEGRATED: {
dongfang's avatar
dongfang committed
1364
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());
1365
        qreal aiheight = height();
dongfang's avatar
dongfang committed
1366 1367
        qreal aiwidth = width()-tapeGaugeWidth*2;
        if (aiheight > aiwidth) aiheight = aiwidth;
1368 1369 1370 1371

        AIMainArea = QRectF(
                    tapeGaugeWidth,
                    0,
dongfang's avatar
dongfang committed
1372
                    aiwidth,
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
                    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
1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
        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;

1420 1421
        break;
    }
dongfang's avatar
dongfang committed
1422 1423 1424 1425
    case COMPASS_SEPARATED: {
        // A layout for containers higher than their width.
        tapeGaugeWidth = tapesGaugeWidthFor(width(), width());

1426 1427
        qreal aiheight = width() - tapeGaugeWidth*2;
        qreal panelsHeight = 0;
dongfang's avatar
dongfang committed
1428

1429 1430
        AIMainArea = QRectF(
                    tapeGaugeWidth,
dongfang's avatar
dongfang committed
1431
                    0,
1432
                    width()-tapeGaugeWidth*2,
dongfang's avatar
dongfang committed
1433 1434
                    aiheight);

1435 1436 1437 1438 1439 1440 1441
        AIPaintArea = style == OVERLAY_HSI ?
                    QRectF(
                    0,
                    0,
                    width(),
                    height() - panelsHeight) : AIMainArea;

dongfang's avatar
dongfang committed
1442
        velocityMeterArea = QRectF (0, 0, tapeGaugeWidth, aiheight);
1443
        altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight);
dongfang's avatar
dongfang committed
1444

1445
        /*
dongfang's avatar
dongfang committed
1446
        qreal panelsWidth = width() / 4.0f;
dongfang's avatar
dongfang committed
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
        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
1457
            maxCompassDiam = fmin(width(), height()-AIArea.height()-panelsHeight);
dongfang's avatar
dongfang committed
1458 1459
        } else {
            // Remaining part is wider than high; place panels in corners around compass
1460
            // Naaah it is really ugly, do not do that.
dongfang's avatar
dongfang committed
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
            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
1472 1473 1474 1475 1476 1477 1478 1479
            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
1480
        }
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490
        */
        /*
        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
1491
        compassArea = QRectF(compassCenter.x()-compassDiam/2, compassCenter.y()-compassDiam/2, compassDiam, compassDiam);
dongfang's avatar
dongfang committed
1492 1493 1494
        break;
    }
    }
1495

dongfang's avatar
dongfang committed
1496
    bool hadClip = painter.hasClipping();
dongfang's avatar
dongfang committed
1497

dongfang's avatar
dongfang committed
1498
    painter.setClipping(true);
1499
    painter.setClipRect(AIPaintArea);
1500

1501
    drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea);
dongfang's avatar
dongfang committed
1502
    drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion);
1503
    drawAIAirframeFixedFeatures(painter, AIMainArea);
1504

1505 1506 1507
   // if(layout ==COMPASS_SEPARATED)
        //drawSeparateCompassDisk(painter, compassArea);
   // else
dongfang's avatar
dongfang committed
1508
        drawAICompassDisk(painter, compassArea, compassHalfSpan);
1509

dongfang's avatar
dongfang committed
1510
    painter.setClipping(hadClip);
1511

1512
    drawAltimeter(painter, altimeterArea, primaryAltitude, GPSAltitude, verticalVelocity);
1513

1514
    drawVelocityMeter(painter, velocityMeterArea, primarySpeed, groundspeed);
1515

1516
    /*
dongfang's avatar
dongfang committed
1517 1518 1519 1520
    drawSensorsStatsPanel(painter, sensorsStatsArea);
    drawLinkStatsPanel(painter, linkStatsArea);
    drawSysStatsPanel(painter, sysStatsArea);
    drawMissionStatsPanel(painter, missionStatsArea);
1521 1522
    */
    /*
dongfang's avatar
dongfang committed
1523 1524 1525
    if (style == OPAGUE_TAPES) {
        drawInstrumentBackground(painter, AIArea);
    }
1526
    */
dongfang's avatar
dongfang committed
1527

dongfang's avatar
dongfang committed
1528
    painter.end();
1529
}
1530 1531

void PrimaryFlightDisplay:: createActions() {}