From 41e1b2bfb8e2ba3d0e74180200e7cc109171213e Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Tue, 26 Jan 2016 18:15:50 +0100 Subject: 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 --- barchart.cpp | 95 ++++++++++++++++++++++++++++++++--------------------- barchart.h | 9 +++-- brushinghandler.cpp | 17 ++++++++++ brushinghandler.h | 25 ++++++++++++++ geometry.cpp | 25 ++++++++++++++ geometry.h | 6 ++++ main.cpp | 45 ++++++++++++++++--------- pm.pro | 2 ++ scatterplot.cpp | 89 +++++++++++++++++++++++++++++++++++++++---------- scatterplot.h | 5 +++ 10 files changed, 245 insertions(+), 73 deletions(-) create mode 100644 brushinghandler.cpp create mode 100644 brushinghandler.h 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 &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(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(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 &selection) const; void selectionInteractivelyChanged(const std::vector &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 &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 m_originalIndices; + std::vector m_originalIndices, m_currentIndices; LinearScale 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 + +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("PM", 1, 0, "Scatterplot"); - qmlRegisterType("PM", 1, 0, "HistoryGraph"); - qmlRegisterType("PM", 1, 0, "BarChart"); - qmlRegisterType("PM", 1, 0, "VoronoiSplat"); - qmlRegisterType("PM", 1, 0, "Colormap"); - qmlRegisterSingletonType
("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("PM", 1, 0, "Scatterplot"); + qmlRegisterType("PM", 1, 0, "BarChart"); + qmlRegisterType("PM", 1, 0, "VoronoiSplat"); + qmlRegisterType("PM", 1, 0, "Colormap"); + qmlRegisterSingletonType
("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("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 &)), m->rpPlot, SLOT(setSelection(const std::vector &))); + // 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(node); + brushNode->setMatrix(transform); +} + void Scatterplot::mousePressEvent(QMouseEvent *event) { switch (m_currentInteractionState) { @@ -450,6 +497,12 @@ void Scatterplot::setSelection(const std::vector &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 &selection) const; void selectionInteractivelyChanged(const std::vector &selection) const; + void itemBrushed(int item) const; + void itemInteractivelyBrushed(int item) const; void scaleChanged(const LinearScale &sx, const LinearScale &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 &selection); + void brushItem(int item); void setScale(const LinearScale &sx, const LinearScale &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 m_selection; + int m_brushedItem; enum InteractionState { INTERACTION_NONE, -- cgit v1.2.3