main.qml Example File | Qt Data Visualization 5.12.3

Qt 5.12 | Qt Data Visualization | Qt Quick 2 Axis Dragging Example | main.qml Example File

Qt Data Visualization | Commercial or GPLv3

# main.qml Example File

qmlaxisdrag/qml/qmlaxisdrag/main.qml

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the Qt Data Visualization module of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:GPL$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** GNU General Public License Usage
      ** Alternatively, this file may be used under the terms of the GNU
      ** General Public License version 3 or (at your option) any later version
      ** approved by the KDE Free Qt Foundation. The licenses are as published by
      ** the Free Software Foundation and appearing in the file LICENSE.GPL3
      ** included in the packaging of this file. Please review the following
      ** information to ensure the GNU General Public License requirements will
      ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      import QtQuick 2.1
      import QtDataVisualization 1.1
      import "."

      Item {
          id: mainView
          width: 800
          height: 600
          visible: true

          property int selectedAxisLabel: -1
          property real dragSpeedModifier: 100.0
          property int currentMouseX: -1
          property int currentMouseY: -1
          property int previousMouseX: -1
          property int previousMouseY: -1

          ListModel {
              id: graphModel
              ListElement{ xPos: 0.0; yPos: 0.0; zPos: 0.0; rotation: "@0,0,0,0" }
              ListElement{ xPos: 1.0; yPos: 1.0; zPos: 1.0; rotation: "@45,1,1,1" }
          }

          Timer {
              id: dataTimer
              interval: 1
              running: true
              repeat: true
              property bool isIncreasing: true
              property real rotationAngle: 0

              function generateQuaternion() {
                  return "@" + Math.random() * 360 + "," + Math.random() + ","
                          + Math.random() + "," + Math.random()
              }

              function appendRow() {
                  graphModel.append({"xPos": Math.random(),
                                        "yPos": Math.random(),
                                        "zPos": Math.random(),
                                        "rotation": generateQuaternion()
                                    });
              }

              onTriggered: {
                  rotationAngle = rotationAngle + 1
                  qtCube.setRotationAxisAndAngle(Qt.vector3d(1,0,1), rotationAngle)
                  scatterSeries.setMeshAxisAndAngle(Qt.vector3d(1,1,1), rotationAngle)
                  if (isIncreasing) {
                      for (var i = 0; i < 10; i++)
                          appendRow()
                      if (graphModel.count > 2002) {
                          scatterGraph.theme = isabelleTheme
                          isIncreasing = false
                      }
                  } else {
                      graphModel.remove(2, 10);
                      if (graphModel.count == 2) {
                          scatterGraph.theme = dynamicColorTheme
                          isIncreasing = true
                      }
                  }
              }
          }

          ThemeColor {
              id: dynamicColor
              ColorAnimation on color {
                  from: "red"
                  to: "yellow"
                  duration: 2000
                  loops: Animation.Infinite
              }
          }

          Theme3D {
              id: dynamicColorTheme
              type: Theme3D.ThemeEbony
              baseColors: [dynamicColor]
              font.pointSize: 50
              labelBorderEnabled: true
              labelBackgroundColor: "gold"
              labelTextColor: "black"
          }

          Theme3D {
              id: isabelleTheme
              type: Theme3D.ThemeIsabelle
              font.pointSize: 50
              labelBorderEnabled: true
              labelBackgroundColor: "gold"
              labelTextColor: "black"
          }

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

              Scatter3D {
                  id: scatterGraph
                  inputHandler: null
                  width: dataView.width
                  height: dataView.height
                  theme: dynamicColorTheme
                  shadowQuality: AbstractGraph3D.ShadowQualityLow
                  scene.activeCamera.yRotation: 45.0
                  scene.activeCamera.xRotation: 45.0
                  scene.activeCamera.zoomLevel: 75.0

                  Scatter3DSeries {
                      id: scatterSeries
                      itemLabelFormat: "X:@xLabel Y:@yLabel Z:@zLabel"
                      mesh: Abstract3DSeries.MeshCube

                      ItemModelScatterDataProxy {
                          itemModel: graphModel
                          xPosRole: "xPos"
                          yPosRole: "yPos"
                          zPosRole: "zPos"
                          rotationRole: "rotation"
                      }
                  }
                  customItemList: [
                      Custom3DItem {
                          id: qtCube
                          meshFile: ":/mesh/cube"
                          textureFile: ":/texture/texture"
                          position: Qt.vector3d(0.65,0.35,0.65)
                          scaling: Qt.vector3d(0.3,0.3,0.3)
                      }
                  ]
                  onSelectedElementChanged: {
                      if (selectedElement >= AbstractGraph3D.ElementAxisXLabel
                              && selectedElement <= AbstractGraph3D.ElementAxisZLabel)
                          selectedAxisLabel = selectedElement
                      else
                          selectedAxisLabel = -1
                  }
              }

              MouseArea {
                  anchors.fill: parent
                  hoverEnabled: true
                  acceptedButtons: Qt.LeftButton

                  onPositionChanged: {
                      currentMouseX = mouse.x;
                      currentMouseY = mouse.y;
                      if (pressed && selectedAxisLabel != -1)
                          dragAxis();
                      previousMouseX = currentMouseX;
                      previousMouseY = currentMouseY;
                  }

                  onPressed: {
                      scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y);
                  }

                  onReleased: {
                      // We need to clear mouse positions and selected axis, because touch devices cannot
                      // track position all the time
                      selectedAxisLabel = -1
                      currentMouseX = -1
                      currentMouseY = -1
                      previousMouseX = -1
                      previousMouseY = -1
                  }
              }
          }

          function dragAxis() {
              // Do nothing if previous mouse position is uninitialized
              if (previousMouseX === -1)
                  return

              // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we
              // can use one precalculated value instead of calculating xx, xy, zx and zy individually
              var cameraMultiplier = 0.70710678

              // Calculate the mouse move amount
              var moveX = currentMouseX - previousMouseX
              var moveY = currentMouseY - previousMouseY

              // Adjust axes
              switch (selectedAxisLabel) {
              case AbstractGraph3D.ElementAxisXLabel:
                  var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier
                  // Check if we need to change min or max first to avoid invalid ranges
                  if (distance > 0) {
                      scatterGraph.axisX.min -= distance
                      scatterGraph.axisX.max -= distance
                  } else {
                      scatterGraph.axisX.max -= distance
                      scatterGraph.axisX.min -= distance
                  }
                  break
              case AbstractGraph3D.ElementAxisYLabel:
                  distance = moveY / dragSpeedModifier
                  // Check if we need to change min or max first to avoid invalid ranges
                  if (distance > 0) {
                      scatterGraph.axisY.max += distance
                      scatterGraph.axisY.min += distance
                  } else {
                      scatterGraph.axisY.min += distance
                      scatterGraph.axisY.max += distance
                  }
                  break
              case AbstractGraph3D.ElementAxisZLabel:
                  distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier
                  // Check if we need to change min or max first to avoid invalid ranges
                  if (distance > 0) {
                      scatterGraph.axisZ.max += distance
                      scatterGraph.axisZ.min += distance
                  } else {
                      scatterGraph.axisZ.min += distance
                      scatterGraph.axisZ.max += distance
                  }
                  break
              }
          }

          NewButton {
              id: rangeToggle
              width: parent.width / 3 // We're adding 3 buttons and want to divide them equally
              text: "Use Preset Range"
              anchors.left: parent.left
              property bool autoRange: true
              onClicked: {
                  if (autoRange) {
                      text = "Use Automatic Range"
                      scatterGraph.axisX.min = 0.3
                      scatterGraph.axisX.max = 0.7
                      scatterGraph.axisY.min = 0.3
                      scatterGraph.axisY.max = 0.7
                      scatterGraph.axisZ.min = 0.3
                      scatterGraph.axisZ.max = 0.7
                      autoRange = false
                      dragSpeedModifier = 200.0
                  } else {
                      text = "Use Preset Range"
                      autoRange = true
                      dragSpeedModifier = 100.0
                  }
                  scatterGraph.axisX.autoAdjustRange = autoRange
                  scatterGraph.axisY.autoAdjustRange = autoRange
                  scatterGraph.axisZ.autoAdjustRange = autoRange
              }
          }

          NewButton {
              id: orthoToggle
              width: parent.width / 3
              text: "Display Orthographic"
              anchors.left: rangeToggle.right
              onClicked: {
                  if (scatterGraph.orthoProjection) {
                      text = "Display Orthographic";
                      scatterGraph.orthoProjection = false
                      // Orthographic projection disables shadows, so we need to switch them back on
                      scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityLow
                  } else {
                      text = "Display Perspective";
                      scatterGraph.orthoProjection = true
                  }
              }
          }

          NewButton {
              id: exitButton
              width: parent.width / 3
              text: "Quit"
              anchors.left: orthoToggle.right
              onClicked: Qt.quit(0);
          }
      }