From 7676a38ea8a06d3d31228cb7eb1be00f90de46d3 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Thu, 7 Jan 2016 15:10:20 +0100 Subject: Added a bar chart. * HistoryGraph replaced by BarChart * HistoryGraph not removed from code, might be useful in the future --- barchart.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ barchart.h | 34 ++++++++++++++++ main.cpp | 18 +++++---- main_view.qml | 12 ++++-- pm.pro | 2 + 5 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 barchart.cpp create mode 100644 barchart.h diff --git a/barchart.cpp b/barchart.cpp new file mode 100644 index 0000000..02509ae --- /dev/null +++ b/barchart.cpp @@ -0,0 +1,123 @@ +#include "barchart.h" + +#include + +#include "geometry.h" +#include "scale.h" + +static const QColor OUTLINE_COLOR(0, 0, 0); +static const QColor BAR_COLOR(128, 128, 128); +static const float DEFAULT_OPACITY = 0.8f; + +BarChart::BarChart(QQuickItem *parent) + : QQuickItem(parent) + , m_shouldUpdateBars(false) +{ + setClip(true); + setFlag(QQuickItem::ItemHasContents); + // setAcceptedMouseButtons(Qt::LeftButton); + setAcceptHoverEvents(true); +} + +BarChart::~BarChart() +{ +} + +void BarChart::setValues(const arma::vec &values) +{ + m_values = values; + m_shouldUpdateBars = true; + emit valuesChanged(values); +} + +QSGNode *BarChart::newBarNode() +{ + // A bar node is: + // opacityNode [outlineGeomNode barGeomNode] + + QSGGeometryNode *outlineGeomNode = new QSGGeometryNode; + QSGGeometry *outlineGeometry = + new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4); + outlineGeometry->setDrawingMode(GL_LINE_LOOP); + outlineGeomNode->setGeometry(outlineGeometry); + outlineGeomNode->setFlag(QSGNode::OwnsGeometry); + QSGFlatColorMaterial *material = new QSGFlatColorMaterial; + material->setColor(OUTLINE_COLOR); + outlineGeomNode->setMaterial(material); + outlineGeomNode->setFlag(QSGNode::OwnsMaterial); + + QSGGeometryNode *barGeomNode = new QSGGeometryNode; + QSGGeometry *barGeometry = + new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4); + barGeometry->setDrawingMode(GL_POLYGON); + barGeomNode->setGeometry(barGeometry); + barGeomNode->setFlag(QSGNode::OwnsGeometry); + material = new QSGFlatColorMaterial; + material->setColor(BAR_COLOR); + barGeomNode->setMaterial(material); + barGeomNode->setFlag(QSGNode::OwnsMaterial); + + QSGOpacityNode *opacityNode = new QSGOpacityNode; + opacityNode->setOpacity(DEFAULT_OPACITY); + opacityNode->appendChildNode(barGeomNode); + opacityNode->appendChildNode(outlineGeomNode); + + return opacityNode; +} + +void BarChart::updateBarNodeGeom(QSGNode *barNode, float x, float barWidth, float barHeight) +{ + QSGGeometryNode *outlineGeomNode = + static_cast(barNode->firstChild()); + QSGGeometryNode *barGeomNode = + static_cast(barNode->firstChild()->nextSibling()); + + float y = height() - barHeight; + updateRectGeometry(outlineGeomNode->geometry(), x, y, barWidth, barHeight); + updateRectGeometry(barGeomNode->geometry(), x, y, barWidth, barHeight); +} + +void BarChart::updateBars(QSGNode *root) +{ + QSGNode *node = root->firstChild(); + float x = 0; + float barWidth = width() / m_values.n_elem; + LinearScale heightScale(m_values.min(), m_values.max(), 0, height()); + + for (auto it = m_values.cbegin(); it != m_values.cend(); it++) { + updateBarNodeGeom(node, x, barWidth, heightScale((float) *it)); + x += barWidth; + node = node->nextSibling(); + } +} + +QSGNode *BarChart::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QSGNode *root = oldNode ? oldNode : new QSGNode; + if (m_shouldUpdateBars) { + // First, make sure we have the same number of values & bars + while (m_values.n_elem > root->childCount()) { + QSGNode *barNode = newBarNode(); + root->appendChildNode(barNode); + } + while (m_values.n_elem < root->childCount()) { + // NOTE: as stated in docs, QSGNode's children are stored in a + // linked list. Hence, this operation should be as fast as expected + root->removeChildNode(root->firstChild()); + } + + // Then, update the geometry of bars to reflect the values + updateBars(root); + m_shouldUpdateBars = false; + } + + return root; +} + +void BarChart::hoverMoveEvent(QHoverEvent *event) +{ +} + +void BarChart::mousePressEvent(QMouseEvent *event) +{ +} diff --git a/barchart.h b/barchart.h new file mode 100644 index 0000000..ccbb13e --- /dev/null +++ b/barchart.h @@ -0,0 +1,34 @@ +#ifndef BARCHART_H +#define BARCHART_H + +#include +#include + +class BarChart : public QQuickItem +{ + Q_OBJECT +public: + BarChart(QQuickItem *parent = 0); + ~BarChart(); + +signals: + void valuesChanged(const arma::vec &values); + +public slots: + void setValues(const arma::vec &values); + +protected: + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); + void hoverMoveEvent(QHoverEvent *event); + void mousePressEvent(QMouseEvent *event); + +private: + QSGNode *newBarNode(); + void updateBarNodeGeom(QSGNode *barNode, float x, float width, float height); + void updateBars(QSGNode *root); + + arma::vec m_values; + bool m_shouldUpdateBars; +}; + +#endif // BARCHART_H diff --git a/main.cpp b/main.cpp index b9d7232..41d0ed0 100644 --- a/main.cpp +++ b/main.cpp @@ -14,6 +14,7 @@ #include "scatterplot.h" #include "voronoisplat.h" #include "historygraph.h" +#include "barchart.h" #include "interactionhandler.h" #include "selectionhandler.h" #include "effectivenessobserver.h" @@ -158,6 +159,7 @@ int main(int argc, char **argv) qmlRegisterType("PM", 1, 0, "Scatterplot"); qmlRegisterType("PM", 1, 0, "HistoryGraph"); + qmlRegisterType("PM", 1, 0, "BarChart"); qmlRegisterType("PM", 1, 0, "InteractionHandler"); qmlRegisterSingletonType
("PM", 1, 0, "Main", mainProvider); @@ -222,11 +224,14 @@ int main(int argc, char **argv) plot, SLOT(setSelection(const QSet &))); // Connections between history graph and subsample plot - HistoryGraph *history = engine.rootObjects()[0]->findChild("history"); - QObject::connect(subsamplePlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)), - history, SLOT(addHistoryItem(const arma::mat &))); - QObject::connect(history, SIGNAL(currentItemChanged(const arma::mat &)), - subsamplePlot, SLOT(setXY(const arma::mat &))); + //HistoryGraph *history = engine.rootObjects()[0]->findChild("history"); + //QObject::connect(subsamplePlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)), + // history, SLOT(addHistoryItem(const arma::mat &))); + //QObject::connect(history, SIGNAL(currentItemChanged(const arma::mat &)), + // subsamplePlot, SLOT(setXY(const arma::mat &))); + + BarChart *barChart = engine.rootObjects()[0]->findChild("barChart"); + barChart->setValues(arma::randn(100)); // Map distortion as the glyph color //DistortionObserver distortionObs(X, sampleIndices); @@ -245,8 +250,7 @@ int main(int argc, char **argv) //QObject::connect(&enforcer, SIGNAL(effectivenessChanged(const arma::vec &)), // subsamplePlot, SLOT(setColorData(const arma::vec &))); - - history->addHistoryItem(Ys); + //history->addHistoryItem(Ys); subsamplePlot->setXY(Ys); subsamplePlot->setColorData(labels(sampleIndices)); plot->setColorScale(&colorScale); diff --git a/main_view.qml b/main_view.qml index a9c1c18..1be06d0 100644 --- a/main_view.qml +++ b/main_view.qml @@ -107,11 +107,17 @@ ApplicationWindow { border.width: 1 border.color: "#cccccc" - HistoryGraph { - id: history - objectName: "history" + BarChart { + id: barChart + objectName: "barChart" anchors.fill: parent } + + //HistoryGraph { + // id: history + // objectName: "history" + // anchors.fill: parent + //} } } diff --git a/pm.pro b/pm.pro index 0a76551..54476c8 100644 --- a/pm.pro +++ b/pm.pro @@ -12,6 +12,7 @@ HEADERS += main.h \ scatterplot.h \ voronoisplat.h \ historygraph.h \ + barchart.h \ interactionhandler.h \ selectionhandler.h \ effectivenessobserver.h \ @@ -28,6 +29,7 @@ SOURCES += main.cpp \ scatterplot.cpp \ voronoisplat.cpp \ historygraph.cpp \ + barchart.cpp \ interactionhandler.cpp \ selectionhandler.cpp \ effectivenessobserver.cpp \ -- cgit v1.2.3