aboutsummaryrefslogtreecommitdiffstats
path: root/examples/datavisualization/graphgallery/surfacegraphmodifier.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/datavisualization/graphgallery/surfacegraphmodifier.py')
-rw-r--r--examples/datavisualization/graphgallery/surfacegraphmodifier.py643
1 files changed, 643 insertions, 0 deletions
diff --git a/examples/datavisualization/graphgallery/surfacegraphmodifier.py b/examples/datavisualization/graphgallery/surfacegraphmodifier.py
new file mode 100644
index 000000000..68499fac8
--- /dev/null
+++ b/examples/datavisualization/graphgallery/surfacegraphmodifier.py
@@ -0,0 +1,643 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from math import sqrt, sin
+from pathlib import Path
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
+from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
+ QQuaternion, QVector3D)
+from PySide6.QtDataVisualization import (QAbstract3DGraph, QCustom3DItem,
+ QCustom3DLabel,
+ QHeightMapSurfaceDataProxy,
+ QValue3DAxis, QSurfaceDataItem,
+ QSurfaceDataProxy, QSurface3DSeries,
+ Q3DInputHandler, Q3DCamera, Q3DTheme)
+
+
+from highlightseries import HighlightSeries
+from topographicseries import TopographicSeries
+from custominputhandler import CustomInputHandler
+
+
+SAMPLE_COUNT_X = 150
+SAMPLE_COUNT_Z = 150
+HEIGHTMAP_GRID_STEP_X = 6
+HEIGHTMAP_GRID_STEP_Z = 6
+SAMPLE_MIN = -8.0
+SAMPLE_MAX = 8.0
+
+AREA_WIDTH = 8000.0
+AREA_HEIGHT = 8000.0
+ASPECT_RATIO = 0.1389
+MIN_RANGE = AREA_WIDTH * 0.49
+
+
+class SurfaceGraphModifier(QObject):
+
+ def __init__(self, surface, label, parent):
+ super().__init__(parent)
+ self._data_path = Path(__file__).resolve().parent / "data"
+ self._graph = surface
+ self._textField = label
+ self._sqrtSinProxy = None
+ self._sqrtSinSeries = None
+ self._heightMapProxyOne = None
+ self._heightMapProxyTwo = None
+ self._heightMapProxyThree = None
+ self._heightMapSeriesOne = None
+ self._heightMapSeriesTwo = None
+ self._heightMapSeriesThree = None
+
+ self._axisMinSliderX = None
+ self._axisMaxSliderX = None
+ self._axisMinSliderZ = None
+ self._axisMaxSliderZ = None
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 0.0
+ self._stepZ = 0.0
+ self._heightMapWidth = 0
+ self._heightMapHeight = 0
+
+ self._selectionAnimation = None
+ self._titleLabel = None
+ self._previouslyAnimatedItem = None
+ self._previousScaling = {}
+
+ self._topography = None
+ self._highlight = None
+ self._highlightWidth = 0
+ self._highlightHeight = 0
+
+ self._customInputHandler = None
+ self._defaultInputHandler = Q3DInputHandler()
+
+ ac = self._graph.scene().activeCamera()
+ ac.setZoomLevel(85.0)
+ ac.setCameraPreset(Q3DCamera.CameraPresetIsometricRight)
+ self._graph.activeTheme().setType(Q3DTheme.ThemeRetro)
+
+ self._x_axis = QValue3DAxis()
+ self._y_axis = QValue3DAxis()
+ self._z_axis = QValue3DAxis()
+ self._graph.setAxisX(self._x_axis)
+ self._graph.setAxisY(self._y_axis)
+ self._graph.setAxisZ(self._z_axis)
+
+ #
+ # Sqrt Sin
+ #
+ self._sqrtSinProxy = QSurfaceDataProxy()
+ self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
+ self.fillSqrtSinProxy()
+
+ #
+ # Multisurface heightmap
+ #
+ # Create the first surface layer
+ heightMapImageOne = QImage(self._data_path / "layer_1.png")
+ self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
+ self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
+ self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # Create the other 2 surface layers
+ heightMapImageTwo = QImage(self._data_path / "layer_2.png")
+ self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
+ self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
+ self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ heightMapImageThree = QImage(self._data_path / "layer_3.png")
+ self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
+ self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
+ self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # The images are the same size, so it's enough to get the dimensions
+ # from one
+ self._heightMapWidth = heightMapImageOne.width()
+ self._heightMapHeight = heightMapImageOne.height()
+
+ # Set the gradients for multi-surface layers
+ grOne = QLinearGradient()
+ grOne.setColorAt(0.0, Qt.black)
+ grOne.setColorAt(0.38, Qt.darkYellow)
+ grOne.setColorAt(0.39, Qt.darkGreen)
+ grOne.setColorAt(0.5, Qt.darkGray)
+ grOne.setColorAt(1.0, Qt.gray)
+ self._heightMapSeriesOne.setBaseGradient(grOne)
+ self._heightMapSeriesOne.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ grTwo = QLinearGradient()
+ grTwo.setColorAt(0.39, Qt.blue)
+ grTwo.setColorAt(0.4, Qt.white)
+ self._heightMapSeriesTwo.setBaseGradient(grTwo)
+ self._heightMapSeriesTwo.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.white)
+ grThree.setColorAt(0.05, Qt.black)
+ self._heightMapSeriesThree.setBaseGradient(grThree)
+ self._heightMapSeriesThree.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ # Custom items and label
+ self._graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ self._selectionAnimation = QPropertyAnimation(self)
+ self._selectionAnimation.setPropertyName(b"scaling")
+ self._selectionAnimation.setDuration(500)
+ self._selectionAnimation.setLoopCount(-1)
+
+ titleFont = QFont("Century Gothic", 30)
+ titleFont.setBold(True)
+ self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
+ QVector3D(0.0, 1.2, 0.0),
+ QVector3D(1.0, 1.0, 0.0),
+ QQuaternion())
+ self._titleLabel.setPositionAbsolute(True)
+ self._titleLabel.setFacingCamera(True)
+ self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
+ self._graph.addCustomItem(self._titleLabel)
+ self._titleLabel.setVisible(False)
+
+ # Make two of the custom object visible
+ self.toggleItemOne(True)
+ self.toggleItemTwo(True)
+
+ #
+ # Topographic map
+ #
+ self._topography = TopographicSeries()
+ file_name = os.fspath(self._data_path / "topography.png")
+ self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
+ self._topography.setItemLabelFormat("@yLabel m")
+
+ self._highlight = HighlightSeries()
+ self._highlight.setTopographicSeries(self._topography)
+ self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
+ self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
+
+ self._customInputHandler = CustomInputHandler(self._graph)
+ self._customInputHandler.setHighlightSeries(self._highlight)
+ self._customInputHandler.setAxes(self._x_axis, self._y_axis, self._z_axis)
+ self._customInputHandler.setLimits(0.0, AREA_WIDTH, MIN_RANGE)
+ self._customInputHandler.setAspectRatio(ASPECT_RATIO)
+
+ def fillSqrtSinProxy(self):
+ stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+
+ dataArray = []
+ for i in range(0, SAMPLE_COUNT_Z):
+ newRow = []
+ # Keep values within range bounds, since just adding step can
+ # cause minor drift due to the rounding errors.
+ z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
+ for j in range(0, SAMPLE_COUNT_X):
+ x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
+ R = sqrt(z * z + x * x) + 0.01
+ y = (sin(R) / R + 0.24) * 1.61
+ item = QSurfaceDataItem(QVector3D(x, y, z))
+ newRow.append(item)
+ dataArray.append(newRow)
+ self._sqrtSinProxy.resetArray(dataArray)
+
+ @Slot(bool)
+ def enableSqrtSinModel(self, enable):
+ if enable:
+ self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
+ self._sqrtSinSeries.setFlatShadingEnabled(True)
+
+ self._graph.axisX().setLabelFormat("%.2f")
+ self._graph.axisZ().setLabelFormat("%.2f")
+ self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisY().setRange(0.0, 2.0)
+ self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisX().setLabelAutoRotation(30.0)
+ self._graph.axisY().setLabelAutoRotation(90.0)
+ self._graph.axisZ().setLabelAutoRotation(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+
+ self._graph.addSeries(self._sqrtSinSeries)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ self._graph.setActiveInputHandler(self._defaultInputHandler)
+
+ # Reset range sliders for Sqrt & Sin
+ self._rangeMinX = SAMPLE_MIN
+ self._rangeMinZ = SAMPLE_MIN
+ self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
+ self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
+ self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
+
+ @Slot(bool)
+ def enableHeightMapModel(self, enable):
+ if enable:
+ self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesOne.setFlatShadingEnabled(False)
+ self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesTwo.setFlatShadingEnabled(False)
+ self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesThree.setFlatShadingEnabled(False)
+
+ self._graph.axisX().setLabelFormat("%.1f N")
+ self._graph.axisZ().setLabelFormat("%.1f E")
+ self._graph.axisX().setRange(34.0, 40.0)
+ self._graph.axisY().setAutoAdjustRange(True)
+ self._graph.axisZ().setRange(18.0, 24.0)
+
+ self._graph.axisX().setTitle("Latitude")
+ self._graph.axisY().setTitle("Height")
+ self._graph.axisZ().setTitle("Longitude")
+
+ self._graph.removeSeries(self._sqrtSinSeries)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+ self._graph.addSeries(self._heightMapSeriesOne)
+ self._graph.addSeries(self._heightMapSeriesTwo)
+ self._graph.addSeries(self._heightMapSeriesThree)
+
+ self._graph.setActiveInputHandler(self._defaultInputHandler)
+
+ self._titleLabel.setVisible(True)
+ self._graph.axisX().setTitleVisible(True)
+ self._graph.axisY().setTitleVisible(True)
+ self._graph.axisZ().setTitleVisible(True)
+
+ # Reset range sliders for height map
+ mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
+ mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
+ self._rangeMinX = 34.0
+ self._rangeMinZ = 18.0
+ self._stepX = 6.0 / float(mapGridCountX - 1)
+ self._stepZ = 6.0 / float(mapGridCountZ - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(mapGridCountX - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
+ self._axisMaxSliderX.setValue(mapGridCountX - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
+ self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
+
+ @Slot(bool)
+ def enableTopographyModel(self, enable):
+ if enable:
+ self._graph.axisX().setLabelFormat("%i")
+ self._graph.axisZ().setLabelFormat("%i")
+ self._graph.axisX().setRange(0.0, AREA_WIDTH)
+ self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
+ self._graph.axisX().setLabelAutoRotation(30.0)
+ self._graph.axisY().setLabelAutoRotation(90.0)
+ self._graph.axisZ().setLabelAutoRotation(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.addSeries(self._topography)
+ self._graph.addSeries(self._highlight)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ self._graph.setActiveInputHandler(self._customInputHandler)
+
+ # Reset range sliders for topography map
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 1.0
+ self._stepZ = 1.0
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(200)
+ self._axisMaxSliderX.setMaximum(AREA_WIDTH)
+ self._axisMaxSliderX.setValue(AREA_WIDTH)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(200)
+ self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
+ self._axisMaxSliderZ.setValue(AREA_HEIGHT)
+
+ def adjustXMin(self, min):
+ minX = self._stepX * float(min) + self._rangeMinX
+
+ max = self._axisMaxSliderX.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderX.setValue(max)
+
+ maxX = self._stepX * max + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustXMax(self, max):
+ maxX = self._stepX * float(max) + self._rangeMinX
+
+ min = self._axisMinSliderX.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderX.setValue(min)
+
+ minX = self._stepX * min + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustZMin(self, min):
+ minZ = self._stepZ * float(min) + self._rangeMinZ
+
+ max = self._axisMaxSliderZ.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderZ.setValue(max)
+
+ maxZ = self._stepZ * max + self._rangeMinZ
+
+ self.setAxisZRange(minZ, maxZ)
+
+ def adjustZMax(self, max):
+ maxX = self._stepZ * float(max) + self._rangeMinZ
+
+ min = self._axisMinSliderZ.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderZ.setValue(min)
+
+ minX = self._stepZ * min + self._rangeMinZ
+
+ self.setAxisZRange(minX, maxX)
+
+ def setAxisXRange(self, min, max):
+ self._graph.axisX().setRange(min, max)
+
+ def setAxisZRange(self, min, max):
+ self._graph.axisZ().setRange(min, max)
+
+ def setBlackToYellowGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.black)
+ gr.setColorAt(0.33, Qt.blue)
+ gr.setColorAt(0.67, Qt.red)
+ gr.setColorAt(1.0, Qt.yellow)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ def setGreenToRedGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.darkGreen)
+ gr.setColorAt(0.5, Qt.yellow)
+ gr.setColorAt(0.8, Qt.red)
+ gr.setColorAt(1.0, Qt.darkRed)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+
+ @Slot(bool)
+ def toggleItemOne(self, show):
+ positionOne = QVector3D(39.0, 77.0, 19.2)
+ positionOnePipe = QVector3D(39.0, 45.0, 19.2)
+ positionOneLabel = QVector3D(39.0, 107.0, 19.2)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.red)
+ file_name = os.fspath(self._data_path / "oilrig.obj")
+ item = QCustom3DItem(file_name, positionOne,
+ QVector3D(0.025, 0.025, 0.025),
+ QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
+ color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.obj")
+ item = QCustom3DItem(file_name, positionOnePipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig One")
+ label.setPosition(positionOneLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionOne)
+ self._graph.removeCustomItemAt(positionOnePipe)
+ self._graph.removeCustomItemAt(positionOneLabel)
+
+ @Slot(bool)
+ def toggleItemTwo(self, show):
+ positionTwo = QVector3D(34.5, 77.0, 23.4)
+ positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
+ positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.red)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "oilrig.obj")
+ item.setMeshFile(file_name)
+ item.setPosition(positionTwo)
+ item.setScaling(QVector3D(0.025, 0.025, 0.025))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.obj")
+ item = QCustom3DItem(file_name, positionTwoPipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig Two")
+ label.setPosition(positionTwoLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionTwo)
+ self._graph.removeCustomItemAt(positionTwoPipe)
+ self._graph.removeCustomItemAt(positionTwoLabel)
+
+ @Slot(bool)
+ def toggleItemThree(self, show):
+ positionThree = QVector3D(34.5, 86.0, 19.1)
+ positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.darkMagenta)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "refinery.obj")
+ item.setMeshFile(file_name)
+ item.setPosition(positionThree)
+ item.setScaling(QVector3D(0.04, 0.04, 0.04))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Refinery")
+ label.setPosition(positionThreeLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionThree)
+ self._graph.removeCustomItemAt(positionThreeLabel)
+
+ @Slot(bool)
+ def toggleSeeThrough(self, seethrough):
+ s0 = self._graph.seriesList()[0]
+ s1 = self._graph.seriesList()[1]
+ if seethrough:
+ s0.setDrawMode(QSurface3DSeries.DrawWireframe)
+ s1.setDrawMode(QSurface3DSeries.DrawWireframe)
+ else:
+ s0.setDrawMode(QSurface3DSeries.DrawSurface)
+ s1.setDrawMode(QSurface3DSeries.DrawSurface)
+
+ @Slot(bool)
+ def toggleOilHighlight(self, highlight):
+ s2 = self._graph.seriesList()[2]
+ if highlight:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.black)
+ grThree.setColorAt(0.05, Qt.red)
+ s2.setBaseGradient(grThree)
+ else:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.white)
+ grThree.setColorAt(0.05, Qt.black)
+ s2.setBaseGradient(grThree)
+
+ @Slot(bool)
+ def toggleShadows(self, shadows):
+ sq = (QAbstract3DGraph.ShadowQualityMedium
+ if shadows else QAbstract3DGraph.ShadowQualityNone)
+ self._graph.setShadowQuality(sq)
+
+ @Slot(bool)
+ def toggleSurfaceTexture(self, enable):
+ if enable:
+ file_name = os.fspath(self._data_path / "maptexture.jpg")
+ self._topography.setTextureFile(file_name)
+ else:
+ self._topography.setTextureFile("")
+
+ def handleElementSelected(self, type):
+ self.resetSelection()
+ if type == QAbstract3DGraph.ElementCustomItem:
+ item = self._graph.selectedCustomItem()
+ text = ""
+ if isinstance(item, QCustom3DItem):
+ text += "Custom label: "
+ else:
+ file = item.meshFile().split("/")[-1]
+ text += f"{file}: "
+
+ text += str(self._graph.selectedCustomItemIndex())
+ self._textField.setText(text)
+ self._previouslyAnimatedItem = item
+ self._previousScaling = item.scaling()
+ self._selectionAnimation.setTargetObject(item)
+ self._selectionAnimation.setStartValue(item.scaling())
+ self._selectionAnimation.setEndValue(item.scaling() * 1.5)
+ self._selectionAnimation.start()
+ elif type == QAbstract3DGraph.ElementSeries:
+ text = "Surface ("
+ series = self._graph.selectedSeries()
+ if series:
+ point = series.selectedPoint()
+ text += f"{point.x()}, {point.y()}"
+ text += ")"
+ self._textField.setText(text)
+ elif (type.value > QAbstract3DGraph.ElementSeries.value
+ and type < QAbstract3DGraph.ElementCustomItem.value):
+ index = self._graph.selectedLabelIndex()
+ text = ""
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ text += "Axis X label: "
+ elif type == QAbstract3DGraph.ElementAxisYLabel:
+ text += "Axis Y label: "
+ else:
+ text += "Axis Z label: "
+ text += str(index)
+ self._textField.setText(text)
+ else:
+ self._textField.setText("Nothing")
+
+ def resetSelection(self):
+ self._selectionAnimation.stop()
+ if self._previouslyAnimatedItem:
+ self._previouslyAnimatedItem.setScaling(self._previousScaling)
+ self._previouslyAnimatedItem = None
+
+ def toggleModeNone(self):
+ self._graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
+
+ def toggleModeItem(self):
+ self._graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
+
+ def toggleModeSliceRow(self):
+ sm = (QAbstract3DGraph.SelectionItemAndRow
+ | QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionMultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def toggleModeSliceColumn(self):
+ sm = (QAbstract3DGraph.SelectionItemAndColumn
+ | QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionMultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def setAxisMinSliderX(self, slider):
+ self._axisMinSliderX = slider
+
+ def setAxisMaxSliderX(self, slider):
+ self._axisMaxSliderX = slider
+
+ def setAxisMinSliderZ(self, slider):
+ self._axisMinSliderZ = slider
+
+ def setAxisMaxSliderZ(self, slider):
+ self._axisMaxSliderZ = slider