From a96f9f1a2688c215c478cfbee5748b4bb2043a43 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Mon, 18 May 2015 18:33:50 -0300 Subject: Updated UI. - Removed unnecessary UI elements from QML file; - Added the ColorScale class and implemented glyph color mapping from class labels; - Mark geometry nodes of individual glyphs as dirty when updating the scene graph. --- colorscale.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ colorscale.h | 24 +++++++++++++++++++ main.cpp | 9 +++++--- main_view.qml | 27 ---------------------- pm.pro | 5 +++- scatterplot.cpp | 45 ++++++++++++++++++------------------ scatterplot.h | 9 ++++++-- 7 files changed, 136 insertions(+), 55 deletions(-) create mode 100644 colorscale.cpp create mode 100644 colorscale.h diff --git a/colorscale.cpp b/colorscale.cpp new file mode 100644 index 0000000..50fedae --- /dev/null +++ b/colorscale.cpp @@ -0,0 +1,72 @@ +#include "colorscale.h" + +ColorScale::ColorScale(const QColor &firstColor, const QColor &lastColor) + : m_colors{firstColor, lastColor} +{ + setExtents(0, 1); +} + +ColorScale::ColorScale(std::initializer_list colors) + : m_colors(colors) +{ + setExtents(0, 1); +} + +ColorScale::ColorScale(const QList &colors) + : m_colors(colors) +{ + setExtents(0, 1); +} + +ColorScale::~ColorScale() +{ +} + +void ColorScale::setExtents(qreal min, qreal max) +{ + if (min >= max) + return; + + m_min = min; + m_max = max; +} + +static QColor lerp(const QColor &c1, const QColor &c2, qreal t) +{ + qreal r1, g1, b1, a1; + qreal r2, g2, b2, a2; + + c1.getRgbF(&r1, &g1, &b1, &a1); + c2.getRgbF(&r2, &g2, &b2, &a2); + QColor color; + color.setRgbF(r1 * (1 - t) + r2 * t, + g1 * (1 - t) + g2 * t, + b1 * (1 - t) + b2 * t, + a1 * (1 - t) + a2 * t); + return color; +} + +QColor ColorScale::color(qreal t) const +{ + if (t < m_min || t > m_max) + return QColor(); + + // normalize t + t = (t - m_min) / (m_max - m_min); + + // two colors, use a simpler solution + if (m_colors.size() == 2) + return lerp(m_colors.first(), m_colors.last(), t); + + // find which colors in the scale are adjacent to ours + qreal step = 1.0 / m_colors.size(); + int i = (int) (t / step); + int j = i + 1; + + if (i >= m_colors.size() - 1) + return QColor(m_colors.last()); + + // normalize t between the two colors + t = (t - i*step) / (j*step - i*step); + return lerp(m_colors[i], m_colors[j], t); +} diff --git a/colorscale.h b/colorscale.h new file mode 100644 index 0000000..6e1212d --- /dev/null +++ b/colorscale.h @@ -0,0 +1,24 @@ +#ifndef COLORSCALE_H +#define COLORSCALE_H + +#include +#include +#include + +class ColorScale +{ +public: + ColorScale(const QColor &firstColor, const QColor &lastColor); + ColorScale(std::initializer_list colors); + ColorScale(const QList &colors); + ~ColorScale(); + + void setExtents(qreal min, qreal max); + QColor color(qreal t) const; + +private: + qreal m_min, m_max; + QList m_colors; +}; + +#endif // COLORSCALE_H diff --git a/main.cpp b/main.cpp index fb6ab1e..a00d884 100644 --- a/main.cpp +++ b/main.cpp @@ -35,9 +35,12 @@ int main(int argc, char **argv) if (argc > 1) { arma::mat X; X.load(argv[1], arma::raw_ascii); - arma::mat projection = getProjection(X); - Scatterplot *plot = view.rootObject()->findChild("plot", Qt::FindDirectChildrenOnly); - plot->setData(projection); + + Scatterplot *plot = view.rootObject()->findChild("plot"); + arma::mat scatterData(X.n_rows, 3); + scatterData.cols(0, 1) = getProjection(X.cols(0, X.n_cols - 2)); + scatterData.col(2) = X.col(X.n_cols - 1); + plot->setData(scatterData); } view.show(); diff --git a/main_view.qml b/main_view.qml index bf143fc..a9ff37e 100644 --- a/main_view.qml +++ b/main_view.qml @@ -10,31 +10,4 @@ Item { objectName: "plot" anchors.fill: parent } - - Item { - id: messageBox - anchors.right: parent.right - anchors.left: parent.left - anchors.bottom: parent.bottom - - Rectangle { - color: Qt.rgba(1, 1, 1, 0.7) - radius: 5 - border.width: 1 - border.color: "white" - anchors.fill: messageLabel - anchors.margins: -10 - } - - Text { - id: messageLabel - color: "black" - wrapMode: Text.WordWrap - text: "The background here is a squircle rendered with raw OpenGL using the 'beforeRender()' signal in QQuickWindow. This text label and its border is rendered using QML" - anchors.right: parent.right - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.margins: 20 - } - } } diff --git a/pm.pro b/pm.pro index 2ba534d..1c6e26a 100644 --- a/pm.pro +++ b/pm.pro @@ -1,9 +1,12 @@ QT += qml quick +QMAKE_CXXFLAGS += -std=c++11 QMAKE_LIBS += -larmadillo -HEADERS += scatterplot.h \ +HEADERS += colorscale.h \ + scatterplot.h \ mp.h SOURCES += main.cpp \ + colorscale.cpp \ scatterplot.cpp \ lamp.cpp \ forceScheme.cpp \ diff --git a/scatterplot.cpp b/scatterplot.cpp index b5e8261..a3a0dfc 100644 --- a/scatterplot.cpp +++ b/scatterplot.cpp @@ -1,18 +1,22 @@ #include "scatterplot.h" +#include + #include -#include #include #include #include #include #include +#include -const int GLYPH_SIZE = 4; -const float PADDING = 5; +const int GLYPH_SIZE = 5; +const float PADDING = 10; const float PI = 3.1415f; -Scatterplot::Scatterplot() +Scatterplot::Scatterplot(QQuickItem *parent) + : QQuickItem(parent) + , m_colorScale{QColor("red"), QColor("green"), QColor("blue")} { setFlag(QQuickItem::ItemHasContents); } @@ -23,10 +27,11 @@ Scatterplot::~Scatterplot() void Scatterplot::setData(const arma::mat &data) { - if (data.n_cols != 2) + if (data.n_cols != 3) return; m_data = data; + m_colorScale.setExtents(m_data.col(2).min(), m_data.col(2).max()); update(); } @@ -68,10 +73,10 @@ void updateSquareGeometry(QSGGeometry *geometry, float size, float cx, float cy) QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { + if (m_data.n_rows < 1) + return 0; + QSGNode *node = 0; - QSGGeometryNode *childNode = 0; - QSGGeometry *geometry = 0; - QSGFlatColorMaterial *material = 0; int vertexCount = calculateCircleVertexCount(GLYPH_SIZE / 2); qreal xmin = m_data.col(0).min(), xmax = m_data.col(0).max(), @@ -82,19 +87,15 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) if (!oldNode) { node = new QSGNode; for (arma::uword i = 0; i < m_data.n_rows; i++) { - arma::rowvec row = m_data.row(i); - x = (row[0] - xmin) / (xmax - xmin) * width(); - y = (row[1] - ymin) / (ymax - ymin) * height(); - - childNode = new QSGGeometryNode; + QSGGeometryNode *childNode = new QSGGeometryNode; - geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount); + QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount); geometry->setDrawingMode(GL_POLYGON); childNode->setGeometry(geometry); childNode->setFlag(QSGNode::OwnsGeometry); - material = new QSGFlatColorMaterial; - material->setColor(QColor()); + QSGFlatColorMaterial *material = new QSGFlatColorMaterial; + material->setColor(m_colorScale.color(m_data(i, 2))); childNode->setMaterial(material); childNode->setFlag(QSGNode::OwnsMaterial); @@ -104,17 +105,17 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) node = oldNode; } - childNode = static_cast(node->firstChild()); + QSGNode *childNode = node->firstChild(); for (arma::uword i = 0; i < m_data.n_rows; i++) { arma::rowvec row = m_data.row(i); - x = (row[0] - xmin) / (xmax - xmin) * width(); - y = (row[1] - ymin) / (ymax - ymin) * height(); + x = PADDING + (row[0] - xmin) / (xmax - xmin) * (width() - 2*PADDING); + y = PADDING + (row[1] - ymin) / (ymax - ymin) * (height() - 2*PADDING); - geometry = childNode->geometry(); + QSGGeometry *geometry = static_cast(childNode)->geometry(); updateCircleGeometry(geometry, GLYPH_SIZE, x, y); - childNode = static_cast(childNode->nextSibling()); + childNode->markDirty(QSGNode::DirtyGeometry); + childNode = childNode->nextSibling(); } - node->markDirty(QSGNode::DirtyGeometry); return node; } diff --git a/scatterplot.h b/scatterplot.h index f38ce78..3f2fee6 100644 --- a/scatterplot.h +++ b/scatterplot.h @@ -5,22 +5,27 @@ #include #include +#include "colorscale.h" + class Scatterplot : public QQuickItem { Q_OBJECT public: - Scatterplot(); + Scatterplot(QQuickItem *parent = 0); ~Scatterplot(); - QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); void setData(const arma::mat &data); signals: public slots: +protected: + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); + private: arma::mat m_data; + ColorScale m_colorScale; }; #endif // SCATTERPLOT_H -- cgit v1.2.3