Skip to content
QMap3DWidget.cc 19.5 KiB
Newer Older
/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Definition of the class QMap3DWidget.
 *
 *   @author Lionel Heng <hengli@student.ethz.ch>
 *
 */

lm's avatar
lm committed
#include "QMap3DWidget.h"

#include <QCheckBox>
lm's avatar
lm committed
#include <sys/time.h>

pixhawk's avatar
pixhawk committed
//#include "QGCGlut.h"
lm's avatar
lm committed
#include "UASManager.h"
#include "UASInterface.h"
lm's avatar
lm committed
#include "QGC.h"
lm's avatar
lm committed

QMap3DWidget::QMap3DWidget(QWidget* parent)
     : Q3DWidget(parent)
     , uas(NULL)
     , lastRedrawTime(0.0)
     , displayTrail(false)
     , lockCamera(true)
     , updateLastUnlockedPose(true)
     , displayTarget(false)
lm's avatar
lm committed
     , displayWaypoints(true)
lm's avatar
lm committed
{
    setFocusPolicy(Qt::StrongFocus);

    initialize(10, 10, 1000, 900, 15.0f);
    setCameraParams(0.05f, 0.5f, 0.01f, 0.5f, 30.0f, 0.01f, 1000000.0f);
lm's avatar
lm committed

    setDisplayFunc(display, this);
lm's avatar
lm committed
    addTimerFunc(100, timer, this);

    buildLayout();
pixhawk's avatar
pixhawk committed
    //font.reset(new FTTextureFont("images/Vera.ttf"));
lm's avatar
lm committed
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)),
            this, SLOT(setActiveUAS(UASInterface*)));
}

QMap3DWidget::~QMap3DWidget()
{

}

QMap3DWidget::buildLayout(void)
    QCheckBox* gridCheckBox = new QCheckBox(this);
    gridCheckBox->setText("Grid");
    gridCheckBox->setChecked(displayGrid);

    QCheckBox* trailCheckBox = new QCheckBox(this);
    trailCheckBox->setText("Trail");
    trailCheckBox->setChecked(displayTrail);

lm's avatar
lm committed
    QCheckBox* waypointsCheckBox = new QCheckBox(this);
    waypointsCheckBox->setText("Waypoints");
    waypointsCheckBox->setChecked(displayWaypoints);

    QLabel* imageryLabel = new QLabel(this);
    imageryLabel->setText("Imagery");

    imageryComboBox = new QComboBox(this);
    imageryComboBox->addItem("None");
    imageryComboBox->addItem("Map (Google)");
    imageryComboBox->addItem("Satellite (Google)");

    QPushButton* recenterButton = new QPushButton(this);
    recenterButton->setText("Recenter Camera");

    QCheckBox* lockCameraCheckBox = new QCheckBox(this);
    lockCameraCheckBox->setText("Lock Camera");
    lockCameraCheckBox->setChecked(lockCamera);

    //positionLabel = new QLabel(this);
    //positionLabel->setText(tr("Waiting for first position update.. "));
pixhawk's avatar
pixhawk committed

    QGridLayout* layout = new QGridLayout(this);
    layout->setMargin(0);
    layout->setSpacing(2);
    layout->addWidget(gridCheckBox, 1, 0);
    layout->addWidget(trailCheckBox, 1, 1);
lm's avatar
lm committed
    layout->addWidget(waypointsCheckBox, 1, 2);
    layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 3);
    layout->addWidget(imageryLabel, 1, 4);
    layout->addWidget(imageryComboBox, 1, 5);
    layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 6);
    layout->addWidget(recenterButton, 1, 7);
    layout->addWidget(lockCameraCheckBox, 1, 8);
    layout->setRowStretch(0, 100);
    layout->setRowStretch(1, 1);
    layout->setColumnStretch(3, 50);
    layout->setColumnStretch(6, 50);
    setLayout(layout);
    connect(gridCheckBox, SIGNAL(stateChanged(int)),
            this, SLOT(showGrid(int)));
    connect(trailCheckBox, SIGNAL(stateChanged(int)),
            this, SLOT(showTrail(int)));
    connect(imageryComboBox, SIGNAL(currentIndexChanged(const QString &)),
            this, SLOT(showImagery(const QString &)));
    connect(recenterButton, SIGNAL(clicked()), this, SLOT(recenterCamera()));
    connect(lockCameraCheckBox, SIGNAL(stateChanged(int)),
            this, SLOT(toggleLockCamera(int)));
lm's avatar
lm committed
void
QMap3DWidget::display(void* clientData)
{
    QMap3DWidget* map3d = reinterpret_cast<QMap3DWidget *>(clientData);
    map3d->displayHandler();
}

void
QMap3DWidget::displayHandler(void)
{
lm's avatar
lm committed
    if (cheetahModel.data() == 0)
lm's avatar
lm committed
    {
        cheetahModel.reset(new CheetahModel);
        cheetahModel->init(1.0f, 1.0f, 1.0f);
    }

    float robotX = 0.0f, robotY = 0.0f, robotZ = 0.0f;
    float robotRoll = 0.0f, robotPitch = 0.0f, robotYaw = 0.0f;
    if (uas != NULL)
        robotX = uas->getLocalX();
        robotY = uas->getLocalY();
        robotZ = uas->getLocalZ();
        robotRoll = uas->getRoll();
        robotPitch = uas->getPitch();
        robotYaw = uas->getYaw();
    if (updateLastUnlockedPose)
    {
        lastUnlockedPose.x = robotX;
        lastUnlockedPose.y = robotY;
        lastUnlockedPose.z = robotZ;

        camOffset.x = 0.0f;
        camOffset.y = 0.0f;
        camOffset.z = 0.0f;

        updateLastUnlockedPose = false;
    }

    if (!lockCamera)
    {
        camOffset.x = robotX - lastUnlockedPose.x;
        camOffset.y = robotY - lastUnlockedPose.y;
        camOffset.z = robotZ - lastUnlockedPose.z;
    }
lm's avatar
lm committed
    // turn on smooth lines
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // clear window
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    glTranslatef(camOffset.x, camOffset.y, camOffset.z);

lm's avatar
lm committed
    // draw Cheetah model
    drawPlatform(robotRoll, robotPitch, robotYaw);
    if (displayGrid)
    {
        drawGrid();
    }
    if (displayTrail)
    {
        drawTrail(robotX, robotY, robotZ);
    }
    if (displayTarget)
    {
        drawTarget(robotX, robotY, robotZ);
    }

lm's avatar
lm committed
    if (displayWaypoints)
    {
        drawWaypoints();
    }

        drawImagery(robotX, robotY, "32T", true);
    // switch to 2D
    drawLegend();

    // display pose information
    glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
    glBegin(GL_POLYGON);
    glVertex2f(0.0f, 0.0f);
    glVertex2f(0.0f, 45.0f);
    glVertex2f(getWindowWidth(), 45.0f);
    glVertex2f(getWindowWidth(), 0.0f);
    glEnd();

    std::pair<float,float> mouseWorldCoords =
            getPositionIn3DMode(getMouseX(), getMouseY());

    // QT QPAINTER OPENGL PAINTING

    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);
    paintText(QString("x = %1 y = %2 z = %3 r = %4 p = %5 y = %6 Cursor [%7 %8]").arg(robotX, 0, 'f', 2).arg(robotY, 0, 'f', 2).arg(robotZ, 0, 'f', 2).arg(robotRoll, 0, 'f', 2).arg(robotPitch, 0, 'f', 2).arg(robotYaw, 0, 'f', 2).arg( mouseWorldCoords.first + robotX, 0, 'f', 2).arg( mouseWorldCoords.second + robotY, 0, 'f', 2),
lm's avatar
lm committed
void QMap3DWidget::drawWaypoints()
{
    if (uas)
    {
        const QVector<Waypoint*>& list = uas->getWaypointManager().getWaypointList();
        QColor color;

        QPointF lastWaypoint;

        for (int i = 0; i < list.size(); i++)
        {
            QPointF in(list.at(i)->getX(), list.at(i)->getY());
            // Transform from world to body coordinates
            //in = metricWorldToBody(in);

            // DRAW WAYPOINT
            float waypointRadius = 0.1f;// = vwidth / 20.0f * 2.0f;


            // Select color based on if this is the current waypoint
            if (list.at(i)->getCurrent())
            {
                color = QGC::colorCyan;//uas->getColor();

            }
            else
            {
                color = uas->getColor();

            }

            //float radius = (waypointSize/2.0f) * 0.8 * (1/sqrt(2.0f));
            // Draw yaw
            // Draw sphere





            static double radius = 0.2;

            glPushMatrix();
            glTranslatef(in.x() - uas->getLocalX(), in.y() - uas->getLocalY(), 0.0f);
            glColor3f(1.0f, 0.3f, 0.3f);
lm's avatar
lm committed
            glLineWidth(1.0f);

//            // Make sure quad object exists
//            static GLUquadricObj* quadObj2;
//            if(!quadObj2) quadObj2 = gluNewQuadric();
//            gluQuadricDrawStyle(quadObj2, GLU_LINE);
//            gluQuadricNormals(quadObj2, GLU_SMOOTH);
//            /* If we ever changed/used the texture or orientation state
//               of quadObj, we'd need to change it to the defaults here
//               with gluQuadricTexture and/or gluQuadricOrientation. */
//            gluSphere(quadObj2, radius, 10, 10);

            wireSphere(radius, 10, 10);

            glPopMatrix();






            // DRAW CONNECTING LINE
            // Draw line from last waypoint to this one
            if (!lastWaypoint.isNull())
            {
                // OpenGL line
            }
            lastWaypoint = in;
        }
    }
}

void
QMap3DWidget::drawLegend(void)
{
    // draw marker outlines
    glColor3f(1.0f, 1.0f, 1.0f);
    glLineWidth(3.0f);
    glBegin(GL_LINES);
    glVertex2f(20.0f, 60.0f);
    glVertex2f(20.0f, 80.0f);
    glVertex2f(20.0f, 70.0f);
    glVertex2f(100.0f, 70.0f);
    glVertex2f(100.0f, 60.0f);
    glVertex2f(100.0f, 80.0f);
    glEnd();

    // draw markers
    glColor3f(0.0f, 0.0f, 0.0f);
    glLineWidth(1.5f);
    glBegin(GL_LINES);
    glVertex2f(20.0f, 60.0f);
    glVertex2f(20.0f, 80.0f);
    glVertex2f(20.0f, 70.0f);
    glVertex2f(100.0f, 70.0f);
    glVertex2f(100.0f, 60.0f);
    glVertex2f(100.0f, 80.0f);
    glEnd();

    float f = windowHeight / 2.0f / tanf(d2r(cameraParams.cameraFov / 2.0f));
    float dist = cameraPose.distance / f * 80.0f;

    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing, true);

    QColor rgb(255, 255, 255);
    if (imageryComboBox->currentText().compare("Map (Google)") == 0)
    {
        rgb.setRgb(0, 0, 0);
    }

    paintText(QString("%1 m").arg(dist, 0, 'f', 2),
              rgb,
              10,
              25,
              getWindowHeight() - 65,
              &painter);
}

void QMap3DWidget::paintText(QString text, QColor color, float fontSize, float refX, float refY, QPainter* painter)
{
    QPen prevPen = painter->pen();

    float pPositionX = refX;
    float pPositionY = refY;

    QFont font("Bitstream Vera Sans");
    // Enforce minimum font size of 5 pixels
    int fSize = qMax(5, (int)(fontSize));
    font.setPixelSize(fSize);

    QFontMetrics metrics = QFontMetrics(font);
    int border = qMax(4, metrics.leading());
    QRect rect = metrics.boundingRect(0, 0, width() - 2*border, int(height()*0.125),
                                      Qt::AlignLeft | Qt::TextWordWrap, text);
    painter->setPen(color);
    painter->setFont(font);
    painter->setRenderHint(QPainter::TextAntialiasing);
    painter->drawText(pPositionX, pPositionY,
                      rect.width(), rect.height(),
                      Qt::AlignCenter | Qt::TextWordWrap, text);
    painter->setPen(prevPen);
void
QMap3DWidget::mouse(Qt::MouseButton button, MouseState state,
                    int32_t x, int32_t y, void* clientData)
    QMap3DWidget* map3d = reinterpret_cast<QMap3DWidget *>(clientData);
    map3d->mouseHandler(button, state, x, y);
}

void
QMap3DWidget::mouseHandler(Qt::MouseButton button, MouseState state,
                           int32_t x, int32_t y)
{
    if (button == Qt::RightButton && state == MOUSE_STATE_DOWN)
        QAction* targetAction = menu.addAction(tr("Mark as Target"));
        connect(targetAction, SIGNAL(triggered()), this, SLOT(markTarget()));
        menu.exec(mapToGlobal(QPoint(x, y)));
lm's avatar
lm committed
    }
}

void
QMap3DWidget::timer(void* clientData)
{
    QMap3DWidget* map3d = reinterpret_cast<QMap3DWidget *>(clientData);
    map3d->timerHandler();
}

void
QMap3DWidget::timerHandler(void)
{
    if (imagery.isNull())
    {
        imagery.reset(new Imagery);
    }

lm's avatar
lm committed
    double timeLapsed = getTime() - lastRedrawTime;
    if (timeLapsed > 0.1)
    {
lm's avatar
lm committed
        forceRedraw();
        lastRedrawTime = getTime();
    }
    addTimerFunc(100, timer, this);
}

double
QMap3DWidget::getTime(void) const
{
     struct timeval tv;

     gettimeofday(&tv, NULL);

     return static_cast<double>(tv.tv_sec) +
             static_cast<double>(tv.tv_usec) / 1000000.0;
}
/**
 *
 * @param uas the UAS/MAV to monitor/display with the HUD
 */
void QMap3DWidget::setActiveUAS(UASInterface* uas)
{
    if (this->uas != NULL && this->uas != uas)
    {
        // Disconnect any previously connected active MAV
        //disconnect(uas, SIGNAL(valueChanged(UASInterface*,QString,double,quint64)), this, SLOT(updateValue(UASInterface*,QString,double,quint64)));
    }

    this->uas = uas;
}

void
QMap3DWidget::markTarget(void)
{
    std::pair<float,float> mouseWorldCoords =
            getPositionIn3DMode(getLastMouseX(), getLastMouseY());

    float robotX = 0.0f, robotY = 0.0f, robotZ = 0.0f;
    if (uas != NULL)
    {
        robotX = uas->getLocalX();
        robotY = uas->getLocalY();
        robotZ = uas->getLocalZ();
    }

    targetPosition.x = mouseWorldCoords.first + robotX;
    targetPosition.y = mouseWorldCoords.second + robotY;
    targetPosition.z = robotZ;

    displayTarget = true;

    if (uas)
    {
        uas->setTargetPosition(targetPosition.x, targetPosition.y,
                               targetPosition.z, 0.0f);
    }
void
QMap3DWidget::showGrid(int32_t state)
{
    if (state == Qt::Checked)
    {
        displayGrid = true;
    }
    else
    {
        displayGrid = false;
    }
}

void
QMap3DWidget::showImagery(const QString& text)
{
    if (text.compare("None") == 0)
    {
        displayImagery = false;
    }
    else
    {
        if (text.compare("Map (Google)") == 0)
        {
            imagery->setImageryType(Imagery::MAP);
        }
        else if (text.compare("Satellite (Google)") == 0)
        {
            imagery->setImageryType(Imagery::SATELLITE);
        }
        displayImagery = true;
    }
}


void
QMap3DWidget::showTrail(int32_t state)
{
    if (state == Qt::Checked)
    {
        if (!displayTrail)
        {
            trail.clear();
        }

        displayTrail = true;
    }
    else
    {
        displayTrail = false;
    }
}

void
QMap3DWidget::recenterCamera(void)
{
    recenter();
}

void
QMap3DWidget::toggleLockCamera(int32_t state)
{
    if (state == Qt::Checked)
    {
        lockCamera = true;
    }
    else
    {
        lockCamera = false;
    }
}

void
QMap3DWidget::drawPlatform(float roll, float pitch, float yaw)
{
    glPushMatrix();

    glRotatef(yaw * 180.0f / M_PI, 0.0f, 0.0f, 1.0f);
    glRotatef(pitch * 180.0f / M_PI, 0.0f, 1.0f, 0.0f);
    glRotatef(roll * 180.0f / M_PI, 1.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.3f, 0.0f, 0.0f);
    glEnd();

    // Y AXIS
    glColor3f(0.0f, 1.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.15f, 0.0f);
    glEnd();

    // Z AXIS
    glColor3f(0.0f, 0.0f, 1.0f);
    glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.15f);
    glEnd();

    cheetahModel->draw();

    glPopMatrix();
}

void
QMap3DWidget::drawGrid(void)
{
    float radius = 10.0f;
    float resolution = 0.25f;

    glPushMatrix();

    // draw a 20m x 20m grid with 0.25m resolution
    glColor3f(0.5f, 0.5f, 0.5f);
    for (float i = -radius; i <= radius; i += resolution)
    {
        if (fabsf(i - roundf(i)) < 0.01f)
        {
            glLineWidth(2.0f);
        }
        else
        {
            glLineWidth(0.25f);
        }

        glBegin(GL_LINES);
        glVertex3f(i, -radius, 0.0f);
        glVertex3f(i, radius, 0.0f);
        glVertex3f(-radius, i, 0.0f);
        glVertex3f(radius, i, 0.0f);
        glEnd();
    }

    glPopMatrix();
}

void
QMap3DWidget::drawImagery(double originX, double originY, const QString& zone,
                          bool prefetch)
{
    glPushMatrix();
    glEnable(GL_BLEND);

    double viewingRadius = cameraPose.distance / 4000.0 * 3000.0;
    if (viewingRadius < 100.0)
    {
        viewingRadius = 100.0;
    }

    double minResolution = 0.25;
    double centerResolution = cameraPose.distance / 100.0;
    double maxResolution = 1048576.0;

    if (imageryComboBox->currentText().compare("Map (Google)") == 0)
    {
        minResolution = 0.25;
    }
    else if (imageryComboBox->currentText().compare("Satellite (Google)") == 0)
    {
        minResolution = 0.5;
    }

    double resolution = minResolution;
    while (resolution * 2.0 < centerResolution)
    {
        resolution *= 2.0;
    }
    if (resolution > maxResolution)
    {
        resolution = maxResolution;
    }

    imagery->draw3D(viewingRadius, resolution, originX, originY,
                    cameraPose.xOffset, cameraPose.yOffset, zone);

    if (prefetch)
    {
        if (resolution / 2.0 >= minResolution)
        {
            imagery->prefetch3D(viewingRadius / 2.0, resolution / 2.0,
                                originX, originY,
                                cameraPose.xOffset, cameraPose.yOffset, zone);
        }
        if (resolution * 2.0 <= maxResolution)
        {
            imagery->prefetch3D(viewingRadius * 2.0, resolution * 2.0,
                                originX, originY,
                                cameraPose.xOffset, cameraPose.yOffset, zone);
void
QMap3DWidget::drawTrail(float x, float y, float z)
{
    bool addToTrail = false;
    if (trail.size() > 0)
    {
        if (fabsf(x - trail[trail.size() - 1].x) > 0.01f ||
            fabsf(y - trail[trail.size() - 1].y) > 0.01f ||
            fabsf(z - trail[trail.size() - 1].z) > 0.01f)
        {
            addToTrail = true;
        }
    }
    else
    {
        addToTrail = true;
    }

    if (addToTrail)
    {
        Pose3D p = {x, y, z};
        if (trail.size() == trail.capacity())
        {
            memcpy(trail.data(), trail.data() + 1,
                   (trail.size() - 1) * sizeof(Pose3D));
            trail[trail.size() - 1] = p;
        }
        else
        {
            trail.append(p);
        }
    }

    glColor3f(1.0f, 0.0f, 0.0f);
    glLineWidth(1.0f);
    glBegin(GL_LINE_STRIP);
    for (int32_t i = 0; i < trail.size(); ++i)
    {
        glVertex3f(trail[i].x - x, trail[i].y - y, trail[i].z - z);
    }
    glEnd();
}

void
QMap3DWidget::drawTarget(float x, float y, float z)
{
    static double radius = 0.2;
    static bool expand = true;

    if (radius < 0.1)
    {
        expand = true;
    }
    else if (radius > 0.25)
    {
        expand = false;
    }

    glPushMatrix();
    glTranslatef(targetPosition.x - x, targetPosition.y - y, 0.0f);
    glColor3f(0.0f, 0.7f, 1.0f);
    glLineWidth(1.0f);
lm's avatar
lm committed
    wireSphere(radius, 10, 10);