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
 import SurfaceGallery

 Item {
     id: oscilloscopeView

     property int sampleColumns: sampleSlider.value
     property int sampleRows: sampleColumns / 2
     property int sampleCache: 24

     required property bool portraitMode

     property real controlWidth: oscilloscopeView.portraitMode ? oscilloscopeView.width - 10
                                                               : oscilloscopeView.width / 4 - 6.66

     property real buttonWidth: oscilloscopeView.portraitMode ? oscilloscopeView.width - 10
                                                              : oscilloscopeView.width / 3 - 7.5

     onSampleRowsChanged: {
         surfaceSeries.selectedPoint = surfaceSeries.invalidSelectionPosition
         generateData()
     }

     DataSource {
         id: dataSource
     }

     Item {
         id: dataView
         anchors.bottom: parent.bottom
         width: parent.width
         height: parent.height - controlArea.height

         Surface3D {
             id: surfaceGraph
             anchors.fill: parent

             Surface3DSeries {
                 id: surfaceSeries
                 drawMode: Surface3DSeries.DrawSurfaceAndWireframe
                 itemLabelFormat: "@xLabel, @zLabel: @yLabel"
                 itemLabelVisible: false

                 onItemLabelChanged: {
                     if (surfaceSeries.selectedPoint == surfaceSeries.invalidSelectionPosition)
                         selectionText.text = "No selection";
                     else
                         selectionText.text = surfaceSeries.itemLabel;
                 }
             }

             shadowQuality: AbstractGraph3D.ShadowQuality.None
             selectionMode: AbstractGraph3D.SelectionSlice | AbstractGraph3D.SelectionItemAndColumn
             theme: Theme3D {
                 type: Theme3D.Theme.Isabelle
                 backgroundEnabled: false
             }
             cameraPreset: AbstractGraph3D.CameraPreset.FrontHigh

             axisX.labelFormat: "%d ms"
             axisY.labelFormat: "%d W"
             axisZ.labelFormat: "%d mV"
             axisX.min: 0
             axisY.min: 0
             axisZ.min: 0
             axisX.max: 1000
             axisY.max: 100
             axisZ.max: 800
             axisX.segmentCount: 4
             axisY.segmentCount: 4
             axisZ.segmentCount: 4
             measureFps: true
             renderingMode: AbstractGraph3D.RenderingMode.DirectToBackground

             onCurrentFpsChanged: (currentFps)=> {
                                      fpsText.text = "FPS: " + currentFps;
                                  }

             Component.onCompleted: oscilloscopeView.generateData();
         }
     }

     Timer {
         id: refreshTimer
         interval: 1000 / frequencySlider.value
         running: true
         repeat: true
         onTriggered: dataSource.update(surfaceSeries);
     }

     Rectangle {
         id: controlArea
         height: oscilloscopeView.portraitMode ? flatShadingToggle.implicitHeight * 7
                                               : flatShadingToggle.implicitHeight * 2
         anchors.left: parent.left
         anchors.top: parent.top
         anchors.right: parent.right
         color: surfaceGraph.theme.backgroundColor

         // Samples
         Rectangle {
             id: samples
             width: oscilloscopeView.controlWidth
             height: flatShadingToggle.implicitHeight
             anchors.left: parent.left
             anchors.top: parent.top
             anchors.margins: 5

             color: surfaceGraph.theme.windowColor
             border.color: surfaceGraph.theme.gridLineColor
             border.width: 1
             radius: 4

             Row {
                 anchors.centerIn: parent
                 spacing: 10
                 padding: 5

                 Slider {
                     id: sampleSlider
                     from: oscilloscopeView.sampleCache * 2
                     to: from * 10
                     stepSize: oscilloscopeView.sampleCache

                     background: Rectangle {
                         x: sampleSlider.leftPadding
                         y: sampleSlider.topPadding + sampleSlider.availableHeight / 2
                            - height / 2
                         implicitWidth: 200
                         implicitHeight: 4
                         width: sampleSlider.availableWidth
                         height: implicitHeight
                         radius: 2
                         color: surfaceGraph.theme.gridLineColor

                         Rectangle {
                             width: sampleSlider.visualPosition * parent.width
                             height: parent.height
                             color: surfaceGraph.theme.labelTextColor
                             radius: 2
                         }
                     }

                     handle: Rectangle {
                         x: sampleSlider.leftPadding + sampleSlider.visualPosition
                            * (sampleSlider.availableWidth - width)
                         y: sampleSlider.topPadding + sampleSlider.availableHeight / 2
                            - height / 2
                         implicitWidth: 20
                         implicitHeight: 20
                         radius: 10
                         color: sampleSlider.pressed ? surfaceGraph.theme.gridLineColor
                                                     : surfaceGraph.theme.windowColor
                         border.color: sampleSlider.pressed ? surfaceGraph.theme.labelTextColor
                                                            : surfaceGraph.theme.gridLineColor
                     }

                     Component.onCompleted: value = from;
                 }

                 Text {
                     id: samplesText
                     text: "Samples: " + (oscilloscopeView.sampleRows * oscilloscopeView.sampleColumns)
                     verticalAlignment: Text.AlignVCenter
                     horizontalAlignment: Text.AlignHCenter
                     color: surfaceGraph.theme.labelTextColor
                 }
             }
         }

         // Frequency
         Rectangle {
             id: frequency
             width: oscilloscopeView.controlWidth
             height: flatShadingToggle.implicitHeight
             anchors.left: oscilloscopeView.portraitMode ? parent.left : samples.right
             anchors.top: oscilloscopeView.portraitMode ? samples.bottom : parent.top
             anchors.margins: 5

             color: surfaceGraph.theme.windowColor
             border.color: surfaceGraph.theme.gridLineColor
             border.width: 1
             radius: 4

             Row {
                 anchors.centerIn: parent
                 spacing: 10
                 padding: 5

                 Slider {
                     id: frequencySlider
                     from: 2
                     to: 60
                     stepSize: 2
                     value: 30

                     background: Rectangle {
                         x: frequencySlider.leftPadding
                         y: frequencySlider.topPadding + frequencySlider.availableHeight / 2
                            - height / 2
                         implicitWidth: 200
                         implicitHeight: 4
                         width: frequencySlider.availableWidth
                         height: implicitHeight
                         radius: 2
                         color: surfaceGraph.theme.gridLineColor

                         Rectangle {
                             width: frequencySlider.visualPosition * parent.width
                             height: parent.height
                             color: surfaceGraph.theme.labelTextColor
                             radius: 2
                         }
                     }

                     handle: Rectangle {
                         x: frequencySlider.leftPadding + frequencySlider.visualPosition
                            * (frequencySlider.availableWidth - width)
                         y: frequencySlider.topPadding + frequencySlider.availableHeight / 2
                            - height / 2
                         implicitWidth: 20
                         implicitHeight: 20
                         radius: 10
                         color: frequencySlider.pressed ? surfaceGraph.theme.gridLineColor
                                                        : surfaceGraph.theme.windowColor
                         border.color: frequencySlider.pressed ? surfaceGraph.theme.labelTextColor
                                                               : surfaceGraph.theme.gridLineColor
                     }
                 }

                 Text {
                     id: frequencyText
                     text: "Freq: " + frequencySlider.value + " Hz"
                     verticalAlignment: Text.AlignVCenter
                     horizontalAlignment: Text.AlignHCenter
                     color: surfaceGraph.theme.labelTextColor
                 }
             }
         }

         // FPS
         Rectangle {
             id: fpsindicator
             width: oscilloscopeView.controlWidth
             height: flatShadingToggle.implicitHeight
             anchors.left: oscilloscopeView.portraitMode ? parent.left : frequency.right
             anchors.top: oscilloscopeView.portraitMode ? frequency.bottom : parent.top
             anchors.margins: 5

             color: surfaceGraph.theme.windowColor
             border.color: surfaceGraph.theme.gridLineColor
             border.width: 1
             radius: 4

             Text {
                 id: fpsText
                 anchors.fill: parent
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
                 color: surfaceGraph.theme.labelTextColor
             }
         }

         // Selection
         Rectangle {
             id: selection
             width: oscilloscopeView.controlWidth
             height: flatShadingToggle.implicitHeight
             anchors.left: oscilloscopeView.portraitMode ? parent.left : fpsindicator.right
             anchors.top: oscilloscopeView.portraitMode ? fpsindicator.bottom : parent.top
             anchors.margins: 5

             color: surfaceGraph.theme.windowColor
             border.color: surfaceGraph.theme.gridLineColor
             border.width: 1
             radius: 4

             Text {
                 id: selectionText
                 anchors.fill: parent
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
                 text: "No selection"
                 color: surfaceGraph.theme.labelTextColor
             }
         }

         // Flat shading
         Button {
             id: flatShadingToggle
             width: oscilloscopeView.buttonWidth
             anchors.left: parent.left
             anchors.top: selection.bottom
             anchors.margins: 5

             text: surfaceSeries.flatShadingSupported ? "Show\nSmooth" : "Flat\nnot supported"
             enabled: surfaceSeries.flatShadingSupported

             onClicked: {
                 if (surfaceSeries.flatShadingEnabled) {
                     surfaceSeries.flatShadingEnabled = false;
                     text = "Show\nFlat"
                 } else {
                     surfaceSeries.flatShadingEnabled = true;
                     text = "Show\nSmooth"
                 }
             }

             contentItem: Text {
                 text: flatShadingToggle.text
                 opacity: flatShadingToggle.enabled ? 1.0 : 0.3
                 color: surfaceGraph.theme.labelTextColor
                 horizontalAlignment: Text.AlignHCenter
                 verticalAlignment: Text.AlignVCenter
                 elide: Text.ElideRight
             }

             background: Rectangle {
                 opacity: flatShadingToggle.enabled ? 1 : 0.3
                 color: flatShadingToggle.down ? surfaceGraph.theme.gridLineColor
                                               : surfaceGraph.theme.windowColor
                 border.color: flatShadingToggle.down ? surfaceGraph.theme.labelTextColor
                                                      : surfaceGraph.theme.gridLineColor
                 border.width: 1
                 radius: 2
             }
         }

         // Surface grid
         Button {
             id: surfaceGridToggle
             width: oscilloscopeView.buttonWidth
             anchors.left: oscilloscopeView.portraitMode ? parent.left : flatShadingToggle.right
             anchors.top: oscilloscopeView.portraitMode ? flatShadingToggle.bottom : selection.bottom
             anchors.margins: 5

             text: "Hide\nSurface Grid"

             onClicked: {
                 if (surfaceSeries.drawMode & Surface3DSeries.DrawWireframe) {
                     surfaceSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
                     text = "Show\nSurface Grid";
                 } else {
                     surfaceSeries.drawMode |= Surface3DSeries.DrawWireframe;
                     text = "Hide\nSurface Grid";
                 }
             }

             contentItem: Text {
                 text: surfaceGridToggle.text
                 color: surfaceGraph.theme.labelTextColor
                 horizontalAlignment: Text.AlignHCenter
                 verticalAlignment: Text.AlignVCenter
                 elide: Text.ElideRight
             }

             background: Rectangle {
                 color: surfaceGridToggle.down ? surfaceGraph.theme.gridLineColor
                                               : surfaceGraph.theme.windowColor
                 border.color: surfaceGridToggle.down ? surfaceGraph.theme.labelTextColor
                                                      : surfaceGraph.theme.gridLineColor
                 border.width: 1
                 radius: 2
             }
         }

         // Exit
         Button {
             id: exitButton
             width: oscilloscopeView.buttonWidth
             height: surfaceGridToggle.height
             anchors.left: oscilloscopeView.portraitMode ? parent.left : surfaceGridToggle.right
             anchors.top: oscilloscopeView.portraitMode ? surfaceGridToggle.bottom : selection.bottom
             anchors.margins: 5

             text: "Quit"

             onClicked: Qt.quit();

             contentItem: Text {
                 text: exitButton.text
                 color: surfaceGraph.theme.labelTextColor
                 horizontalAlignment: Text.AlignHCenter
                 verticalAlignment: Text.AlignVCenter
                 elide: Text.ElideRight
             }

             background: Rectangle {
                 color: exitButton.down ? surfaceGraph.theme.gridLineColor
                                        : surfaceGraph.theme.windowColor
                 border.color: exitButton.down ? surfaceGraph.theme.labelTextColor
                                               : surfaceGraph.theme.gridLineColor
                 border.width: 1
                 radius: 2
             }
         }
     }

     function generateData() {
         dataSource.generateData(oscilloscopeView.sampleCache, oscilloscopeView.sampleRows,
                                 oscilloscopeView.sampleColumns,
                                 surfaceGraph.axisX.min, surfaceGraph.axisX.max,
                                 surfaceGraph.axisY.min, surfaceGraph.axisY.max,
                                 surfaceGraph.axisZ.min, surfaceGraph.axisZ.max);
     }
 }