aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-01-26 18:15:50 +0100
committerSamuel Fadel <samuelfadel@gmail.com>2016-01-26 18:15:50 +0100
commit41e1b2bfb8e2ba3d0e74180200e7cc109171213e (patch)
tree27c7dbaa8fcdcc7efb300c0ea0a44b28d2ac76c0
parent7f0f945c35bc8c12d55efbce7545995fc76892c1 (diff)
Scatterplot & BarChart: initial brushing mechanism.
* Both components now support brushing (support in Scatterplot for activating a brush is still incomplete, though it can be brushed on by other components) * Added a handler for linking the brushing between components * Added crosshair geometry handler to geometry lib * Fixed issue #15
-rw-r--r--barchart.cpp95
-rw-r--r--barchart.h9
-rw-r--r--brushinghandler.cpp17
-rw-r--r--brushinghandler.h25
-rw-r--r--geometry.cpp25
-rw-r--r--geometry.h6
-rw-r--r--main.cpp45
-rw-r--r--pm.pro2
-rw-r--r--scatterplot.cpp89
-rw-r--r--scatterplot.h5
10 files changed, 245 insertions, 73 deletions
diff --git a/barchart.cpp b/barchart.cpp
index 343c3ef..22541d0 100644
--- a/barchart.cpp
+++ b/barchart.cpp
@@ -12,7 +12,7 @@
#include "geometry.h"
static const QColor OUTLINE_COLOR(0, 0, 0);
-static const QColor HINTS_COLOR(0, 0, 0);
+static const QColor BRUSH_COLOR(0, 0, 0);
static const QColor BAR_COLOR(128, 128, 128);
static const QColor SELECTION_COLOR(128, 128, 128, 96);
@@ -27,7 +27,7 @@ static inline T clamp(T value, T min, T max)
BarChart::BarChart(QQuickItem *parent)
: QQuickItem(parent)
, m_shouldUpdateBars(false)
- , m_hoverPos(-1.0f)
+ , m_brushedItem(-1)
, m_shouldUpdateSelectionRect(false)
, m_dragStartPos(-1.0f)
, m_dragLastPos(-1.0f)
@@ -48,13 +48,13 @@ void BarChart::setValues(const arma::vec &values)
{
m_values = values;
- m_originalIndices.resize(m_values.n_elem);
-
if (m_selection.size() != m_values.n_elem) {
m_selection.resize(m_values.n_elem);
m_selection.assign(m_selection.size(), false);
}
+ m_originalIndices.resize(m_values.n_elem);
+ m_currentIndices.resize(m_values.n_elem);
if (m_values.n_elem > 0) {
m_scale.setDomain(m_values.min(), m_values.max());
m_colorScale.setExtents(m_values.min(), m_values.max());
@@ -62,6 +62,11 @@ void BarChart::setValues(const arma::vec &values)
std::iota(m_originalIndices.begin(), m_originalIndices.end(), 0);
std::sort(m_originalIndices.begin(), m_originalIndices.end(),
[this](int i, int j) { return m_values[i] > m_values[j]; });
+
+ int k = 0;
+ for (auto i: m_originalIndices) {
+ m_currentIndices[i] = k++;
+ }
}
emit valuesChanged(values);
@@ -94,33 +99,44 @@ void BarChart::setSelection(const std::vector<bool> &selection)
update();
}
+void BarChart::brushItem(int item)
+{
+ if (item < 0) {
+ m_brushedItem = item;
+ emit itemBrushed(m_brushedItem);
+ } else {
+ m_brushedItem = m_currentIndices[item];
+ emit itemBrushed(m_originalIndices[m_brushedItem]);
+ }
+}
+
QSGNode *BarChart::newSceneGraph() const
{
// NOTE: scene graph structure is as follows:
- // root [ barsNode [ ... ] hoverHintsNode selectionNode ]
+ // root [ barsNode [ ... ] selectionNode brushNode ]
QSGTransformNode *root = new QSGTransformNode;
// The node that has all bars as children
root->appendChildNode(new QSGNode);
- // The node for drawing the hover hints
- QSGGeometryNode *hintsGeomNode = new QSGGeometryNode;
- QSGGeometry *hintsGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
- hintsGeom->setDrawingMode(GL_LINES);
- hintsGeom->setVertexDataPattern(QSGGeometry::DynamicPattern);
- hintsGeom->setLineWidth(1.0f);
- QSGFlatColorMaterial *hintsMaterial = new QSGFlatColorMaterial;
- hintsMaterial->setColor(HINTS_COLOR);
- hintsGeomNode->setGeometry(hintsGeom);
- hintsGeomNode->setMaterial(hintsMaterial);
- hintsGeomNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
- root->appendChildNode(hintsGeomNode);
-
- // The node for drawing the selectionRect
+ // The node for drawing the selection rect
QSGSimpleRectNode *selectionRectNode = new QSGSimpleRectNode;
selectionRectNode->setColor(SELECTION_COLOR);
root->appendChildNode(selectionRectNode);
+ // The node for drawing the brush
+ QSGGeometryNode *brushGeomNode = new QSGGeometryNode;
+ QSGGeometry *brushGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+ brushGeom->setDrawingMode(GL_POLYGON);
+ brushGeom->setVertexDataPattern(QSGGeometry::DynamicPattern);
+ brushGeom->setLineWidth(1.0f);
+ QSGFlatColorMaterial *brushMaterial = new QSGFlatColorMaterial;
+ brushMaterial->setColor(BRUSH_COLOR);
+ brushGeomNode->setGeometry(brushGeom);
+ brushGeomNode->setMaterial(brushMaterial);
+ brushGeomNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
+ root->appendChildNode(brushGeomNode);
+
return root;
}
@@ -225,16 +241,16 @@ void BarChart::updateBars(QSGNode *node)
}
}
-void BarChart::updateHoverHints(QSGNode *node)
+void BarChart::updateBrush(QSGNode *node)
{
- QSGGeometryNode *hintsGeomNode = static_cast<QSGGeometryNode *>(node);
- QSGGeometry *hintsGeom = hintsGeomNode->geometry();
-
- QSGGeometry::Point2D *vertexData = hintsGeom->vertexDataAsPoint2D();
- vertexData[0].set(m_hoverPos, 0.0f);
- vertexData[1].set(m_hoverPos, 1.0f);
-
- hintsGeomNode->markDirty(QSGNode::DirtyGeometry);
+ float barWidth = 1.0f / m_values.n_elem;
+ QSGGeometryNode *brushGeomNode = static_cast<QSGGeometryNode *>(node);
+ updateRectGeometry(brushGeomNode->geometry(),
+ barWidth * m_brushedItem,
+ 0.0f,
+ barWidth,
+ 1.0f);
+ brushGeomNode->markDirty(QSGNode::DirtyGeometry);
}
void BarChart::updateSelectionRect(QSGNode *node)
@@ -261,15 +277,15 @@ QSGNode *BarChart::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
}
node = node->nextSibling();
- updateHoverHints(node);
- node = node->nextSibling();
-
if (m_shouldUpdateSelectionRect) {
updateSelectionRect(node);
m_shouldUpdateSelectionRect = false;
}
node = node->nextSibling();
+ updateBrush(node);
+ node = node->nextSibling();
+
return root;
}
@@ -308,19 +324,25 @@ void BarChart::interactiveSelection(float start, float end)
void BarChart::hoverEnterEvent(QHoverEvent *event)
{
- m_hoverPos = float(event->pos().x()) / width();
+ m_brushedItem = itemAt(float(event->pos().x()) / width());
+ emit itemInteractivelyBrushed(m_originalIndices[m_brushedItem]);
+
update();
}
void BarChart::hoverMoveEvent(QHoverEvent *event)
{
- m_hoverPos = float(event->pos().x()) / width();
+ m_brushedItem = itemAt(float(event->pos().x()) / width());
+ emit itemInteractivelyBrushed(m_originalIndices[m_brushedItem]);
+
update();
}
void BarChart::hoverLeaveEvent(QHoverEvent *event)
{
- m_hoverPos = -1.0f;
+ m_brushedItem = -1;
+ emit itemInteractivelyBrushed(m_brushedItem);
+
update();
}
@@ -332,7 +354,7 @@ void BarChart::mousePressEvent(QMouseEvent *event)
float pos = float(event->pos().x()) / width();
m_dragStartPos = pos;
m_dragLastPos = pos;
- m_hoverPos = pos;
+
m_shouldUpdateSelectionRect = true;
update();
}
@@ -341,7 +363,7 @@ void BarChart::mouseMoveEvent(QMouseEvent *event)
{
float pos = float(event->pos().x()) / width();
m_dragLastPos = clamp(pos, 0.0f, 1.0f);
- m_hoverPos = pos;
+
m_shouldUpdateSelectionRect = true;
update();
}
@@ -352,7 +374,6 @@ void BarChart::mouseReleaseEvent(QMouseEvent *event)
float pos = float(event->pos().x()) / width();
m_dragLastPos = clamp(pos, 0.0f, 1.0f);
- m_hoverPos = pos;
if (m_values.n_elem > 0) {
interactiveSelection(m_dragStartPos, m_dragLastPos);
diff --git a/barchart.h b/barchart.h
index d49c001..e9de96d 100644
--- a/barchart.h
+++ b/barchart.h
@@ -23,11 +23,14 @@ signals:
void colorScaleChanged(const ColorScale &scale) const;
void selectionChanged(const std::vector<bool> &selection) const;
void selectionInteractivelyChanged(const std::vector<bool> &selection) const;
+ void itemBrushed(int item) const;
+ void itemInteractivelyBrushed(int item) const;
public slots:
void setValues(const arma::vec &values);
void setColorScale(const ColorScale &scale);
void setSelection(const std::vector<bool> &selection);
+ void brushItem(int item);
protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
@@ -48,9 +51,9 @@ private:
void updateBarNodeGeom(QSGNode *barNode, float x, float width, float height);
void updateBarNodeColor(QSGNode *barNode, const QColor &color);
void updateBars(QSGNode *node);
- void updateHoverHints(QSGNode *node);
+ void updateBrush(QSGNode *node);
bool m_shouldUpdateBars;
- float m_hoverPos;
+ int m_brushedItem;
void updateSelectionRect(QSGNode *node);
bool m_shouldUpdateSelectionRect;
@@ -62,7 +65,7 @@ private:
arma::vec m_values;
ColorScale m_colorScale;
- std::vector<int> m_originalIndices;
+ std::vector<int> m_originalIndices, m_currentIndices;
LinearScale<float> m_scale;
};
diff --git a/brushinghandler.cpp b/brushinghandler.cpp
new file mode 100644
index 0000000..0a8b9aa
--- /dev/null
+++ b/brushinghandler.cpp
@@ -0,0 +1,17 @@
+#include "brushinghandler.h"
+
+BrushingHandler::BrushingHandler()
+ : m_brushedItem(-1)
+{
+}
+
+void BrushingHandler::clearBrush()
+{
+ brushItem(-1);
+}
+
+void BrushingHandler::brushItem(int item)
+{
+ m_brushedItem = item;
+ emit itemBrushed(item);
+}
diff --git a/brushinghandler.h b/brushinghandler.h
new file mode 100644
index 0000000..ebee75a
--- /dev/null
+++ b/brushinghandler.h
@@ -0,0 +1,25 @@
+#ifndef BRUSHINGHANDLER_H
+#define BRUSHINGHANDLER_H
+
+#include <QObject>
+
+class BrushingHandler
+ : public QObject
+{
+ Q_OBJECT
+public:
+ BrushingHandler();
+
+ void clearBrush();
+
+signals:
+ void itemBrushed(int item) const;
+
+public slots:
+ void brushItem(int item);
+
+private:
+ int m_brushedItem;
+};
+
+#endif // BRUSHINGHANDLER_H
diff --git a/geometry.cpp b/geometry.cpp
index 4ed01ee..a8be3f4 100644
--- a/geometry.cpp
+++ b/geometry.cpp
@@ -37,3 +37,28 @@ void updateRectGeometry(QSGGeometry *geometry, float x, float y, float w, float
vertexData[2].set(x + w, y + h);
vertexData[3].set(x, y + h);
}
+
+void updateCrossHairGeometry(QSGGeometry *geometry,
+ float x,
+ float y,
+ float thickness,
+ float length)
+{
+ QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D();
+ if (geometry->vertexCount() != 12) {
+ return;
+ }
+
+ vertexData[ 0].set(x + thickness, y - thickness);
+ vertexData[ 1].set(x + length, y - thickness);
+ vertexData[ 2].set(x + length, y + thickness);
+ vertexData[ 3].set(x + thickness, y + thickness);
+ vertexData[ 4].set(x + thickness, y + length);
+ vertexData[ 5].set(x - thickness, y + length);
+ vertexData[ 6].set(x - thickness, y + thickness);
+ vertexData[ 7].set(x - length, y + thickness);
+ vertexData[ 8].set(x - length, y - thickness);
+ vertexData[ 9].set(x - thickness, y - thickness);
+ vertexData[10].set(x - thickness, y - length);
+ vertexData[11].set(x + thickness, y - length);
+}
diff --git a/geometry.h b/geometry.h
index b29e41f..eb4e05d 100644
--- a/geometry.h
+++ b/geometry.h
@@ -10,4 +10,10 @@ void updateCircleGeometry(QSGGeometry *geometry, float radius, float cx, float c
// Rect
void updateRectGeometry(QSGGeometry *geometry, float x, float y, float w, float h);
+// Crosshair
+void updateCrossHairGeometry(QSGGeometry *geometry,
+ float x,
+ float y,
+ float thickness,
+ float length);
#endif // GEOMETRY_H
diff --git a/main.cpp b/main.cpp
index 58d0f5a..a4f2bf0 100644
--- a/main.cpp
+++ b/main.cpp
@@ -13,12 +13,12 @@
#include "continuouscolorscale.h"
#include "scatterplot.h"
#include "voronoisplat.h"
-#include "historygraph.h"
#include "barchart.h"
#include "colormap.h"
#include "manipulationhandler.h"
#include "mapscalehandler.h"
#include "selectionhandler.h"
+#include "brushinghandler.h"
#include "projectionobserver.h"
static QObject *mainProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
@@ -111,13 +111,6 @@ int main(int argc, char **argv)
m->setCPIndices(cpIndices);
m->setCP(Ys);
- qmlRegisterType<Scatterplot>("PM", 1, 0, "Scatterplot");
- qmlRegisterType<HistoryGraph>("PM", 1, 0, "HistoryGraph");
- qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
- qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
- qmlRegisterType<Colormap>("PM", 1, 0, "Colormap");
- qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
-
// Set up multisampling
QSurfaceFormat fmt;
fmt.setRenderableType(QSurfaceFormat::OpenGL);
@@ -129,6 +122,12 @@ int main(int argc, char **argv)
fmt.setSamples(8);
QSurfaceFormat::setDefaultFormat(fmt);
+ qmlRegisterType<Scatterplot>("PM", 1, 0, "Scatterplot");
+ qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
+ qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
+ qmlRegisterType<Colormap>("PM", 1, 0, "Colormap");
+ qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
+
QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml"));
// Initialize pointers to visual components
@@ -157,13 +156,6 @@ int main(int argc, char **argv)
QObject::connect(&manipulationHandler, SIGNAL(rpChanged(const arma::mat &)),
m->splat, SLOT(setSites(const arma::mat &)));
- // Connections between history graph and cp plot
- //HistoryGraph *history = engine.rootObjects()[0]->findChild<HistoryGraph *>("history");
- //QObject::connect(cpPlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)),
- // history, SLOT(addHistoryItem(const arma::mat &)));
- //QObject::connect(history, SIGNAL(currentItemChanged(const arma::mat &)),
- // cpPlot, SLOT(setXY(const arma::mat &)));
-
// Keep both scatterplots and the splat scaled equally and relative to the
// full plot
MapScaleHandler mapScaleHandler;
@@ -196,6 +188,27 @@ int main(int argc, char **argv)
QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector<bool> &)),
m->rpPlot, SLOT(setSelection(const std::vector<bool> &)));
+ // Brushing between bar chart and respective scatterplot
+ BrushingHandler cpBrushHandler;
+ QObject::connect(m->cpPlot, SIGNAL(itemInteractivelyBrushed(int)),
+ &cpBrushHandler, SLOT(brushItem(int)));
+ QObject::connect(m->cpBarChart, SIGNAL(itemInteractivelyBrushed(int)),
+ &cpBrushHandler, SLOT(brushItem(int)));
+ QObject::connect(&cpBrushHandler, SIGNAL(itemBrushed(int)),
+ m->cpPlot, SLOT(brushItem(int)));
+ QObject::connect(&cpBrushHandler, SIGNAL(itemBrushed(int)),
+ m->cpBarChart, SLOT(brushItem(int)));
+
+ BrushingHandler rpBrushHandler;
+ QObject::connect(m->rpPlot, SIGNAL(itemInteractivelyBrushed(int)),
+ &rpBrushHandler, SLOT(brushItem(int)));
+ QObject::connect(m->rpBarChart, SIGNAL(itemInteractivelyBrushed(int)),
+ &rpBrushHandler, SLOT(brushItem(int)));
+ QObject::connect(&rpBrushHandler, SIGNAL(itemBrushed(int)),
+ m->rpPlot, SLOT(brushItem(int)));
+ QObject::connect(&rpBrushHandler, SIGNAL(itemBrushed(int)),
+ m->rpBarChart, SLOT(brushItem(int)));
+
// Recompute values whenever projection changes
ProjectionObserver projectionObserver(X, cpIndices);
m->projectionObserver = &projectionObserver;
@@ -210,6 +223,7 @@ int main(int argc, char **argv)
QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
m->rpBarChart, SLOT(setValues(const arma::vec &)));
+ // General component set up
m->cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
@@ -224,6 +238,7 @@ int main(int argc, char **argv)
m->cpPlot->setAutoScale(false);
m->rpPlot->setAutoScale(false);
m->cpPlot->setColorData(labels(cpIndices), false);
+ //m->cpPlot->brushItem(0);
manipulationHandler.setCP(Ys);
diff --git a/pm.pro b/pm.pro
index 5678c22..453f0cb 100644
--- a/pm.pro
+++ b/pm.pro
@@ -18,6 +18,7 @@ HEADERS += main.h \
mapscalehandler.h \
numericrange.h \
selectionhandler.h \
+ brushinghandler.h \
projectionobserver.h \
skelft.h \
skelftkernel.h \
@@ -34,6 +35,7 @@ SOURCES += main.cpp \
manipulationhandler.cpp \
mapscalehandler.cpp \
selectionhandler.cpp \
+ brushinghandler.cpp \
projectionobserver.cpp \
skelft_core.cpp \
lamp.cpp \
diff --git a/scatterplot.cpp b/scatterplot.cpp
index b547579..89edb2a 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -27,6 +27,7 @@ Scatterplot::Scatterplot(QQuickItem *parent)
, m_sx(0, 1, 0, 1)
, m_sy(0, 1, 0, 1)
, m_currentInteractionState(INTERACTION_NONE)
+ , m_brushedItem(-1)
, m_shouldUpdateGeometry(false)
, m_shouldUpdateMaterials(false)
{
@@ -182,6 +183,52 @@ void Scatterplot::setGlyphSize(float glyphSize)
setGlyphSize(glyphSize, true);
}
+QSGNode *Scatterplot::newSceneGraph()
+{
+ // NOTE:
+ // The hierarchy in the scene graph is as follows:
+ // root [[splatNode] [glyphsRoot [glyph [...]]] [selectionNode]]
+ QSGNode *root = new QSGNode;
+ QSGNode *glyphTreeRoot = newGlyphTree();
+ if (glyphTreeRoot) {
+ root->appendChildNode(glyphTreeRoot);
+ }
+
+ QSGSimpleRectNode *selectionRectNode = new QSGSimpleRectNode;
+ selectionRectNode->setColor(SELECTION_COLOR);
+ root->appendChildNode(selectionRectNode);
+
+ QSGTransformNode *brushNode = new QSGTransformNode;
+
+ QSGGeometryNode *whiteCrossHairNode = new QSGGeometryNode;
+ QSGGeometry *whiteCrossHairGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 12);
+ whiteCrossHairGeom->setDrawingMode(GL_POLYGON);
+ whiteCrossHairGeom->setVertexDataPattern(QSGGeometry::DynamicPattern);
+ updateCrossHairGeometry(whiteCrossHairGeom, 0, 0, 2, 8);
+ QSGFlatColorMaterial *whiteCrossHairMaterial = new QSGFlatColorMaterial;
+ whiteCrossHairMaterial->setColor(QColor(255, 255, 255));
+ whiteCrossHairNode->setGeometry(whiteCrossHairGeom);
+ whiteCrossHairNode->setMaterial(whiteCrossHairMaterial);
+ whiteCrossHairNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
+ brushNode->appendChildNode(whiteCrossHairNode);
+
+ QSGGeometryNode *blackCrossHairNode = new QSGGeometryNode;
+ QSGGeometry *blackCrossHairGeom = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 12);
+ blackCrossHairGeom->setDrawingMode(GL_POLYGON);
+ blackCrossHairGeom->setVertexDataPattern(QSGGeometry::DynamicPattern);
+ updateCrossHairGeometry(blackCrossHairGeom, 0, 0, 1, 8);
+ QSGFlatColorMaterial *blackCrossHairMaterial = new QSGFlatColorMaterial;
+ blackCrossHairMaterial->setColor(QColor(0, 0, 0));
+ blackCrossHairNode->setGeometry(blackCrossHairGeom);
+ blackCrossHairNode->setMaterial(blackCrossHairMaterial);
+ blackCrossHairNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
+ brushNode->appendChildNode(blackCrossHairNode);
+
+ root->appendChildNode(brushNode);
+
+ return root;
+}
+
QSGNode *Scatterplot::newGlyphTree()
{
// NOTE:
@@ -229,24 +276,6 @@ QSGNode *Scatterplot::newGlyphTree()
return node;
}
-QSGNode *Scatterplot::newSceneGraph()
-{
- // NOTE:
- // The hierarchy in the scene graph is as follows:
- // root [[splatNode] [glyphsRoot [glyph [...]]] [selectionNode]]
- QSGNode *root = new QSGNode;
- QSGNode *glyphTreeRoot = newGlyphTree();
- if (glyphTreeRoot) {
- root->appendChildNode(glyphTreeRoot);
- }
-
- QSGSimpleRectNode *selectionRectNode = new QSGSimpleRectNode;
- selectionRectNode->setColor(SELECTION_COLOR);
- root->appendChildNode(selectionRectNode);
-
- return root;
-}
-
QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
QSGNode *root = oldNode ? oldNode : newSceneGraph();
@@ -279,6 +308,10 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
}
node = node->nextSibling();
+ // Brushing
+ updateBrush(node);
+ node = node->nextSibling();
+
return root;
}
@@ -337,6 +370,20 @@ void Scatterplot::updateGlyphs(QSGNode *glyphsNode)
}
}
+void Scatterplot::updateBrush(QSGNode *node)
+{
+ QMatrix4x4 transform;
+ if (m_brushedItem < 0) {
+ transform.translate(-width(), -height());
+ } else {
+ const arma::rowvec &row = m_xy.row(m_brushedItem);
+ transform.translate(m_sx(row[0]), m_sy(row[1]));
+ }
+
+ QSGTransformNode *brushNode = static_cast<QSGTransformNode *>(node);
+ brushNode->setMatrix(transform);
+}
+
void Scatterplot::mousePressEvent(QMouseEvent *event)
{
switch (m_currentInteractionState) {
@@ -450,6 +497,12 @@ void Scatterplot::setSelection(const std::vector<bool> &selection)
update();
}
+void Scatterplot::brushItem(int item)
+{
+ m_brushedItem = item;
+ update();
+}
+
void Scatterplot::applyManipulation()
{
m_sx.inverse();
diff --git a/scatterplot.h b/scatterplot.h
index 0fb12ba..040f272 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -38,6 +38,8 @@ signals:
void opacityDataChanged(const arma::vec &opacityData) const;
void selectionChanged(const std::vector<bool> &selection) const;
void selectionInteractivelyChanged(const std::vector<bool> &selection) const;
+ void itemBrushed(int item) const;
+ void itemInteractivelyBrushed(int item) const;
void scaleChanged(const LinearScale<float> &sx, const LinearScale<float> &sy) const;
void glyphSizeChanged(float glyphSize) const;
@@ -46,6 +48,7 @@ public slots:
void setColorData(const arma::vec &colorData);
void setOpacityData(const arma::vec &opacityData);
void setSelection(const std::vector<bool> &selection);
+ void brushItem(int item);
void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy);
Q_INVOKABLE void setGlyphSize(float glyphSize);
@@ -61,6 +64,7 @@ private:
void applyManipulation();
void updateGlyphs(QSGNode *node);
+ void updateBrush(QSGNode *node);
// Data
arma::mat m_xy;
@@ -78,6 +82,7 @@ private:
// Internal state
bool interactiveSelection(bool mergeSelection);
std::vector<bool> m_selection;
+ int m_brushedItem;
enum InteractionState {
INTERACTION_NONE,