Graph Gallery

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "surfacegraph.h"

#include <QtGui/qpainter.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qcheckbox.h>
#include <QtWidgets/qcombobox.h>
#include <QtWidgets/qcommandlinkbutton.h>
#include <QtWidgets/qgroupbox.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qradiobutton.h>

using namespace Qt::StringLiterals;

static QPixmap gradientBtoYPB_Pixmap()
{
    QLinearGradient grBtoY(0, 0, 1, 100);
    grBtoY.setColorAt(1.f, Qt::black);
    grBtoY.setColorAt(0.67f, Qt::blue);
    grBtoY.setColorAt(0.33f, Qt::red);
    grBtoY.setColorAt(0.f, Qt::yellow);
    QPixmap pm(24, 100);
    QPainter pmp(&pm);
    pmp.setBrush(QBrush(grBtoY));
    pmp.setPen(Qt::NoPen);
    pmp.drawRect(0, 0, 24, 100);
    return pm;
}

static QPixmap gradientGtoRPB_Pixmap()
{
    QLinearGradient grGtoR(0, 0, 1, 100);
    grGtoR.setColorAt(1.f, Qt::darkGreen);
    grGtoR.setColorAt(0.5f, Qt::yellow);
    grGtoR.setColorAt(0.2f, Qt::red);
    grGtoR.setColorAt(0.f, Qt::darkRed);
    QPixmap pm(24, 100);
    QPainter pmp(&pm);
    pmp.setBrush(QBrush(grGtoR));
    pmp.drawRect(0, 0, 24, 100);
    return pm;
}

static QPixmap highlightPixmap()
{
    constexpr int height = 400;
    constexpr int width = 110;
    constexpr int border = 10;
    QLinearGradient gr(0, 0, 1, height - 2 * border);
    gr.setColorAt(1.f, Qt::black);
    gr.setColorAt(0.8f, Qt::darkGreen);
    gr.setColorAt(0.6f, Qt::green);
    gr.setColorAt(0.4f, Qt::yellow);
    gr.setColorAt(0.2f, Qt::red);
    gr.setColorAt(0.f, Qt::darkRed);

    QPixmap pmHighlight(width, height);
    pmHighlight.fill(Qt::transparent);
    QPainter pmpHighlight(&pmHighlight);
    pmpHighlight.setBrush(QBrush(gr));
    pmpHighlight.setPen(Qt::NoPen);
    pmpHighlight.drawRect(border, border, 35, height - 2 * border);
    pmpHighlight.setPen(Qt::black);
    int step = (height - 2 * border) / 5;
    for (int i = 0; i < 6; ++i) {
        int yPos = i * step + border;
        pmpHighlight.drawLine(border, yPos, 55, yPos);
        const int height = 550 - (i * 110);
        pmpHighlight.drawText(60, yPos + 2, QString::number(height) + u" m"_s);
    }
    return pmHighlight;
}

SurfaceGraph::SurfaceGraph()
{
    m_surfaceGraph = new Q3DSurface();
    initialize();
}

void SurfaceGraph::initialize()
{
    m_surfaceWidget = new QWidget;
    auto *hLayout = new QHBoxLayout(m_surfaceWidget);
    QSize screenSize = m_surfaceGraph->screen()->size();
    m_surfaceGraph->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.75));
    m_surfaceGraph->setMaximumSize(screenSize);
    m_surfaceGraph->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_surfaceGraph->setFocusPolicy(Qt::StrongFocus);
    hLayout->addWidget(m_surfaceGraph, 1);

    auto *vLayout = new QVBoxLayout();
    hLayout->addLayout(vLayout);
    vLayout->setAlignment(Qt::AlignTop);

    // Create control widgets
    auto *modelGroupBox = new QGroupBox(u"Model"_s);

    auto *sqrtSinModelRB = new QRadioButton(m_surfaceWidget);
    sqrtSinModelRB->setText(u"Sqrt && Sin"_s);
    sqrtSinModelRB->setChecked(false);

    auto *heightMapModelRB = new QRadioButton(m_surfaceWidget);
    heightMapModelRB->setText(u"Multiseries\nHeight Map"_s);
    heightMapModelRB->setChecked(false);

    auto *texturedModelRB = new QRadioButton(m_surfaceWidget);
    texturedModelRB->setText(u"Textured\nTopography"_s);
    texturedModelRB->setChecked(false);

    auto *modelVBox = new QVBoxLayout;
    modelVBox->addWidget(sqrtSinModelRB);
    modelVBox->addWidget(heightMapModelRB);
    modelVBox->addWidget(texturedModelRB);
    modelGroupBox->setLayout(modelVBox);

    auto *selectionGroupBox = new QGroupBox(u"Graph Selection Mode"_s);

    auto *modeNoneRB = new QRadioButton(m_surfaceWidget);
    modeNoneRB->setText(u"No selection"_s);
    modeNoneRB->setChecked(false);

    auto *modeItemRB = new QRadioButton(m_surfaceWidget);
    modeItemRB->setText(u"Item"_s);
    modeItemRB->setChecked(false);

    auto *modeSliceRowRB = new QRadioButton(m_surfaceWidget);
    modeSliceRowRB->setText(u"Row Slice"_s);
    modeSliceRowRB->setChecked(false);

    auto *modeSliceColumnRB = new QRadioButton(m_surfaceWidget);
    modeSliceColumnRB->setText(u"Column Slice"_s);
    modeSliceColumnRB->setChecked(false);

    auto *selectionVBox = new QVBoxLayout;
    selectionVBox->addWidget(modeNoneRB);
    selectionVBox->addWidget(modeItemRB);
    selectionVBox->addWidget(modeSliceRowRB);
    selectionVBox->addWidget(modeSliceColumnRB);
    selectionGroupBox->setLayout(selectionVBox);

    auto *axisGroupBox = new QGroupBox(u"Axis ranges"_s);

    auto *axisMinSliderX = new QSlider(Qt::Horizontal);
    axisMinSliderX->setMinimum(0);
    axisMinSliderX->setTickInterval(1);
    axisMinSliderX->setEnabled(true);
    auto *axisMaxSliderX = new QSlider(Qt::Horizontal);
    axisMaxSliderX->setMinimum(1);
    axisMaxSliderX->setTickInterval(1);
    axisMaxSliderX->setEnabled(true);
    auto *axisMinSliderZ = new QSlider(Qt::Horizontal);
    axisMinSliderZ->setMinimum(0);
    axisMinSliderZ->setTickInterval(1);
    axisMinSliderZ->setEnabled(true);
    auto *axisMaxSliderZ = new QSlider(Qt::Horizontal);
    axisMaxSliderZ->setMinimum(1);
    axisMaxSliderZ->setTickInterval(1);
    axisMaxSliderZ->setEnabled(true);

    auto *axisVBox = new QVBoxLayout;
    axisVBox->addWidget(new QLabel(u"Column range"_s));
    axisVBox->addWidget(axisMinSliderX);
    axisVBox->addWidget(axisMaxSliderX);
    axisVBox->addWidget(new QLabel(u"Row range"_s));
    axisVBox->addWidget(axisMinSliderZ);
    axisVBox->addWidget(axisMaxSliderZ);
    axisGroupBox->setLayout(axisVBox);

    // Mode-dependent controls
    // sqrt-sin
    auto *colorGroupBox = new QGroupBox(u"Custom gradient"_s);

    QPixmap pixmap = gradientBtoYPB_Pixmap();
    auto *gradientBtoYPB = new QPushButton(m_surfaceWidget);
    gradientBtoYPB->setIcon(QIcon(pixmap));
    gradientBtoYPB->setIconSize(pixmap.size());

    pixmap = gradientGtoRPB_Pixmap();
    auto *gradientGtoRPB = new QPushButton(m_surfaceWidget);
    gradientGtoRPB->setIcon(QIcon(pixmap));
    gradientGtoRPB->setIconSize(pixmap.size());

    auto *colorHBox = new QHBoxLayout;
    colorHBox->addWidget(gradientBtoYPB);
    colorHBox->addWidget(gradientGtoRPB);
    colorGroupBox->setLayout(colorHBox);

    // Multiseries heightmap
    auto *showGroupBox = new QGroupBox(u"_show Object"_s);
    showGroupBox->setVisible(false);

    auto *checkboxShowOilRigOne = new QCheckBox(u"Oil Rig 1"_s);
    checkboxShowOilRigOne->setChecked(true);

    auto *checkboxShowOilRigTwo = new QCheckBox(u"Oil Rig 2"_s);
    checkboxShowOilRigTwo->setChecked(true);

    auto *checkboxShowRefinery = new QCheckBox(u"Refinery"_s);

    auto *showVBox = new QVBoxLayout;
    showVBox->addWidget(checkboxShowOilRigOne);
    showVBox->addWidget(checkboxShowOilRigTwo);
    showVBox->addWidget(checkboxShowRefinery);
    showGroupBox->setLayout(showVBox);

    auto *visualsGroupBox = new QGroupBox(u"Visuals"_s);
    visualsGroupBox->setVisible(false);

    auto *checkboxVisualsSeeThrough = new QCheckBox(u"See-Through"_s);

    auto *checkboxHighlightOil = new QCheckBox(u"Highlight Oil"_s);

    auto *checkboxShowShadows = new QCheckBox(u"Shadows"_s);
    checkboxShowShadows->setChecked(true);

    auto *visualVBox = new QVBoxLayout;
    visualVBox->addWidget(checkboxVisualsSeeThrough);
    visualVBox->addWidget(checkboxHighlightOil);
    visualVBox->addWidget(checkboxShowShadows);
    visualsGroupBox->setLayout(visualVBox);

    auto *labelSelection = new QLabel(u"Selection:"_s);
    labelSelection->setVisible(false);

    auto *labelSelectedItem = new QLabel(u"Nothing"_s);
    labelSelectedItem->setVisible(false);

    // Textured topography heightmap
    auto *enableTexture = new QCheckBox(u"Surface texture"_s);
    enableTexture->setVisible(false);

    auto *label = new QLabel(m_surfaceWidget);
    label->setPixmap(highlightPixmap());

    auto *heightMapGroupBox = new QGroupBox(u"Highlight color map"_s);
    auto *colorMapVBox = new QVBoxLayout;
    colorMapVBox->addWidget(label);
    heightMapGroupBox->setLayout(colorMapVBox);
    heightMapGroupBox->setVisible(false);

    // Populate vertical layout
    // Common
    vLayout->addWidget(modelGroupBox);
    vLayout->addWidget(selectionGroupBox);
    vLayout->addWidget(axisGroupBox);

    // Sqrt Sin
    vLayout->addWidget(colorGroupBox);

    // Multiseries heightmap
    vLayout->addWidget(showGroupBox);
    vLayout->addWidget(visualsGroupBox);
    vLayout->addWidget(labelSelection);
    vLayout->addWidget(labelSelectedItem);

    // Textured topography
    vLayout->addWidget(heightMapGroupBox);
    vLayout->addWidget(enableTexture);

    // Raise the graph to the top of the widget stack, to hide UI if resized smaller
    m_surfaceGraph->raise();

    // Create the controller
    m_modifier = new SurfaceGraphModifier(m_surfaceGraph, labelSelectedItem, this);

    // Connect widget controls to controller
    QObject::connect(heightMapModelRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::enableHeightMapModel);
    QObject::connect(sqrtSinModelRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::enableSqrtSinModel);
    QObject::connect(texturedModelRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::enableTopographyModel);

    QObject::connect(modeNoneRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::toggleModeNone);
    QObject::connect(modeItemRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::toggleModeItem);
    QObject::connect(modeSliceRowRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::toggleModeSliceRow);
    QObject::connect(modeSliceColumnRB,
                     &QRadioButton::toggled,
                     m_modifier,
                     &SurfaceGraphModifier::toggleModeSliceColumn);

    QObject::connect(axisMinSliderX,
                     &QSlider::valueChanged,
                     m_modifier,
                     &SurfaceGraphModifier::adjustXMin);
    QObject::connect(axisMaxSliderX,
                     &QSlider::valueChanged,
                     m_modifier,
                     &SurfaceGraphModifier::adjustXMax);
    QObject::connect(axisMinSliderZ,
                     &QSlider::valueChanged,
                     m_modifier,
                     &SurfaceGraphModifier::adjustZMin);
    QObject::connect(axisMaxSliderZ,
                     &QSlider::valueChanged,
                     m_modifier,
                     &SurfaceGraphModifier::adjustZMax);

    // Mode dependent connections
    QObject::connect(gradientBtoYPB,
                     &QPushButton::pressed,
                     m_modifier,
                     &SurfaceGraphModifier::setBlackToYellowGradient);
    QObject::connect(gradientGtoRPB,
                     &QPushButton::pressed,
                     m_modifier,
                     &SurfaceGraphModifier::setGreenToRedGradient);

    QObject::connect(checkboxShowOilRigOne,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleItemOne);
    QObject::connect(checkboxShowOilRigTwo,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleItemTwo);
    QObject::connect(checkboxShowRefinery,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleItemThree);

    QObject::connect(checkboxVisualsSeeThrough,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleSeeThrough);
    QObject::connect(checkboxHighlightOil,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleOilHighlight);
    QObject::connect(checkboxShowShadows,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleShadows);

    QObject::connect(enableTexture,
                     &QCheckBox::stateChanged,
                     m_modifier,
                     &SurfaceGraphModifier::toggleSurfaceTexture);

    // Connections to disable features depending on mode
    QObject::connect(sqrtSinModelRB, &QRadioButton::toggled, colorGroupBox, &QGroupBox::setVisible);

    QObject::connect(heightMapModelRB, &QRadioButton::toggled, showGroupBox, &QGroupBox::setVisible);
    QObject::connect(heightMapModelRB,
                     &QRadioButton::toggled,
                     visualsGroupBox,
                     &QGroupBox::setVisible);
    QObject::connect(heightMapModelRB, &QRadioButton::toggled, labelSelection, &QLabel::setVisible);
    QObject::connect(heightMapModelRB,
                     &QRadioButton::toggled,
                     labelSelectedItem,
                     &QLabel::setVisible);

    QObject::connect(texturedModelRB, &QRadioButton::toggled, enableTexture, &QLabel::setVisible);
    QObject::connect(texturedModelRB,
                     &QRadioButton::toggled,
                     heightMapGroupBox,
                     &QGroupBox::setVisible);

    m_modifier->setAxisMinSliderX(axisMinSliderX);
    m_modifier->setAxisMaxSliderX(axisMaxSliderX);
    m_modifier->setAxisMinSliderZ(axisMinSliderZ);
    m_modifier->setAxisMaxSliderZ(axisMaxSliderZ);

    sqrtSinModelRB->setChecked(true);
    modeItemRB->setChecked(true);
    enableTexture->setChecked(true);
}