Index: sources/gui/qml/components/BaseChart.qml =================================================================== diff -u -rd5aa2daeb2dfb38d93d128a85618b86855e07239 -r09ec5af58d98d8cd337359482e81045052a5c264 --- sources/gui/qml/components/BaseChart.qml (.../BaseChart.qml) (revision d5aa2daeb2dfb38d93d128a85618b86855e07239) +++ sources/gui/qml/components/BaseChart.qml (.../BaseChart.qml) (revision 09ec5af58d98d8cd337359482e81045052a5c264) @@ -15,6 +15,7 @@ import QtQuick 2.15 import QtCharts 2.15 +import QtQuick.Shapes 1.15 // Import the Shapes module import "qrc:/globals" @@ -36,7 +37,7 @@ readonly property int minY : -700 readonly property int maxY : 600 - readonly property int plotFrequency : 1000 * 60 // plot every 1 min + readonly property int plotFrequency : 1000 * 60 // plot every 1 min // plot current reading at current time function plotGraph() { @@ -146,5 +147,139 @@ useOpenGL : true width : 3 } + + // --- The Vertical Line --- + Shape { id: _verticalLine + width : 1 + height : _chartView.plotArea.height + y : _chartView.plotArea.y + visible : false // Hidden until touched + + ShapePath { + strokeColor : "#CA8D36" + strokeWidth : 1 + strokeStyle : ShapePath.DashLine + dashPattern : [2, 4] + capStyle : ShapePath.RoundCap + + startX: 1; startY: 0 + PathLine { x: 1; y: _verticalLine.height } + } + } + + Row { id: _row + anchors.top: parent.top + anchors.topMargin: 5 + anchors.horizontalCenter: parent.horizontalCenter + spacing: Variables.defaultMargin + + Text { id: _timeText + color: Colors.offWhite + font.pixelSize: 22 + font.weight: Font.Medium + } + + Text { id: _line1Text + color: _root.lineSeries1Color + font.pixelSize: 22 + font.weight: Font.Medium + } + + Text { id: _line2Text + color: _root.lineSeries2Color + font.pixelSize: 22 + font.weight: Font.Medium + } + + Text { id: _line3Text + color: _root.lineSeries3Color + font.pixelSize: 22 + font.weight: Font.Medium + } + } + + // --- Touch/Mouse Handling Area --- + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onPressed: { + _verticalLine.visible = true + updateLinePosition(mouse.x, mouse.y) + } + onPositionChanged: { + if (pressed) { updateLinePosition(mouse.x, mouse.y) } + } + + function updateLinePosition(mx, my) { + if (mx < _chartView.plotArea.x || mx > _chartView.plotArea.x + _chartView.plotArea.width) + return + + _verticalLine.x = mx + + // Convert to data coordinates + let line1Value = _chartView.mapToValue(Qt.point(mx, my), _line1) + let line2Value = _chartView.mapToValue(Qt.point(mx, my), _line2) + let line3Value = _chartView.mapToValue(Qt.point(mx, my), _line3) + + let newDate = new Date( line1Value.x ) + _timeText.text = qsTr("Time: %1").arg(Qt.formatTime(newDate, "H:mm")) + + let y1 = getYatX( _line1, line1Value.x ) + let y2 = getYatX( _line2, line2Value.x ) + let y3 = getYatX( _line3, line3Value.x ) + + _line1Text.text = y1 ? y1.toFixed(0) : Variables.emptyEntry + _line2Text.text = y2 ? y2.toFixed(0) : Variables.emptyEntry + _line3Text.text = y3 ? y3.toFixed(0) : Variables.emptyEntry + } + + function getYatX(series, x) { + let count = series.count + + // need at least 2 points to interpolate between them + if (count < 2) + return null + + // Clamp outside range + let first = series.at(0) + let last = series.at(count - 1) + + // if x is given before thie first point then return first y + if (x <= first.x) + return first.y + + // if x is given after the last point then return last y + if (x >= last.x) + return last.y + + // Binary Search for a fast look ip + let left = 0 + let right = count - 1 + + while (left <= right) { + let mid = Math.floor((left + right) / 2) + let midPoint = series.at(mid) + + // found y + if (midPoint.x === x) + return midPoint.y + + if (midPoint.x < x) + left = mid + 1 // x is larger so discard left half + else + right = mid - 1 // x is smaller so discard right half + } + + // if not found then interpolate betweeen them + // series.at(right) and series.at(left) + let p1 = series.at(right) + let p2 = series.at(left) + + // Compute how far between them X is + let ratio = (x - p1.x) / (p2.x - p1.x) + return p1.y + ratio * (p2.y - p1.y) //compute y + } + } } }