- Qt 6.7
- Qt Graphs
- Graph Gallery
- Qt Graphs | Commercial or GPLv3

# Graph Gallery

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

     #include "surfacegraphmodifier.h"
     #include "custominputhandler.h"
     #include "highlightseries.h"
     #include "topographicseries.h"

     #include <QtCore/qmath.h>
     #include <QtGraphs/q3dtheme.h>
     #include <QtGraphs/qvalue3daxis.h>
     #include <QtGui/qimage.h>

     using namespace Qt::StringLiterals;

     const int sampleCountX = 150;
     const int sampleCountZ = 150;
     const int heightMapGridStepX = 6;
     const int heightMapGridStepZ = 6;
     const float sampleMin = -8.f;
     const float sampleMax = 8.f;

     const float areaWidth = 8000.f;
     const float areaHeight = 8000.f;
     const float aspectRatio = 0.1389f;
     const float minRange = areaWidth * 0.49f;

     SurfaceGraphModifier::SurfaceGraphModifier(Q3DSurface *surface, QLabel *label, QObject *parent)
         : QObject(parent)
         , m_graph(surface)
         , m_textField(label)
     {
         m_graph->setCameraZoomLevel(85.f);
         m_graph->setCameraPreset(QAbstract3DGraph::CameraPreset::IsometricRight);
         m_graph->activeTheme()->setType(Q3DTheme::Theme::Retro);

         m_graph->setAxisX(new QValue3DAxis);
         m_graph->setAxisY(new QValue3DAxis);
         m_graph->setAxisZ(new QValue3DAxis);

         //
         // Sqrt Sin
         //
         m_sqrtSinProxy = new QSurfaceDataProxy();
         m_sqrtSinSeries = new QSurface3DSeries(m_sqrtSinProxy);
         fillSqrtSinProxy();

         //
         // Multisurface heightmap
         //
         // Create the first surface layer
         QImage heightMapImageOne(":/data/layer_1.png");
         m_heightMapProxyOne = new QHeightMapSurfaceDataProxy(heightMapImageOne);
         m_heightMapSeriesOne = new QSurface3DSeries(m_heightMapProxyOne);
         m_heightMapSeriesOne->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s);
         m_heightMapProxyOne->setValueRanges(34.f, 40.f, 18.f, 24.f);

         // Create the other 2 surface layers
         QImage heightMapImageTwo(":/data/layer_2.png");
         m_heightMapProxyTwo = new QHeightMapSurfaceDataProxy(heightMapImageTwo);
         m_heightMapSeriesTwo = new QSurface3DSeries(m_heightMapProxyTwo);
         m_heightMapSeriesTwo->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s);
         m_heightMapProxyTwo->setValueRanges(34.f, 40.f, 18.f, 24.f);

         QImage heightMapImageThree(":/data/layer_3.png");
         m_heightMapProxyThree = new QHeightMapSurfaceDataProxy(heightMapImageThree);
         m_heightMapSeriesThree = new QSurface3DSeries(m_heightMapProxyThree);
         m_heightMapSeriesThree->setItemLabelFormat(u"(@xLabel, @zLabel): @yLabel"_s);
         m_heightMapProxyThree->setValueRanges(34.f, 40.f, 18.f, 24.f);

         // The images are the same size, so it's enough to get the dimensions from one
         m_heightMapWidth = heightMapImageOne.width();
         m_heightMapHeight = heightMapImageOne.height();

         // Set the gradients for multi-surface layers
         QLinearGradient grOne;
         grOne.setColorAt(0., Qt::black);
         grOne.setColorAt(0.38, Qt::darkYellow);
         grOne.setColorAt(0.39, Qt::darkGreen);
         grOne.setColorAt(0.5, Qt::darkGray);
         grOne.setColorAt(1., Qt::gray);
         m_heightMapSeriesOne->setBaseGradient(grOne);
         m_heightMapSeriesOne->setColorStyle(Q3DTheme::ColorStyle::RangeGradient);

         QLinearGradient grTwo;
         grTwo.setColorAt(0.39, Qt::blue);
         grTwo.setColorAt(0.4, Qt::white);
         m_heightMapSeriesTwo->setBaseGradient(grTwo);
         m_heightMapSeriesTwo->setColorStyle(Q3DTheme::ColorStyle::RangeGradient);

         QLinearGradient grThree;
         grThree.setColorAt(0., Qt::white);
         grThree.setColorAt(0.05, Qt::black);
         m_heightMapSeriesThree->setBaseGradient(grThree);
         m_heightMapSeriesThree->setColorStyle(Q3DTheme::ColorStyle::RangeGradient);

         // Custom items and label
         connect(m_graph,
                 &QAbstract3DGraph::selectedElementChanged,
                 this,
                 &SurfaceGraphModifier::handleElementSelected);

         m_selectionAnimation = new QPropertyAnimation(this);
         m_selectionAnimation->setPropertyName("scaling");
         m_selectionAnimation->setDuration(500);
         m_selectionAnimation->setLoopCount(-1);

         QFont titleFont = QFont("Century Gothic", 30);
         titleFont.setBold(true);
         m_titleLabel = new QCustom3DLabel("Oil Rigs on Imaginary Sea",
                                           titleFont,
                                           QVector3D(0.f, 1.2f, 0.f),
                                           QVector3D(1.f, 1.f, 0.f),
                                           QQuaternion());
         m_titleLabel->setPositionAbsolute(true);
         m_titleLabel->setFacingCamera(true);
         m_titleLabel->setBackgroundColor(QColor(0x66cdaa));
         m_graph->addCustomItem(m_titleLabel);
         m_titleLabel->setVisible(false);

         // Make two of the custom object visible
         toggleItemOne(true);
         toggleItemTwo(true);

         //
         // Topographic map
         //
         m_topography = new TopographicSeries();
         m_topography->setTopographyFile(":/data/topography.png", areaWidth, areaHeight);
         m_topography->setItemLabelFormat(u"@yLabel m"_s);

         m_highlight = new HighlightSeries();
         m_highlight->setTopographicSeries(m_topography);
         m_highlight->setMinHeight(minRange * aspectRatio);
         m_highlight->handleGradientChange(areaWidth * aspectRatio);
         QObject::connect(m_graph->axisY(),
                          &QValue3DAxis::maxChanged,
                          m_highlight,
                          &HighlightSeries::handleGradientChange);

         m_customInputHandler = new CustomInputHandler(m_graph);
         m_customInputHandler->setHighlightSeries(m_highlight);
         m_customInputHandler->setAxes(m_graph->axisX(), m_graph->axisY(), m_graph->axisZ());
         m_customInputHandler->setLimits(0.f, areaWidth, minRange);
         m_customInputHandler->setAspectRatio(aspectRatio);
     }

     SurfaceGraphModifier::~SurfaceGraphModifier() {}

     void SurfaceGraphModifier::fillSqrtSinProxy()
     {
         float stepX = (sampleMax - sampleMin) / float(sampleCountX - 1);
         float stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1);

         QSurfaceDataArray dataArray;
         dataArray.reserve(sampleCountZ);
         for (int i = 0; i < sampleCountZ; ++i) {
             QSurfaceDataRow newRow;
             newRow.reserve(sampleCountX);
             // Keep values within range bounds, since just adding step can cause minor
             // drift due to the rounding errors.
             float z = qMin(sampleMax, (i * stepZ + sampleMin));
             for (int j = 0; j < sampleCountX; ++j) {
                 float x = qMin(sampleMax, (j * stepX + sampleMin));
                 float R = qSqrt(z * z + x * x) + 0.01f;
                 float y = (qSin(R) / R + 0.24f) * 1.61f;
                 newRow.append(QSurfaceDataItem(x, y, z));
             }
             dataArray.append(newRow);
         }

         m_sqrtSinProxy->resetArray(dataArray);
     }

     void SurfaceGraphModifier::enableSqrtSinModel(bool enable)
     {
         if (enable) {
             m_sqrtSinSeries->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);
             m_sqrtSinSeries->setFlatShadingEnabled(true);

             m_graph->axisX()->setLabelFormat("%.2f");
             m_graph->axisZ()->setLabelFormat("%.2f");
             m_graph->axisX()->setRange(sampleMin, sampleMax);
             m_graph->axisY()->setRange(0.f, 2.f);
             m_graph->axisZ()->setRange(sampleMin, sampleMax);
             m_graph->axisX()->setLabelAutoRotation(30.f);
             m_graph->axisY()->setLabelAutoRotation(90.f);
             m_graph->axisZ()->setLabelAutoRotation(30.f);

             m_graph->removeSeries(m_heightMapSeriesOne);
             m_graph->removeSeries(m_heightMapSeriesTwo);
             m_graph->removeSeries(m_heightMapSeriesThree);
             m_graph->removeSeries(m_topography);
             m_graph->removeSeries(m_highlight);

             m_graph->addSeries(m_sqrtSinSeries);

             m_titleLabel->setVisible(false);
             m_graph->axisX()->setTitleVisible(false);
             m_graph->axisY()->setTitleVisible(false);
             m_graph->axisZ()->setTitleVisible(false);

             m_graph->axisX()->setTitle({});
             m_graph->axisY()->setTitle({});
             m_graph->axisZ()->setTitle({});

             m_graph->setActiveInputHandler(m_defaultInputHandler);

             // Reset range sliders for Sqrt & Sin
             m_rangeMinX = sampleMin;
             m_rangeMinZ = sampleMin;
             m_stepX = (sampleMax - sampleMin) / float(sampleCountX - 1);
             m_stepZ = (sampleMax - sampleMin) / float(sampleCountZ - 1);
             m_axisMinSliderX->setMinimum(0);
             m_axisMinSliderX->setMaximum(sampleCountX - 2);
             m_axisMinSliderX->setValue(0);
             m_axisMaxSliderX->setMinimum(1);
             m_axisMaxSliderX->setMaximum(sampleCountX - 1);
             m_axisMaxSliderX->setValue(sampleCountX - 1);
             m_axisMinSliderZ->setMinimum(0);
             m_axisMinSliderZ->setMaximum(sampleCountZ - 2);
             m_axisMinSliderZ->setValue(0);
             m_axisMaxSliderZ->setMinimum(1);
             m_axisMaxSliderZ->setMaximum(sampleCountZ - 1);
             m_axisMaxSliderZ->setValue(sampleCountZ - 1);
         }
     }

     void SurfaceGraphModifier::enableHeightMapModel(bool enable)
     {
         if (enable) {
             m_heightMapSeriesOne->setDrawMode(QSurface3DSeries::DrawSurface);
             m_heightMapSeriesOne->setFlatShadingEnabled(false);
             m_heightMapSeriesTwo->setDrawMode(QSurface3DSeries::DrawSurface);
             m_heightMapSeriesTwo->setFlatShadingEnabled(false);
             m_heightMapSeriesThree->setDrawMode(QSurface3DSeries::DrawSurface);
             m_heightMapSeriesThree->setFlatShadingEnabled(false);

             m_graph->axisX()->setLabelFormat("%.1f N");
             m_graph->axisZ()->setLabelFormat("%.1f E");
             m_graph->axisX()->setRange(34.f, 40.f);
             m_graph->axisY()->setAutoAdjustRange(true);
             m_graph->axisZ()->setRange(18.f, 24.f);

             m_graph->axisX()->setTitle(u"Latitude"_s);
             m_graph->axisY()->setTitle(u"Height"_s);
             m_graph->axisZ()->setTitle(u"Longitude"_s);

             m_graph->removeSeries(m_sqrtSinSeries);
             m_graph->removeSeries(m_topography);
             m_graph->removeSeries(m_highlight);
             m_graph->addSeries(m_heightMapSeriesOne);
             m_graph->addSeries(m_heightMapSeriesTwo);
             m_graph->addSeries(m_heightMapSeriesThree);

             m_graph->setActiveInputHandler(m_defaultInputHandler);

             m_titleLabel->setVisible(true);
             m_graph->axisX()->setTitleVisible(true);
             m_graph->axisY()->setTitleVisible(true);
             m_graph->axisZ()->setTitleVisible(true);

             // Reset range sliders for height map
             int mapGridCountX = m_heightMapWidth / heightMapGridStepX;
             int mapGridCountZ = m_heightMapHeight / heightMapGridStepZ;
             m_rangeMinX = 34.f;
             m_rangeMinZ = 18.f;
             m_stepX = 6.f / float(mapGridCountX - 1);
             m_stepZ = 6.f / float(mapGridCountZ - 1);
             m_axisMinSliderX->setMinimum(0);
             m_axisMinSliderX->setMaximum(mapGridCountX - 2);
             m_axisMinSliderX->setValue(0);
             m_axisMaxSliderX->setMinimum(1);
             m_axisMaxSliderX->setMaximum(mapGridCountX - 1);
             m_axisMaxSliderX->setValue(mapGridCountX - 1);
             m_axisMinSliderZ->setMinimum(0);
             m_axisMinSliderZ->setMaximum(mapGridCountZ - 2);
             m_axisMinSliderZ->setValue(0);
             m_axisMaxSliderZ->setMinimum(1);
             m_axisMaxSliderZ->setMaximum(mapGridCountZ - 1);
             m_axisMaxSliderZ->setValue(mapGridCountZ - 1);
         }
     }

     void SurfaceGraphModifier::enableTopographyModel(bool enable)
     {
         if (enable) {
             m_graph->axisX()->setLabelFormat("%i");
             m_graph->axisZ()->setLabelFormat("%i");
             m_graph->axisX()->setRange(0.f, areaWidth);
             m_graph->axisY()->setRange(100.f, areaWidth * aspectRatio);
             m_graph->axisZ()->setRange(0.f, areaHeight);
             m_graph->axisX()->setLabelAutoRotation(30.f);
             m_graph->axisY()->setLabelAutoRotation(90.f);
             m_graph->axisZ()->setLabelAutoRotation(30.f);

             m_graph->removeSeries(m_sqrtSinSeries);
             m_graph->removeSeries(m_heightMapSeriesOne);
             m_graph->removeSeries(m_heightMapSeriesTwo);
             m_graph->removeSeries(m_heightMapSeriesThree);
             m_graph->addSeries(m_topography);
             m_graph->addSeries(m_highlight);

             m_titleLabel->setVisible(false);
             m_graph->axisX()->setTitleVisible(false);
             m_graph->axisY()->setTitleVisible(false);
             m_graph->axisZ()->setTitleVisible(false);

             m_graph->axisX()->setTitle({});
             m_graph->axisY()->setTitle({});
             m_graph->axisZ()->setTitle({});

             m_graph->setActiveInputHandler(m_customInputHandler);

             // Reset range sliders for topography map
             m_rangeMinX = 0.f;
             m_rangeMinZ = 0.f;
             m_stepX = 1.f;
             m_stepZ = 1.f;
             m_axisMinSliderX->setMinimum(0);
             m_axisMinSliderX->setMaximum(areaWidth - 200);
             m_axisMinSliderX->setValue(0);
             m_axisMaxSliderX->setMinimum(200);
             m_axisMaxSliderX->setMaximum(areaWidth);
             m_axisMaxSliderX->setValue(areaWidth);
             m_axisMinSliderZ->setMinimum(0);
             m_axisMinSliderZ->setMaximum(areaHeight - 200);
             m_axisMinSliderZ->setValue(0);
             m_axisMaxSliderZ->setMinimum(200);
             m_axisMaxSliderZ->setMaximum(areaHeight);
             m_axisMaxSliderZ->setValue(areaHeight);
         }
     }

     void SurfaceGraphModifier::adjustXMin(int min)
     {
         float minX = m_stepX * float(min) + m_rangeMinX;

         int max = m_axisMaxSliderX->value();
         if (min >= max) {
             max = min + 1;
             m_axisMaxSliderX->setValue(max);
         }
         float maxX = m_stepX * max + m_rangeMinX;

         setAxisXRange(minX, maxX);
     }

     void SurfaceGraphModifier::adjustXMax(int max)
     {
         float maxX = m_stepX * float(max) + m_rangeMinX;

         int min = m_axisMinSliderX->value();
         if (max <= min) {
             min = max - 1;
             m_axisMinSliderX->setValue(min);
         }
         float minX = m_stepX * min + m_rangeMinX;

         setAxisXRange(minX, maxX);
     }

     void SurfaceGraphModifier::adjustZMin(int min)
     {
         float minZ = m_stepZ * float(min) + m_rangeMinZ;

         int max = m_axisMaxSliderZ->value();
         if (min >= max) {
             max = min + 1;
             m_axisMaxSliderZ->setValue(max);
         }
         float maxZ = m_stepZ * max + m_rangeMinZ;

         setAxisZRange(minZ, maxZ);
     }

     void SurfaceGraphModifier::adjustZMax(int max)
     {
         float maxX = m_stepZ * float(max) + m_rangeMinZ;

         int min = m_axisMinSliderZ->value();
         if (max <= min) {
             min = max - 1;
             m_axisMinSliderZ->setValue(min);
         }
         float minX = m_stepZ * min + m_rangeMinZ;

         setAxisZRange(minX, maxX);
     }

     void SurfaceGraphModifier::setAxisXRange(float min, float max)
     {
         m_graph->axisX()->setRange(min, max);
     }

     void SurfaceGraphModifier::setAxisZRange(float min, float max)
     {
         m_graph->axisZ()->setRange(min, max);
     }

     void SurfaceGraphModifier::setBlackToYellowGradient()
     {
         QLinearGradient gr;
         gr.setColorAt(0.f, Qt::black);
         gr.setColorAt(0.33f, Qt::blue);
         gr.setColorAt(0.67f, Qt::red);
         gr.setColorAt(1.f, Qt::yellow);

         m_sqrtSinSeries->setBaseGradient(gr);
         m_sqrtSinSeries->setColorStyle(Q3DTheme::ColorStyle::RangeGradient);
     }

     void SurfaceGraphModifier::setGreenToRedGradient()
     {
         QLinearGradient gr;
         gr.setColorAt(0.f, Qt::darkGreen);
         gr.setColorAt(0.5f, Qt::yellow);
         gr.setColorAt(0.8f, Qt::red);
         gr.setColorAt(1.f, Qt::darkRed);

         m_sqrtSinSeries->setBaseGradient(gr);
         m_sqrtSinSeries->setColorStyle(Q3DTheme::ColorStyle::RangeGradient);
     }

     void SurfaceGraphModifier::toggleItemOne(bool show)
     {
         QVector3D positionOne = QVector3D(39.f, 77.f, 19.2f);
         QVector3D positionOnePipe = QVector3D(39.f, 45.f, 19.2f);
         QVector3D positionOneLabel = QVector3D(39.f, 107.f, 19.2f);
         if (show) {
             QImage color = QImage(2, 2, QImage::Format_RGB32);
             color.fill(Qt::red);
             auto *item = new QCustom3DItem(":/data/oilrig.mesh",
                                            positionOne,
                                            QVector3D(0.025f, 0.025f, 0.025f),
                                            QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 45.f),
                                            color);
             m_graph->addCustomItem(item);
             item = new QCustom3DItem(":/data/pipe.mesh",
                                      positionOnePipe,
                                      QVector3D(0.005f, 0.5f, 0.005f),
                                      QQuaternion(),
                                      color);
             item->setShadowCasting(false);
             m_graph->addCustomItem(item);

             auto *label = new QCustom3DLabel();
             label->setText("Oil Rig One");
             label->setPosition(positionOneLabel);
             label->setScaling(QVector3D(1.f, 1.f, 1.f));
             m_graph->addCustomItem(label);
         } else {
             resetSelection();
             m_graph->removeCustomItemAt(positionOne);
             m_graph->removeCustomItemAt(positionOnePipe);
             m_graph->removeCustomItemAt(positionOneLabel);
         }
     }

     void SurfaceGraphModifier::toggleItemTwo(bool show)
     {
         QVector3D positionTwo = QVector3D(34.5f, 77.f, 23.4f);
         QVector3D positionTwoPipe = QVector3D(34.5f, 45.f, 23.4f);
         QVector3D positionTwoLabel = QVector3D(34.5f, 107.f, 23.4f);
         if (show) {
             QImage color = QImage(2, 2, QImage::Format_RGB32);
             color.fill(Qt::red);
             auto *item = new QCustom3DItem();
             item->setMeshFile(":/data/oilrig.mesh");
             item->setPosition(positionTwo);
             item->setScaling(QVector3D(0.025f, 0.025f, 0.025f));
             item->setRotation(QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 25.f));
             item->setTextureImage(color);
             m_graph->addCustomItem(item);
             item = new QCustom3DItem(":/data/pipe.mesh",
                                      positionTwoPipe,
                                      QVector3D(0.005f, 0.5f, 0.005f),
                                      QQuaternion(),
                                      color);
             item->setShadowCasting(false);
             m_graph->addCustomItem(item);

             auto *label = new QCustom3DLabel();
             label->setText("Oil Rig Two");
             label->setPosition(positionTwoLabel);
             label->setScaling(QVector3D(1.f, 1.f, 1.f));
             m_graph->addCustomItem(label);
         } else {
             resetSelection();
             m_graph->removeCustomItemAt(positionTwo);
             m_graph->removeCustomItemAt(positionTwoPipe);
             m_graph->removeCustomItemAt(positionTwoLabel);
         }
     }

     void SurfaceGraphModifier::toggleItemThree(bool show)
     {
         QVector3D positionThree = QVector3D(34.5f, 86.f, 19.1f);
         QVector3D positionThreeLabel = QVector3D(34.5f, 116.f, 19.1f);
         if (show) {
             QImage color = QImage(2, 2, QImage::Format_RGB32);
             color.fill(Qt::darkMagenta);
             auto *item = new QCustom3DItem();
             item->setMeshFile(":/data/refinery.mesh");
             item->setPosition(positionThree);
             item->setScaling(QVector3D(0.04f, 0.04f, 0.04f));
             item->setRotation(QQuaternion::fromAxisAndAngle(0.f, 1.f, 0.f, 75.f));
             item->setTextureImage(color);
             m_graph->addCustomItem(item);

             auto *label = new QCustom3DLabel();
             label->setText("Refinery");
             label->setPosition(positionThreeLabel);
             label->setScaling(QVector3D(1.f, 1.f, 1.f));
             m_graph->addCustomItem(label);
         } else {
             resetSelection();
             m_graph->removeCustomItemAt(positionThree);
             m_graph->removeCustomItemAt(positionThreeLabel);
         }
     }

     void SurfaceGraphModifier::toggleSeeThrough(bool seethrough)
     {
         if (seethrough) {
             m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawWireframe);
             m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawWireframe);
         } else {
             m_graph->seriesList().at(0)->setDrawMode(QSurface3DSeries::DrawSurface);
             m_graph->seriesList().at(1)->setDrawMode(QSurface3DSeries::DrawSurface);
         }
     }

     void SurfaceGraphModifier::toggleOilHighlight(bool highlight)
     {
         if (highlight) {
             QLinearGradient grThree;
             grThree.setColorAt(0.0, Qt::black);
             grThree.setColorAt(0.05, Qt::red);
             m_graph->seriesList().at(2)->setBaseGradient(grThree);
         } else {
             QLinearGradient grThree;
             grThree.setColorAt(0.0, Qt::white);
             grThree.setColorAt(0.05, Qt::black);
             m_graph->seriesList().at(2)->setBaseGradient(grThree);
         }
     }

     void SurfaceGraphModifier::toggleShadows(bool shadows)
     {
         if (shadows)
             m_graph->setShadowQuality(QAbstract3DGraph::ShadowQuality::Medium);
         else
             m_graph->setShadowQuality(QAbstract3DGraph::ShadowQuality::None);
     }

     void SurfaceGraphModifier::toggleSurfaceTexture(bool enable)
     {
         if (enable)
             m_topography->setTextureFile(":/data/maptexture.jpg");
         else
             m_topography->setTextureFile("");
     }

     void SurfaceGraphModifier::handleElementSelected(QAbstract3DGraph::ElementType type)
     {
         resetSelection();
         if (type == QAbstract3DGraph::ElementType::CustomItem) {
             QCustom3DItem *item = m_graph->selectedCustomItem();
             QString text;
             if (qobject_cast<QCustom3DLabel *>(item) != 0) {
                 text.append("Custom label: ");
             } else {
                 QStringList split = item->meshFile().split("/");
                 text.append(split.last());
                 text.append(": ");
             }
             int index = m_graph->selectedCustomItemIndex();
             text.append(QString::number(index));
             m_textField->setText(text);
             m_previouslyAnimatedItem = item;
             m_previousScaling = item->scaling();
             m_selectionAnimation->setTargetObject(item);
             m_selectionAnimation->setStartValue(item->scaling());
             m_selectionAnimation->setEndValue(item->scaling() * 1.5f);
             m_selectionAnimation->start();
         } else if (type == QAbstract3DGraph::ElementType::Series) {
             QString text = "Surface (";
             QSurface3DSeries *series = m_graph->selectedSeries();
             if (series) {
                 QPoint point = series->selectedPoint();
                 QString posStr;
                 posStr.setNum(point.x());
                 text.append(posStr);
                 text.append(", ");
                 posStr.setNum(point.y());
                 text.append(posStr);
             }
             text.append(")");
             m_textField->setText(text);
         } else if (type > QAbstract3DGraph::ElementType::Series
                    && type < QAbstract3DGraph::ElementType::CustomItem) {
             int index = m_graph->selectedLabelIndex();
             QString text;
             if (type == QAbstract3DGraph::ElementType::AxisXLabel)
                 text.append("Axis X label: ");
             else if (type == QAbstract3DGraph::ElementType::AxisYLabel)
                 text.append("Axis Y label: ");
             else
                 text.append("Axis Z label: ");
             text.append(QString::number(index));
             m_textField->setText(text);
         } else {
             m_textField->setText("Nothing");
         }
     }

     void SurfaceGraphModifier::resetSelection()
     {
         m_selectionAnimation->stop();
         if (m_previouslyAnimatedItem)
             m_previouslyAnimatedItem->setScaling(m_previousScaling);
         m_previouslyAnimatedItem = nullptr;
     }