Qt Quick 2 Surface Multiseries Example

Using multiple series with Surface3D in a QML application.

The Qt Quick 2 surface example shows how to make a 3D surface plot displaying 3 layers using Surface3D with Qt Quick 2.

The focus in this example is on generating a multiseries surface plot from 3 different height map images, so in this section we skip explaining the application creation. For a more detailed QML example documentation, see Qt Quick 2 Scatter Example.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

Adding Data to the Graph

This example shows how to add several surface series to one graph using using HeightMapSurfaceDataProxies and how to control their visibilities individually.

Let's start by creating a specific gradient for each layer:

 ColorGradient {
     id: layerOneGradient
     ColorGradientStop { position: 0.0; color: "black" }
     ColorGradientStop { position: 0.31; color: "tan" }
     ColorGradientStop { position: 0.32; color: "green" }
     ColorGradientStop { position: 0.40; color: "darkslategray" }
     ColorGradientStop { position: 1.0; color: "white" }
 }

 ColorGradient {
     id: layerTwoGradient
     ColorGradientStop { position: 0.315; color: "blue" }
     ColorGradientStop { position: 0.33; color: "white" }
 }

 ColorGradient {
     id: layerThreeGradient
     ColorGradientStop { position: 0.0; color: "red" }
     ColorGradientStop { position: 0.15; color: "black" }
 }

Then we'll create the series themselves. It happens simply by adding 3 separate Surface3DSeries to the Surface3D graph as children:

 ...
 Surface3DSeries {
     id: layerOneSeries
     baseGradient: layerOneGradient
     HeightMapSurfaceDataProxy {
         heightMapFile: ":/heightmaps/layer_1.png"
     }
     flatShadingEnabled: false
     drawMode: Surface3DSeries.DrawSurface
     visible: layerOneToggle.checked // bind to checkbox state
 }

 Surface3DSeries {
     id: layerTwoSeries
     baseGradient: layerTwoGradient
     HeightMapSurfaceDataProxy {
         heightMapFile: ":/heightmaps/layer_2.png"
     }
     flatShadingEnabled: false
     drawMode: Surface3DSeries.DrawSurface
     visible: layerTwoToggle.checked // bind to checkbox state
 }

 Surface3DSeries {
     id: layerThreeSeries
     baseGradient: layerThreeGradient
     HeightMapSurfaceDataProxy {
         heightMapFile: ":/heightmaps/layer_3.png"
     }
     flatShadingEnabled: false
     drawMode: Surface3DSeries.DrawSurface
     visible: layerThreeToggle.checked // bind to checkbox state
 }
 ...

You'll notice we added the created gradients to the baseGradient properties of the series. We could have added them to the baseGradients property of the Theme3D in Surface3D instead, but doing it this way ensures each gradient is applied to a correct series:

 Surface3DSeries {
     id: layerOneSeries
     baseGradient: layerOneGradient
     ...

Controlling the Graph

Let's add some checkboxes to control the visibility of layers:

 GroupBox {
     flat: true
     Layout.fillWidth: true
     Column {
         spacing: 10

         Label {
             font.pointSize: fontSize
             font.bold: true
             text: "Layer Selection"
         }

         CheckBox {
             id: layerOneToggle
             checked: true
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Ground Layer"
                 }
             }
         }

         CheckBox {
             id: layerTwoToggle
             checked: true
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Sea Layer"
                 }
             }
         }

         CheckBox {
             id: layerThreeToggle
             checked: true
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Tectonic Layer"
                 }
             }
         }
     }
 }

We don't need to do anything on the onCheckedChanged as we bound the checked state to the visible property of the series directly:

 ...
 visible: layerOneToggle.checked // bind to checkbox state
 ...

Let's add some more checkboxes to control how the layers are displayed, when visible:

 GroupBox {
     flat: true
     Layout.fillWidth: true
     Column {
         spacing: 10

         Label {
             font.pointSize: fontSize
             font.bold: true
             text: "Layer Style"
         }

         CheckBox {
             id: layerOneGrid
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Ground as Grid"
                 }
             }
             onCheckedChanged: {
                 if (checked)
                     layerOneSeries.drawMode = Surface3DSeries.DrawWireframe
                 else
                     layerOneSeries.drawMode = Surface3DSeries.DrawSurface
             }
         }

         CheckBox {
             id: layerTwoGrid
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Sea as Grid"
                 }
             }
             onCheckedChanged: {
                 if (checked)
                     layerTwoSeries.drawMode = Surface3DSeries.DrawWireframe
                 else
                     layerTwoSeries.drawMode = Surface3DSeries.DrawSurface
             }
         }

         CheckBox {
             id: layerThreeGrid
             style: CheckBoxStyle {
                 label: Label {
                     font.pointSize: fontSize
                     text: "Show Tectonic as Grid"
                 }
             }
             onCheckedChanged: {
                 if (checked)
                     layerThreeSeries.drawMode = Surface3DSeries.DrawWireframe
                 else
                     layerThreeSeries.drawMode = Surface3DSeries.DrawSurface
             }
         }
     }
 }

In addition to these we have three buttons, one of which is of special interest to us. It is used to control whether we want to slice into only one layer, or all of them:

 NewButton {
     id: sliceButton
     text: "Slice All Layers"
     fontSize: fontSize
     Layout.fillWidth: true
     Layout.minimumHeight: 40
     onClicked: {
         if (surfaceLayers.selectionMode & AbstractGraph3D.SelectionMultiSeries) {
             surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                     | AbstractGraph3D.SelectionSlice
             text = "Slice All Layers"
         } else {
             surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                     | AbstractGraph3D.SelectionSlice
                     | AbstractGraph3D.SelectionMultiSeries
             text = "Slice One Layer"
         }
     }
 }

Example Contents

Example project @ code.qt.io