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 {
    Layout.fillWidth: true
    Column {
        spacing: 10

        Label {
            font.pointSize: fontSize
            font.bold: true
            text: portraitMode ? "Layer\nSelection" : "Layer Selection"
        }

        CheckBox {
            id: layerOneToggle
            checked: true
            text: portraitMode ? "Show\nGround\nLayer" : "Show Ground Layer"
        }

        CheckBox {
            id: layerTwoToggle
            checked: true
            text: portraitMode ? "Show\nSea\nLayer" : "Show Sea Layer"
        }

        CheckBox {
            id: layerThreeToggle
            checked: true
            text: portraitMode ? "Show\nTectonic\nLayer" : "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 {
    Layout.fillWidth: true
    Column {
        spacing: 10

        Label {
            font.pointSize: fontSize
            font.bold: true
            text: portraitMode ? "Layer\nStyle" : "Layer Style"
        }

        CheckBox {
            id: layerOneGrid
            text: portraitMode ? "Show\nGround\nas Grid" : "Show Ground as Grid"
            onCheckedChanged: {
                if (checked)
                    layerOneSeries.drawMode = Surface3DSeries.DrawWireframe
                else
                    layerOneSeries.drawMode = Surface3DSeries.DrawSurface
            }
        }

        CheckBox {
            id: layerTwoGrid
            text: portraitMode ? "Show\nSea\nas Grid" : "Show Sea as Grid"

            onCheckedChanged: {
                if (checked)
                    layerTwoSeries.drawMode = Surface3DSeries.DrawWireframe
                else
                    layerTwoSeries.drawMode = Surface3DSeries.DrawSurface
            }
        }

        CheckBox {
            id: layerThreeGrid
            text: portraitMode ? "Show\nTectonic\nas Grid" : "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:

Button {
    id: sliceButton
    text: portraitMode ? "Slice\nAll\nLayers" : "Slice All Layers"
    Layout.fillWidth: true
    Layout.minimumHeight: 40
    onClicked: {
        if (surfaceLayers.selectionMode & AbstractGraph3D.SelectionMultiSeries) {
            surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                    | AbstractGraph3D.SelectionSlice
            text = portraitMode ? "Slice\nAll\nLayers" : "Slice All Layers"
        } else {
            surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                    | AbstractGraph3D.SelectionSlice
                    | AbstractGraph3D.SelectionMultiSeries
            text = portraitMode ? "Slice\nOne\nLayer" : "Slice One Layer"
        }
    }
}

Example Contents

Example project @ code.qt.io

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.