diff --git a/qml/Ping1DVisualizer.qml b/qml/Ping1DVisualizer.qml index bf2afcd4e..9c005449a 100644 --- a/qml/Ping1DVisualizer.qml +++ b/qml/Ping1DVisualizer.qml @@ -62,8 +62,21 @@ Item { orientation: Qt.Horizontal anchors.fill: parent - WaterfallPlot { - id: waterfall + ShaderEffect { + id: shader + + WaterfallPlot { + id: waterfall + visible: false + anchors.fill: parent + } + + property variant src: waterfall + property variant horizontalRatio: waterfall.drawHorizontalRatio + property variant verticalRatio: waterfall.drawVerticalRatio + vertexShader: "qrc:/opengl/waterfallplot/vertex.glsl" + fragmentShader: "qrc:/opengl/waterfallplot/fragment.glsl" + Layout.fillHeight: true Layout.fillWidth: true Layout.preferredWidth: 350 diff --git a/qml/opengl/waterfallplot/fragment.glsl b/qml/opengl/waterfallplot/fragment.glsl new file mode 100644 index 000000000..9c4f8517c --- /dev/null +++ b/qml/opengl/waterfallplot/fragment.glsl @@ -0,0 +1,11 @@ +varying vec2 coord; +uniform float horizontalRatio; +uniform float verticalRatio; +uniform sampler2D src; + +void main() { + // Mod is used to wrap around the 0-1 scaled x axis + vec2 shiftedCoord = mod(coord + vec2(horizontalRatio, 0), 1.0); + shiftedCoord.y = shiftedCoord.y * verticalRatio; + gl_FragColor = texture2D(src, shiftedCoord); +} diff --git a/qml/opengl/waterfallplot/vertex.glsl b/qml/opengl/waterfallplot/vertex.glsl new file mode 100644 index 000000000..47c80206f --- /dev/null +++ b/qml/opengl/waterfallplot/vertex.glsl @@ -0,0 +1,8 @@ +uniform highp mat4 qt_Matrix; +attribute highp vec4 qt_Vertex; +attribute highp vec2 qt_MultiTexCoord0; +varying highp vec2 coord; +void main() { + coord = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; +} diff --git a/resources.qrc b/resources.qrc index 3911b1137..9a07717b4 100644 --- a/resources.qrc +++ b/resources.qrc @@ -69,5 +69,7 @@ qml/opengl/polarplot/fragment.glsl qml/opengl/polarplot/vertex.glsl + qml/opengl/waterfallplot/fragment.glsl + qml/opengl/waterfallplot/vertex.glsl diff --git a/src/waterfall/waterfallplot.cpp b/src/waterfall/waterfallplot.cpp index e526d2b36..2c081b306 100644 --- a/src/waterfall/waterfallplot.cpp +++ b/src/waterfall/waterfallplot.cpp @@ -15,8 +15,8 @@ uint16_t WaterfallPlot::_displayWidth = 500; WaterfallPlot::WaterfallPlot(QQuickItem* parent) : Waterfall(parent) - , _currentDrawIndex(_displayWidth) - , _image(2048, 2500, QImage::Format_RGBA8888) + , _currentDrawIndex(0) + , _image(_displayWidth, 2000, QImage::Format_RGBA8888) , _maxDepthToDrawInPixels(0) , _minDepthToDrawInPixels(0) , _mouseDepth(0) @@ -50,20 +50,13 @@ void WaterfallPlot::paint(QPainter* painter) _painter = painter; } - static uint16_t first; - - if (_currentDrawIndex < _displayWidth) { - first = 0; - } else { - first = _currentDrawIndex - _displayWidth; - } - // http://blog.qt.io/blog/2006/05/13/fast-transformed-pixmapimage-drawing/ pix = QPixmap::fromImage(_image, Qt::NoFormatConversion); // Code for debug, draw the entire waterfall - //_painter->drawPixmap(_painter->viewport(), pix, QRect(0, 0, _image.width(), _image.height())); - _painter->drawPixmap(QRect(0, 0, width(), height()), pix, - QRect(first, _minDepthToDrawInPixels, _displayWidth, _maxDepthToDrawInPixels)); + _painter->drawPixmap(_painter->viewport(), pix, QRect(0, 0, _image.width(), _image.height())); + + emit drawHorizontalRatioChanged(); + emit drawVerticalRatioChanged(); } void WaterfallPlot::setImage(const QImage& image) @@ -137,6 +130,10 @@ void WaterfallPlot::draw(const QVector& points, float confidence, float // This ring vector will store variables of the last n samples for user access _DCRing.append({initPoint, length, confidence, distance}); + // Limit currentDrawIndex to be inside our image max width + _currentDrawIndex++; + _currentDrawIndex %= _displayWidth; + /** * @brief Get lastMaxDepth from the last n samples */ @@ -183,7 +180,6 @@ void WaterfallPlot::draw(const QVector& points, float confidence, float QPainter painter(&_image); // Clean everything and start from zero painter.fillRect(_image.rect(), Qt::transparent); - // QRect(0, 0, _image.width(), _image.height()*dynamicPixelsPerMeterScalar), old painter.drawImage(dst, old, src); painter.end(); }; @@ -217,16 +213,6 @@ void WaterfallPlot::draw(const QVector& points, float confidence, float int virtualFloor = initPoint * _minPixelsPerMeter; int virtualHeight = length * _minPixelsPerMeter * dynamicPixelsPerMeterScalar; - // Copy tail to head - // TODO: can we get even better and allocate just once at initialization? ie circular buffering - if (_currentDrawIndex >= _image.width()) { - redrawImage(QRect(0, 0, _displayWidth, _image.height()), - QRect(old.width() - _displayWidth, 0, _displayWidth, old.height())); - - // Start painting from the beginning - _currentDrawIndex = _displayWidth; - } - // Do up/downsampling float factor = points.length() / ((float)(virtualHeight)); @@ -273,7 +259,10 @@ void WaterfallPlot::draw(const QVector& points, float confidence, float _image.setPixelColor(_currentDrawIndex, i + virtualFloor, valueToRGB(points[factor * i])); } } - _currentDrawIndex++; // This can get to be an issue at very fast update rates from ping + + for (int i = virtualHeight; i < _image.height(); i++) { + _image.setPixelColor(_currentDrawIndex, i, Qt::transparent); + } // Fix max update in 20Hz at max if (!_updateTimer->isActive()) { diff --git a/src/waterfall/waterfallplot.h b/src/waterfall/waterfallplot.h index b05c80e60..f365732af 100644 --- a/src/waterfall/waterfallplot.h +++ b/src/waterfall/waterfallplot.h @@ -111,7 +111,28 @@ class WaterfallPlot : public Waterfall { Q_INVOKABLE float getMinDepthToDraw() { return _minDepthToDraw; } Q_PROPERTY(float minDepthToDraw READ getMinDepthToDraw NOTIFY minDepthToDrawChanged) + /** + * @brief Return the necessary horizontal ratio value to perform the correct waterfall draw + * + * @return double + */ + Q_INVOKABLE double drawHorizontalRatio() { return static_cast(_currentDrawIndex + 1) / _image.width(); } + Q_PROPERTY(double drawHorizontalRatio READ drawHorizontalRatio NOTIFY drawHorizontalRatioChanged) + + /** + * @brief Return the necessary vertical ratio value to perform the correct waterfall draw + * + * @return double + */ + Q_INVOKABLE double drawVerticalRatio() + { + return (_maxDepthToDraw - _minDepthToDraw) * _minPixelsPerMeter / _image.height(); + } + Q_PROPERTY(double drawVerticalRatio READ drawVerticalRatio NOTIFY drawVerticalRatioChanged) + signals: + void drawHorizontalRatioChanged(); + void drawVerticalRatioChanged(); void imageChanged(); void maxDepthToDrawChanged(); void minDepthToDrawChanged();