diff --git a/pingviewer.pro b/pingviewer.pro index 7238e2859..7b05458ca 100644 --- a/pingviewer.pro +++ b/pingviewer.pro @@ -24,7 +24,7 @@ RESOURCES += \ # Warning as error *-g++ | *-clang { - QMAKE_CXXFLAGS += -Werror + #QMAKE_CXXFLAGS += -Werror } *msvc { QMAKE_CXXFLAGS += /WX \ diff --git a/qml/MainPage.qml b/qml/MainPage.qml index 097a8d68a..a46aef649 100644 --- a/qml/MainPage.qml +++ b/qml/MainPage.qml @@ -15,6 +15,8 @@ import PingEnumNamespace 1.0 import SettingsManager 1.0 import StyleManager 1.0 +import QQuickOpenGL 1.0 + Item { id: root visible: true @@ -260,6 +262,20 @@ Item { radialGradient : sensorVisualizer.toString().includes("Ping360") ? radialGradient : linearGradient } + QQuickOpenGL { + id: potato + //width: 200 + //height: 200 + anchors.centerIn: parent + //anchors.fill: parent + width: Math.min(parent.height, parent.width) + height: width + //width: 200 + //height: 200 + visible: true + z: 10000000 + } + PingStatus { // We need to be over the mainvisualizer z: sensorVisualizer ? sensorVisualizer.z + 1 : 0 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/Ping360Visualizer.qml b/qml/Ping360Visualizer.qml index e9a2b01c8..05eabfd97 100644 --- a/qml/Ping360Visualizer.qml +++ b/qml/Ping360Visualizer.qml @@ -60,7 +60,7 @@ Item { onWidthChanged: { if(chart.Layout.minimumWidth === chart.width) { - shader.parent.width = width - chart.width + waterfall.parent.width = width - chart.width } } @@ -73,25 +73,25 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - /** TODO: The shader logic should be inside done inside of PolarPlot c++ Object - * Or better encapsulated - */ - ShaderEffect { - id: shader + PolarPlot { + id: waterfall + /* height: Math.min(ping.sectorSize > 180 ? parent.height : parent.height*2, parent.width*scale) width: height anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: ping.sectorSize > 180 ? parent.verticalCenter : parent.bottom + */ + anchors.fill: parent property var scale: ping.sectorSize >= 180 ? 1 : 0.8/Math.sin(ping.sectorSize*Math.PI/360) - property variant src: waterfall + //property variant src: waterfall property var angle: 1 property bool verticalFlip: false property bool horizontalFlip: false - vertexShader: "qrc:/opengl/polarplot/vertex.glsl" - fragmentShader: "qrc:/opengl/polarplot/fragment.glsl" + //vertexShader: "qrc:/opengl/polarplot/vertex.glsl" + //fragmentShader: "qrc:/opengl/polarplot/fragment.glsl" Text { id: northText @@ -158,7 +158,7 @@ Item { } Shape { - visible: waterfall.containsMouse + //visible: waterfall.containsMouse anchors.centerIn: parent opacity: 0.5 ShapePath { @@ -168,7 +168,7 @@ Item { startY: 0 //TODO: This need to be updated in sensor integration PathLine { - property real angle: -Math.atan2(waterfall.mousePos.x - waterfall.width/2, waterfall.mousePos.y - waterfall.height/2) + Math.PI/2 + property real angle: 0//-Math.atan2(waterfall.mousePos.x - waterfall.width/2, waterfall.mousePos.y - waterfall.height/2) + Math.PI/2 x: waterfall.width*Math.cos(angle)/2 y: waterfall.height*Math.sin(angle)/2 } @@ -176,22 +176,16 @@ Item { } transform: Rotation { - origin.x: shader.width/2 - origin.y: shader.height/2 - axis { x: shader.verticalFlip; y: shader.horizontalFlip; z: ping.sectorSize > 180} + origin.x: waterfall.width/2 + origin.y: waterfall.height/2 + axis { x: waterfall.verticalFlip; y: waterfall.horizontalFlip; z: ping.sectorSize > 180} angle: -ping.heading*180/200 } } - PolarPlot { - id: waterfall - anchors.fill: shader - visible: false - } - Text { id: mouseReadout - visible: waterfall.containsMouse + //visible: waterfall.containsMouse anchors.right: parent.right anchors.top: parent.top anchors.margins: 5 @@ -214,17 +208,17 @@ Item { function calcAngleFromFlips(angle) { var value = waterfall.mouseSampleAngle - if(shader.verticalFlip && shader.horizontalFlip) { + if(waterfall.verticalFlip && waterfall.horizontalFlip) { value = (360 + 270 - value) % 360 return transformValue(value) } - if(shader.verticalFlip) { + if(waterfall.verticalFlip) { value = (360 + 180 - value) % 360 return transformValue(value) } - if(shader.horizontalFlip) { + if(waterfall.horizontalFlip) { value = 360 - value return transformValue(value) } @@ -236,7 +230,7 @@ Item { PolarGrid { id: polarGrid - anchors.fill: shader + anchors.fill: waterfall angle: ping.sectorSize maxDistance: waterfall.maxDistance } @@ -246,7 +240,7 @@ Item { height: 10 anchors.bottom: parent.bottom anchors.right: parent.right - waterfallGradient: waterfall.waterfallGradient + //waterfallGradient: waterfall.waterfallGradient } } @@ -311,7 +305,7 @@ Item { Layout.columnSpan: 5 Layout.fillWidth: true onCheckStateChanged: { - shader.horizontalFlip = checkState + waterfall.horizontalFlip = checkState } } diff --git a/qml/main.qml b/qml/main.qml index ef7f73e6b..ccdbcdadc 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -29,6 +29,7 @@ ApplicationWindow { width: parent.width/2 height: parent.height*0.9 anchors.centerIn: parent + visible: false } } diff --git a/qml/opengl/polarplot/fragment.glsl b/qml/opengl/polarplot/fragment.glsl index d4c74503f..9f1caead5 100644 --- a/qml/opengl/polarplot/fragment.glsl +++ b/qml/opengl/polarplot/fragment.glsl @@ -3,10 +3,12 @@ * Tilman Schmidt [KeyMaster-] example */ uniform sampler2D src; -uniform float angle; varying vec2 coord; void main() { + gl_FragColor = vec4(0, coord.x, coord.y, 1.0); + return; + vec2 sizeOverRadius = vec2(2.0, 2.0); float sampleOffset = 0.0; float polarFactor = 1.0; @@ -25,8 +27,8 @@ void main() { polar.y = sqrt(relPos.x * relPos.x + relPos.y * relPos.y); //Any radius over 1 would go beyond the source texture size, this simply outputs black for those fragments - if(polar.y > 1.0){ - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + if(polar.y > 0.5){ + gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); return; } diff --git a/qml/opengl/polarplot/vertex.glsl b/qml/opengl/polarplot/vertex.glsl index 47c80206f..2a4669d01 100644 --- a/qml/opengl/polarplot/vertex.glsl +++ b/qml/opengl/polarplot/vertex.glsl @@ -1,8 +1,6 @@ -uniform highp mat4 qt_Matrix; -attribute highp vec4 qt_Vertex; -attribute highp vec2 qt_MultiTexCoord0; -varying highp vec2 coord; +attribute vec4 vertices; +varying vec2 coords; void main() { - coord = qt_MultiTexCoord0; - gl_Position = qt_Matrix * qt_Vertex; + gl_Position = vertices; + coords = vertices.xy; } diff --git a/qml/opengl/qquickopengl/fragment.glsl b/qml/opengl/qquickopengl/fragment.glsl new file mode 100644 index 000000000..17af87ff4 --- /dev/null +++ b/qml/opengl/qquickopengl/fragment.glsl @@ -0,0 +1,10 @@ +varying highp vec2 coord; +uniform lowp float qt_Opacity; +void main() { + float d = distance(coord, vec2(0.5, 0.5)); + if ( d > 0.5) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * qt_Opacity; + return; + } + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * qt_Opacity; +} diff --git a/qml/opengl/qquickopengl/vertex.glsl b/qml/opengl/qquickopengl/vertex.glsl new file mode 100644 index 000000000..4ba495da2 --- /dev/null +++ b/qml/opengl/qquickopengl/vertex.glsl @@ -0,0 +1,11 @@ +uniform highp mat4 qt_Matrix; + +attribute highp vec4 qt_Vertex; +attribute highp vec2 qt_MultiTexCoord0; + +varying highp vec2 coord; + +void main() { + gl_Position = qt_Matrix * qt_Vertex; + coord = qt_MultiTexCoord0;// + vec2(1.0, 1.0)) / 2.0; +} 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..7a254f0e6 100644 --- a/resources.qrc +++ b/resources.qrc @@ -69,5 +69,9 @@ qml/opengl/polarplot/fragment.glsl qml/opengl/polarplot/vertex.glsl + qml/opengl/qquickopengl/fragment.glsl + qml/opengl/qquickopengl/vertex.glsl + qml/opengl/waterfallplot/fragment.glsl + qml/opengl/waterfallplot/vertex.glsl diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2668cc6bd..ff291d384 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ set(INCLUDE_DIRS mavlink network notification + qquickcomponents sensor settings style diff --git a/src/main.cpp b/src/main.cpp index 031bee4da..97a0925bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,11 +22,14 @@ #include "ping.h" #include "ping360.h" #include "polarplot.h" +#include "qquickopengl.h" #include "settingsmanager.h" #include "stylemanager.h" #include "util.h" #include "waterfallplot.h" +#include "qquickopengl.h" + Q_DECLARE_LOGGING_CATEGORY(mainCategory) PING_LOGGING_CATEGORY(mainCategory, "ping.main") @@ -68,6 +71,7 @@ int main(int argc, char *argv[]) qmlRegisterType("Ping", 1, 0, "Ping"); qmlRegisterType("Ping360", 1, 0, "Ping360"); qmlRegisterType("PolarPlot", 1, 0, "PolarPlot"); + qmlRegisterType("QQuickOpenGL", 1, 0, "QQuickOpenGL"); qmlRegisterType("WaterfallPlot", 1, 0, "WaterfallPlot"); qmlRegisterUncreatableMetaObject( diff --git a/src/qquickcomponents/CMakeLists.txt b/src/qquickcomponents/CMakeLists.txt new file mode 100644 index 000000000..acc49ea52 --- /dev/null +++ b/src/qquickcomponents/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library( + qquickcomponents +STATIC + qquickopengl.cpp +) + +target_link_libraries(qquickcomponents +PRIVATE + Qt5::Core + Qt5::Quick +) diff --git a/src/qquickcomponents/qquickcomponents.pri b/src/qquickcomponents/qquickcomponents.pri new file mode 100644 index 000000000..f1d7a6eae --- /dev/null +++ b/src/qquickcomponents/qquickcomponents.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/*.h + +SOURCES += \ + $$PWD/*.cpp diff --git a/src/qquickcomponents/qquickopengl.cpp b/src/qquickcomponents/qquickopengl.cpp new file mode 100644 index 000000000..5512b415d --- /dev/null +++ b/src/qquickcomponents/qquickopengl.cpp @@ -0,0 +1,129 @@ +#include "qquickopengl.h" + +#include + +#include + +#include +#include + +/* +struct MaterialState +{ + QSGTexture *source; +}; + +class MaterialShader : public QSGSimpleMaterialShader +{ + QSG_DECLARE_SIMPLE_SHADER(MaterialShader, MaterialState) + + MaterialShader() + { + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/opengl/qquickopengl/fragment.glsl")); + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/opengl/qquickopengl/vertex.glsl")); + } + + QList attributes() const + { + return QList() << "qt_Vertex" << "qt_MultiTexCoord0"; + } + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + { + Q_ASSERT(program()->isLinked()); + if (state.isOpacityDirty()) { + program()->setUniformValue(m_id_opacity, state.opacity()); + } + if (state.isMatrixDirty()) { + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + } + } +}*/ + +class Material : public QSGMaterial +{ +public: + QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader() const { return new Shader; } + int toto() { return i++; } +private: + int i; +}; + +char const *const *Shader::attributeNames() const +{ + static char const *const names[] = {"qt_Vertex", "qt_MultiTexCoord0", 0 }; + return names; +} + +void Shader::loadDefaultShader() +{ + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/opengl/qquickopengl/fragment.glsl")); + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/opengl/qquickopengl/vertex.glsl")); +} + +void Shader::initialize() +{ + QSGMaterialShader::initialize(); + _matrixId = program()->uniformLocation("qt_Matrix"); + _opacityId = program()->uniformLocation("qt_Opacity"); +} + +void Shader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) +{ + Q_ASSERT(program()->isLinked()); + if (state.isMatrixDirty()) { + program()->setUniformValue(_matrixId, state.combinedMatrix()); + } + if (state.isOpacityDirty()) { + program()->setUniformValue(_opacityId, state.opacity()); + } + + // Let the child class to receive and do the necessary updates with the new state + update(state, newMaterial, oldMaterial); +} + +QQuickOpenGL::QQuickOpenGL(QQuickItem *parent) + : QQuickItem(parent) + , _shaderProgram(nullptr) + , _window(nullptr) +{ + // We are going to fill our own content with OpenGL + QQuickItem::setFlag(QQuickItem::ItemHasContents); + connect(this, &QQuickItem::windowChanged, this, &QQuickOpenGL::handleWindowChanged); +} + +void QQuickOpenGL::handleWindowChanged(QQuickWindow* window) +{ + if (!window) { + return; + } + + _window = window; + window->setClearBeforeRendering(false); +} + + +QSGNode *QQuickOpenGL::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + QSGSimpleRectNode *simpleRectNode = static_cast(node); + if (!simpleRectNode) { + // We are going to deal with the material and the geometry + simpleRectNode = new QSGSimpleRectNode(); + simpleRectNode->setMaterial(new Material()); + simpleRectNode->material()->setFlag(QSGMaterial::Blending); + simpleRectNode->setFlag(QSGNode::OwnsMaterial, true); + + QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); + QSGGeometry::updateTexturedRectGeometry(geometry, boundingRect(), QRectF(0, 0, 1, 1)); + simpleRectNode->setGeometry(geometry); + simpleRectNode->setFlag(QSGNode::OwnsGeometry, true); + } else { + // Update the geometry to make sure that we have the correct size + QSGGeometry::updateTexturedRectGeometry(simpleRectNode->geometry(), boundingRect(), QRectF(0, 0, 1, 1)); + simpleRectNode->markDirty(QSGNode::DirtyGeometry); + } + + simpleRectNode->setRect(boundingRect()); + return simpleRectNode; +} diff --git a/src/qquickcomponents/qquickopengl.h b/src/qquickcomponents/qquickopengl.h new file mode 100644 index 000000000..950545388 --- /dev/null +++ b/src/qquickcomponents/qquickopengl.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +#include + +#include + +class Shader : public QSGMaterialShader +{ +public: + Shader() + { + loadDefaultShader(); + } + + /** + * @brief Return attribute names + * The last element should be zero, from documentation + * + * @return char const* const* + */ + char const *const *attributeNames() const; + + void loadDefaultShader(); + + void initialize(); + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + + virtual void update(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + { + Q_UNUSED(state) + Q_UNUSED(newMaterial) + Q_UNUSED(oldMaterial) + } + +private: + int _matrixId; + int _opacityId; +}; + +class QQuickOpenGL : public QQuickItem +{ + Q_OBJECT + +public: + QQuickOpenGL(QQuickItem *parent = nullptr); + void paint(); + + void handleWindowChanged(QQuickWindow *window); + + QSGNode* updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + +signals: + void shiftChanged(); + +private: + QSharedPointer _shaderProgram; + int _shift; + QQuickWindow* _window; +}; diff --git a/src/src.pri b/src/src.pri index e88c1fcb7..950b57ea2 100644 --- a/src/src.pri +++ b/src/src.pri @@ -24,6 +24,7 @@ include($$PWD/logger/logger.pri) include($$PWD/mavlink/mavlink.pri) include($$PWD/network/network.pri) include($$PWD/notification/notification.pri) +include($$PWD/qquickcomponents/qquickcomponents.pri) include($$PWD/sensor/sensor.pri) include($$PWD/settings/settings.pri) include($$PWD/style/style.pri) diff --git a/src/waterfall/polarplot.cpp b/src/waterfall/polarplot.cpp index ccaa9e120..ba266d678 100644 --- a/src/waterfall/polarplot.cpp +++ b/src/waterfall/polarplot.cpp @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -14,7 +15,7 @@ PING_LOGGING_CATEGORY(polarplot, "ping.polarplot") uint16_t PolarPlot::_angularResolution = 400; PolarPlot::PolarPlot(QQuickItem* parent) - : Waterfall(parent) + : QQuickOpenGL(parent) , _distances(_angularResolution, 0) , _image(400, 1200, QImage::Format_RGBA8888) , _maxDistance(0) @@ -25,14 +26,18 @@ PolarPlot::PolarPlot(QQuickItem* parent) setAcceptHoverEvents(true); _image.fill(QColor(Qt::transparent)); - connect(&_updateTimer, &QTimer::timeout, this, [&] { update(); }); - _updateTimer.setSingleShot(true); - _updateTimer.start(50); + //setVertex(":/opengl/polarplot/vertex.glsl"); + //setFragment(":/opengl/polarplot/fragment.glsl"); - connect(this, &Waterfall::mousePosChanged, this, &PolarPlot::updateMouseColumnData); - connect(this, &Waterfall::themeChanged, this, &PolarPlot::clear); + //connect(&_updateTimer, &QTimer::timeout, this, [&] { update(); }); + //_updateTimer.setSingleShot(true); + //_updateTimer.start(50); + + //connect(this, &Waterfall::mousePosChanged, this, &PolarPlot::updateMouseColumnData); + //connect(this, &Waterfall::themeChanged, this, &PolarPlot::clear); } +/* void PolarPlot::clear() { qCDebug(polarplot) << "Cleaning waterfall and restarting internal variables"; @@ -40,6 +45,14 @@ void PolarPlot::clear() _distances.fill(0, _angularResolution); _maxDistance = 0; } +*/ + +/* +void setValue(QOpenGLShaderProgram *shaderProgram) +{ + printf(""); + //shaderProgram->setUniformValue("coord", static_cast((_shift%100)/100.0f)); +}*/ void PolarPlot::paint(QPainter* painter) { @@ -102,18 +115,20 @@ void PolarPlot::draw( } for (int index = 0; index < _image.height(); index++) { - _image.setPixelColor(static_cast(newAngle), index, valueToRGB(points[index * scale])); + //_image.setPixelColor(static_cast(newAngle), index, valueToRGB(points[index * scale])); } } // Fix max update in 20Hz at max + /* if (!_updateTimer.isActive()) { _updateTimer.start(50); - } + }*/ emit imageChanged(); } +#if 0 void PolarPlot::updateMouseColumnData() { static const float rad2grad = 200.0f / M_PI; @@ -146,3 +161,4 @@ void PolarPlot::updateMouseColumnData() emit mouseSampleAngleChanged(); emit mouseSampleDistanceChanged(); } +#endif diff --git a/src/waterfall/polarplot.h b/src/waterfall/polarplot.h index 2bfff0b55..32aa3494b 100644 --- a/src/waterfall/polarplot.h +++ b/src/waterfall/polarplot.h @@ -5,6 +5,7 @@ #include #include "logger.h" +#include "qquickopengl.h" #include "ringvector.h" #include "waterfall.h" #include "waterfallgradient.h" @@ -15,7 +16,7 @@ Q_DECLARE_LOGGING_CATEGORY(waterfall) * @brief Polar widget * */ -class PolarPlot : public Waterfall { +class PolarPlot : public QQuickOpenGL { Q_OBJECT public: @@ -26,13 +27,20 @@ class PolarPlot : public Waterfall { */ PolarPlot(QQuickItem* parent = nullptr); + /** + * @brief Define setValue + * + * @param shaderProgram + */ + //void setValue(QOpenGLShaderProgram *shaderProgram) override final; + /** * @brief This is used by the qml paint event * This paint the a polar waterfall in qml * * @param painter */ - void paint(QPainter* painter) final override; + void paint(QPainter* painter); /** * @brief Set the polar Image @@ -58,7 +66,7 @@ class PolarPlot : public Waterfall { * @brief Clear waterfall and restart all parameters * */ - Q_INVOKABLE void clear() final override; + //Q_INVOKABLE void clear() final override; /** * @brief Return waterfall image @@ -106,7 +114,7 @@ class PolarPlot : public Waterfall { * @brief Update mouse column information * */ - void updateMouseColumnData(); + //void updateMouseColumnData(); QVector _distances; QImage _image; 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();