- Qt 6.6
- Qt Graphs
- Surface Graph Gallery
- Qt Graphs | Commercial or GPLv3

# Surface Graph Gallery

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

     import QtQuick
     import QtQuick.Controls
     import QtGraphs

     Rectangle {
         id: spectrogramView
         color: surfaceGraph.theme.windowColor

         required property bool portraitMode

         property real buttonWidth: spectrogramView.portraitMode ? (spectrogramView.width - 35) / 2
                                                                 : (spectrogramView.width - 50) / 5

         SpectrogramData {
             id: surfaceData
         }

         Item {
             id: surfaceView
             anchors.top: buttons.bottom
             anchors.left: parent.left
             anchors.right: legend.left
             anchors.bottom: parent.bottom

             ColorGradient {
                 id: surfaceGradient
                 ColorGradientStop { position: 0.0; color: "black" }
                 ColorGradientStop { position: 0.2; color: "red" }
                 ColorGradientStop { position: 0.5; color: "blue" }
                 ColorGradientStop { position: 0.8; color: "yellow" }
                 ColorGradientStop { position: 1.0; color: "white" }
             }

             ValueAxis3D {
                 id: xAxis
                 segmentCount: 8
                 labelFormat: "%i\u00B0"
                 title: "Angle"
                 titleVisible: true
                 titleFixed: false
             }

             ValueAxis3D {
                 id: yAxis
                 segmentCount: 8
                 labelFormat: "%i \%"
                 title: "Value"
                 titleVisible: true
                 labelAutoRotation: 0
                 titleFixed: false
             }

             ValueAxis3D {
                 id: zAxis
                 segmentCount: 5
                 labelFormat: "%i nm"
                 title: "Radius"
                 titleVisible: true
                 titleFixed: false
             }

             Theme3D {
                 id: customTheme
                 type: Theme3D.ThemeQt
                 // Don't show specular spotlight as we don't want it to distort the colors
                 lightStrength: 0.0
                 ambientLightStrength: 1.0
                 backgroundEnabled: false
                 gridLineColor: "#AAAAAA"
                 windowColor: "#EEEEEE"
             }

             Surface3D {
                 id: surfaceGraph
                 anchors.fill: parent

                 Surface3DSeries {
                     id: surfaceSeries
                     flatShadingEnabled: false
                     drawMode: Surface3DSeries.DrawSurface
                     baseGradient: surfaceGradient
                     colorStyle: Theme3D.ColorStyleRangeGradient
                     itemLabelFormat: "(@xLabel, @zLabel): @yLabel"

                     ItemModelSurfaceDataProxy {
                         itemModel: surfaceData.model
                         rowRole: "radius"
                         columnRole: "angle"
                         yPosRole: "value"
                     }
                 }

                 // Remove the perspective and view the graph from top down to achieve 2D effect
                 orthoProjection: true
                 scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove

                 flipHorizontalGrid: true

                 radialLabelOffset: 0.01

                 inputHandler: TouchInputHandler3D {
                     rotationEnabled: !surfaceGraph.orthoProjection
                 }

                 theme: customTheme
                 shadowQuality: AbstractGraph3D.ShadowQualityNone
                 selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndColumn
                 axisX: xAxis
                 axisY: yAxis
                 axisZ: zAxis

                 aspectRatio: 1.0
                 horizontalAspectRatio: 1.0
                 scene.activeCamera.zoomLevel: 140
             }
         }

         Item {
             id: buttons
             anchors.top: parent.top
             anchors.left: parent.left
             anchors.right: parent.right
             height: spectrogramView.portraitMode ? (polarToggle.height + 10) * 3
                                                  : polarToggle.height + 30
             anchors.margins: 10

             Button {
                 id: polarToggle
                 anchors.margins: 5
                 anchors.left: parent.left
                 anchors.top: parent.top
                 width: spectrogramView.buttonWidth // Calculated elsewhere based on screen orientation
                 text: "Switch to\n" + (surfaceGraph.polar ? "cartesian" : "polar")
                 onClicked: surfaceGraph.polar = !surfaceGraph.polar;
             }

             Button {
                 id: orthoToggle
                 anchors.margins: 5
                 anchors.left: polarToggle.right
                 anchors.top: parent.top
                 width: spectrogramView.buttonWidth
                 text: "Switch to\n" + (surfaceGraph.orthoProjection ? "perspective" : "orthographic")
                 onClicked: {
                     if (surfaceGraph.orthoProjection) {
                         surfaceGraph.orthoProjection = false;
                         xAxis.labelAutoRotation = 30;
                         yAxis.labelAutoRotation = 30;
                         zAxis.labelAutoRotation = 30;
                     } else {
                         surfaceGraph.orthoProjection = true;
                         surfaceGraph.scene.activeCamera.cameraPreset
                                 = Camera3D.CameraPresetDirectlyAbove;
                         surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
                         xAxis.labelAutoRotation = 0;
                         yAxis.labelAutoRotation = 0;
                         zAxis.labelAutoRotation = 0;
                     }
                 }
             }

             Button {
                 id: flipGridToggle
                 anchors.margins: 5
                 anchors.left: spectrogramView.portraitMode ? parent.left : orthoToggle.right
                 anchors.top: spectrogramView.portraitMode ? orthoToggle.bottom : parent.top
                 width: spectrogramView.buttonWidth
                 text: "Toggle axis\ngrid on top"
                 onClicked: surfaceGraph.flipHorizontalGrid = !surfaceGraph.flipHorizontalGrid;
             }

             Button {
                 id: labelOffsetToggle
                 anchors.margins: 5
                 anchors.left: flipGridToggle.right
                 anchors.top: spectrogramView.portraitMode ? orthoToggle.bottom : parent.top
                 width: spectrogramView.buttonWidth
                 text: "Toggle radial\nlabel position"
                 visible: surfaceGraph.polar
                 onClicked: {
                     if (surfaceGraph.radialLabelOffset >= 1.0)
                         surfaceGraph.radialLabelOffset = 0.01;
                     else
                         surfaceGraph.radialLabelOffset = 1.0;
                 }
             }

             Button {
                 id: surfaceGridToggle
                 anchors.margins: 5
                 anchors.left: spectrogramView.portraitMode ? (labelOffsetToggle.visible ? parent.left
                                                                                         : flipGridToggle.right)
                                                            : (labelOffsetToggle.visible ? labelOffsetToggle.right
                                                                                         : flipGridToggle.right)
                 anchors.top: spectrogramView.portraitMode ? (labelOffsetToggle.visible ? labelOffsetToggle.bottom
                                                                                        : orthoToggle.bottom)
                                                           : parent.top
                 width: spectrogramView.buttonWidth
                 text: "Toggle\nsurface grid"
                 visible: !surfaceGraph.orthoProjection
                 onClicked: {
                     if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe)
                         surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
                     else
                         surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe;
                 }
             }
         }

         Item {
             id: legend
             anchors.bottom: parent.bottom
             anchors.top: buttons.bottom
             anchors.right: parent.right
             width: spectrogramView.portraitMode ? 100 : 125

             Rectangle {
                 id: gradient
                 anchors.margins: 20
                 anchors.bottom: legend.bottom
                 anchors.top: legend.top
                 anchors.right: legend.right
                 border.color: "black"
                 border.width: 1
                 width: spectrogramView.portraitMode ? 25 : 50
                 rotation: 180
                 gradient: Gradient {
                     GradientStop { position: 0.0; color: "black" }
                     GradientStop { position: 0.2; color: "red" }
                     GradientStop { position: 0.5; color: "blue" }
                     GradientStop { position: 0.8; color: "yellow" }
                     GradientStop { position: 1.0; color: "white" }
                 }
             }

             Text {
                 anchors.verticalCenter: gradient.bottom
                 anchors.right: gradient.left
                 anchors.margins: 2
                 text: surfaceGraph.axisY.min  + "%"
             }

             Text {
                 anchors.verticalCenter: gradient.verticalCenter
                 anchors.right: gradient.left
                 anchors.margins: 2
                 text: (surfaceGraph.axisY.max + surfaceGraph.axisY.min) / 2  + "%"
             }

             Text {
                 anchors.verticalCenter: gradient.top
                 anchors.right: gradient.left
                 anchors.margins: 2
                 text: surfaceGraph.axisY.max + "%"
             }
         }
     }