このページでは

サーフェスグラフギャラリー

Surface3D グラフの3つの活用方法をまとめたギャラリー。

「Surface Graph Gallery」では、Surface3D グラフを用いた3つの異なるカスタム機能を紹介しています。これらの機能には、アプリケーション内にそれぞれ専用のタブが用意されています。

以下のセクションでは、これらの機能にのみ焦点を当て、基本的な機能の説明は省略しています。より詳細な QML のサンプルドキュメントについては、「Simple Scatter Graph」を参照してください。

地形とグリッド制御機能を備えた3D高度マップの表面

例の動作確認

以下の手順で例を実行できます:

標高マップ

「Height Map 」タブで、高さデータから表面グラフを生成します。使用するデータは、ニュージーランドのルアペフ山およびナウルホエ山の高さマップです。

グラフへのデータの追加

データは、高さマップ画像から高さ情報を読み取る `HeightMapSurfaceDataProxy` を使用して設定されます。このプロキシ自体は `Surface3DSeries` に含まれています。`HeightMapSurfaceDataProxy ` 内の `heightMapFile ` プロパティは、高さデータを含む画像ファイルを指定します。 プロキシ内の値プロパティは、表面積の幅、奥行き、高さの最小値と最大値を定義します。z およびx の値は、実世界の位置に近い緯度と経度で指定され、y はメートル単位で指定されます。

注: グラフのアスペクト比は 実世界の縮尺に合わせておらず、代わりに高さが誇張されています。

Surface3DSeries {
    id: heightSeries
    shading: Surface3DSeries.Shading.Smooth
    drawMode: Surface3DSeries.DrawSurface

    HeightMapSurfaceDataProxy {
        heightMapFile: "://qml/surfacegallery/heightmap.png"
        // We don't want the default data values set by heightmap proxy, but use
        // actual coordinate and height values instead
        autoScaleY: true
        minYValue: 740
        maxYValue: 2787
        minZValue: -374 // ~ -39.374411"N
        maxZValue: -116 // ~ -39.115971"N
        minXValue: 472  // ~ 175.471767"E
        maxXValue: 781  // ~ 175.780758"E
    }

    onDrawModeChanged: heightMapView.checkState()
}
データの表示

main.qml で、データを表示するためにSurface3D 要素を設定します。

まず、サーフェスに使用するカスタムグラデーションを定義します。グラデーションを使用して、位置 0.0 から 1.0 までの色を指定し、グラフをより鮮明にするために 2 つのストップを追加します。

Gradient {
    id: surfaceGradient
    GradientStop { position: 0.0; color: "darkgreen"}
    GradientStop { position: 0.15; color: "darkslategray" }
    GradientStop { position: 0.7; color: "peru" }
    GradientStop { position: 1.0; color: "white" }
}

この要素を、Surface3D で使用されているtheme のbaseGradients プロパティに設定します:

theme: GraphsTheme {
    colorScheme: GraphsTheme.ColorScheme.Dark
    labelFont.family: "STCaiyun"
    labelFont.pointSize: 35
    colorStyle: GraphsTheme.ColorStyle.ObjectGradient
    baseGradients: [surfaceGradient] // Use the custom gradient
}

ボタンを使用して、Surface3D のその他の機能を制御します。

最初のボタンは、サーフェスグリッドの表示/非表示を切り替えます。描画モードは完全にクリアできないため、サーフェス自体が可視状態でない限り、サーフェスグリッドを非表示にすることはできません:

onClicked: {
    if (heightSeries.drawMode & Surface3DSeries.DrawWireframe)
        heightSeries.drawMode &= ~Surface3DSeries.DrawWireframe;
    else
        heightSeries.drawMode |= Surface3DSeries.DrawWireframe;
}

2番目のボタンは、サーフェスグリッドの色を設定します:

onClicked: {
    if (Qt.colorEqual(heightSeries.wireframeColor, "#000000")) {
        heightSeries.wireframeColor = "red";
        text = "Black surface\ngrid color";
    } else {
        heightSeries.wireframeColor = "black";
        text = "Red surface\ngrid color";
    }
}

3番目のボタンは、サーフェス描画モードにおいてサーフェスの表示/非表示を切り替えます。描画モードは完全にクリアできないため、サーフェスグリッドが表示されていない限り、サーフェス自体を非表示にすることはできません:

onClicked: {
    if (heightSeries.drawMode & Surface3DSeries.DrawSurface)
        heightSeries.drawMode &= ~Surface3DSeries.DrawSurface;
    else
        heightSeries.drawMode |= Surface3DSeries.DrawSurface;
}

4番目のボタンはシェーディングモードを設定します。OpenGL ESシステムでこのサンプルを実行している場合、フラットシェーディングは利用できません:

onClicked: {
    if (heightSeries.shading === Surface3DSeries.Shading.Flat) {
        heightSeries.shading = Surface3DSeries.Shading.Smooth;
        text = "Show\nFlat"
    } else {
        heightSeries.shading = Surface3DSeries.Shading.Flat;
        text = "Show\nSmooth"
    }
}

残りのボタンは、グラフの背景機能を制御します。

スペクトログラム

「Spectrogram 」タブでは、極座標および直交座標のスペクトログラムを表示し、正投影を用いて 2D で表示します。

スペクトログラムとは、値の違いを強調するために範囲のグラデーションが適用されたサーフェスグラフのことです。通常、スペクトログラムは2次元サーフェスとして表示され、グラフの俯瞰的な正投影ビューでシミュレートされます。2D効果を確実に得るには、正投影モードのときは、マウスやタッチによるグラフの回転を無効にしてください。

スペクトログラムの作成

2次元スペクトログラムを作成するには、Surface3DSeries に指定されたデータとItemModelSurfaceDataProxy を使用して、Surface3D 項目を定義します:

Surface3D {
    id: surfaceGraph
    anchors.fill: parent

    // Don't show specular spotlight as we don't want it to distort the colors
    lightStrength: 0.0
    ambientLightStrength: 1.0

    Surface3DSeries {
        id: surfaceSeries
        shading: Surface3DSeries.Shading.Smooth
        drawMode: Surface3DSeries.DrawSurface
        baseGradient: surfaceGradient
        colorStyle: GraphsTheme.ColorStyle.RangeGradient
        itemLabelFormat: "(@xLabel, @zLabel): @yLabel"

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

2D効果を有効にするための重要なプロパティは、orthoProjection とcameraPreset です。グラフの正投影を有効にして遠近感を排除し、グラフを真上から直接見ることでY軸の視覚的表現を排除します:

// Remove the perspective and view the graph from top down to achieve 2D effect
orthoProjection: true
cameraPreset: Graphs3D.CameraPreset.DirectlyAbove

この視点では、水平軸のグリッドの大部分が曲面で隠れてしまうため、水平グリッドを反転させてグラフの上に描画するようにします:

flipHorizontalGrid: true
極座標スペクトログラム

データによっては、直交座標グラフではなく極座標グラフを使用したほうが自然な場合があります。これは、polar プロパティによってサポートされています。

極座標モードと直交座標モードを切り替えるボタンを追加します:

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;
}

極座標モードでは、X軸は角の極座標軸に変換され、Z軸は半径方向の極座標軸に変換されます。表面上の点は、新しい軸に基づいて再計算されます。

デフォルトでは、半径軸のラベルはグラフの外側に描画されます。グラフ内の0度の角度軸のすぐ隣にラベルを描画するには、ラベルのオフセットをわずかに設定します:

radialLabelOffset: 0.01

2D効果を確実に得るには、デフォルトの入力ハンドラをカスタムハンドラで上書きし、投影モードに基づいてrotationEnabled プロパティを自動的に切り替えることで、正投影モードでのグラフの回転を無効にします:

rotationEnabled: !surfaceGraph.orthoProjection

オシロスコープ

「Oscilloscope 」タブでは、アプリケーション内で C++ と QML を組み合わせて、動的に変化するデータを表示します。

C++ でのデータソース

アイテムモデルベースのプロキシは、単純なグラフや静的なグラフには適していますが、リアルタイムで変化するデータを表示する際は、最高のパフォーマンスを得るために基本プロキシを使用してください。これらはQMLではサポートされていません。なぜなら、格納されるデータアイテムがQObject を継承しておらず、したがってQMLコードから直接操作できないためです。 この制限を克服するには、C++で単純なDataSource クラスを実装し、シリーズのデータプロキシにデータを入力します。

QMLから呼び出せる2つのメソッドを提供するDataSource クラスを作成します。

class DataSource : public QObject
{
    Q_OBJECT
    ...
Q_INVOKABLE void generateData(int cacheCount,
                              int rowCount,
                              int columnCount,
                              float xMin,
                              float xMax,
                              float yMin,
                              float yMax,
                              float zMin,
                              float zMax);

Q_INVOKABLE void update(QSurface3DSeries *series);

1つ目のメソッド `generateData()` は、表示するためのシミュレートされたオシロスコープデータのキャッシュを作成します。データは、QSurfaceDataProxy が受け入れる形式でキャッシュされます:

// Populate caches
for (int i = 0; i < cacheCount; i++) {
    QSurfaceDataArray &cache = m_data[i];
    float cacheXAdjustment = cacheStep * i;
    float cacheIndexAdjustment = cacheIndexStep * i;
    for (int j = 0; j < rowCount; j++) {
        QSurfaceDataRow &row = cache[j];
        float rowMod = (float(j)) / float(rowCount);
        float yRangeMod = yRange * rowMod;
        float zRangeMod = zRange * rowMod;
        float z = zRangeMod + zMin;
        qreal waveAngleMul = M_PI * M_PI * rowMod;
        float waveMul = yRangeMod * 0.2f;
        for (int k = 0; k < columnCount; k++) {
            float colMod = (float(k)) / float(columnCount);
            float xRangeMod = xRange * colMod;
            float x = xRangeMod + xMin + cacheXAdjustment;
            float colWave = float(qSin((2.0 * M_PI * colMod) - (1.0 / 2.0 * M_PI)) + 1.0);
            float y = (colWave * ((float(qSin(waveAngleMul * colMod) + 1.0)))) * waveMul
                      + QRandomGenerator::global()->bounded(0.15f) * yRangeMod;

            int index = k + cacheIndexAdjustment;
            if (index >= columnCount) {
                // Wrap over
                index -= columnCount;
                x -= xRange;
            }
            row[index] = QSurfaceDataItem(x, y, z);
        }
    }
}

2つ目のメソッド `update()` は、キャッシュされたデータの一セットを別の配列にコピーし、`QSurfaceDataProxy::resetArray()` を呼び出すことで、その配列をシリーズに設定します。オーバーヘッドを最小限に抑えるため、配列の次元が変更されていない場合は、同じ配列を再利用します:

// Each iteration uses data from a different cached array
m_index++;
if (m_index > m_data.count() - 1)
    m_index = 0;

QSurfaceDataArray array = m_data.at(m_index);
int newRowCount = array.size();
int newColumnCount = array.at(0).size();

// If the first time or the dimensions of the cache array have changed,
// reconstruct the reset array
if (m_resetArray.isEmpty() || series->dataProxy()->rowCount() != newRowCount
    || series->dataProxy()->columnCount() != newColumnCount) {
    m_resetArray.clear();
    m_resetArray.reserve(newRowCount);
    for (int i = 0; i < newRowCount; i++)
        m_resetArray.append(QSurfaceDataRow(newColumnCount));
}

// Copy items from our cache to the reset array
for (int i = 0; i < newRowCount; i++) {
    const QSurfaceDataRow &sourceRow = array.at(i);
    QSurfaceDataRow &row = m_resetArray[i];
    for (int j = 0; j < newColumnCount; j++)
        row[j].setPosition(sourceRow.at(j).position());
}

// Notify the proxy that data has changed
series->dataProxy()->resetArray(m_resetArray);

以前にシリーズに設定された配列ポインタを操作している場合でも、その中のデータを変更した後は、グラフにデータをレンダリングさせるために、QSurfaceDataProxy::resetArray() を呼び出す必要があります。

QMLからDataSource のメソッドにアクセスできるようにするには、DataSourceをQML_ELEMENT として公開します:

class DataSource : public QObject
{
    Q_OBJECT
    QML_ELEMENT

さらに、CMakeLists.txt内でそれをQMLモジュールとして宣言します:

qt6_add_qml_module(surfacegallery
    URI SurfaceGallery
    VERSION 1.0
    NO_RESOURCE_TARGET_PATH
    SOURCES
        datasource.cpp datasource.h
    ...
)

すべての環境およびビルドにおいて、QSurface3DSeries のポインタをDataSource クラスのメソッドのパラメータとして使用するには、メタタイプが登録されていることを確認してください:

qRegisterMetaType<QSurface3DSeries *>();
QMLアプリケーション

DataSource を使用するには、QMLモジュールをインポートし、使用するDataSource のインスタンスを作成します:

import SurfaceGalleryExample
...
DataSource {
    id: dataSource
}

Surface3D グラフを定義し、そこに `Surface3DSeries` を割り当てます:

Surface3D {
    id: surfaceGraph
    anchors.fill: parent

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

グラフに接続するSurface3DSeries に対してプロキシを指定しないでください。これにより、シリーズはデフォルトのQSurfaceDataProxy を利用することになります。

itemLabelVisible を使用して項目ラベルを非表示にします。動的で変化の激しいデータの場合、浮動式の選択ラベルは注意をそらし、読みづらくなる可能性があります。

itemLabelVisible: false

選択された項目の情報を、選択ポインタの上にあるデフォルトの浮動ラベルの代わりに、Text 要素で表示することができます:

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

グラフの生成が完了したら、ヘルパー関数 `generateData()` を呼び出して `DataSource ` キャッシュを初期化します。この関数は、DataSource 内の同名のメソッドを呼び出します:

Component.onCompleted: oscilloscopeView.generateData();
...
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);
}

データの更新をトリガーするには、Timer を定義し、指定された間隔でDataSource 内のupdate() メソッドを呼び出します:

Timer {
    id: refreshTimer
    interval: 1000 / frequencySlider.value
    running: true
    repeat: true
    onTriggered: dataSource.update(surfaceSeries);
}
ダイレクトレンダリングの有効化

このアプリケーションは、急速に変化する大量のデータを扱う可能性があるため、パフォーマンス向上のためにダイレクトレンダリングモードを使用します。このモードでアンチエイリアシングを有効にするには、アプリケーションウィンドウのサーフェス形式を変更してください。QQuickView で使用されるデフォルトの形式は、アンチエイリアシングをサポートしていません。main.cpp 内のサーフェス形式を変更するには、提供されているユーティリティ関数を使用してください:

#include <QtGraphs/qutils.h>
...
// Enable antialiasing in direct rendering mode
viewer.setFormat(QQuick3D::idealSurfaceFormat(8));

サンプル内容

サンプルプロジェクト @ code.qt.io

© 2026 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.