aboutsummaryrefslogtreecommitdiff
path: root/scatterplot.cpp
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2015-05-20 18:53:07 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2015-05-21 18:09:16 -0300
commitb3c7ac156e1c4ac5d7455ce61e89549291ac85b1 (patch)
tree024ee0050cf68534b2e977d48c266303a2c9bdc8 /scatterplot.cpp
parent02e2ebf10c30ca278dc8a85649c6a7db87858cde (diff)
Nearly complete implementation of interaction.
Interaction still does not work due to signals not being correctly emitted.
Diffstat (limited to 'scatterplot.cpp')
-rw-r--r--scatterplot.cpp111
1 files changed, 90 insertions, 21 deletions
diff --git a/scatterplot.cpp b/scatterplot.cpp
index 414f46a..dd8d378 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -1,16 +1,13 @@
#include "scatterplot.h"
-#include <iostream>
-
#include <cmath>
-#include <QSGNode>
#include <QSGGeometry>
#include <QSGGeometryNode>
#include <QSGMaterial>
#include <QSGFlatColorMaterial>
#include <QSGSimpleRectNode>
-const int GLYPH_SIZE = 5;
+const int GLYPH_SIZE = 8;
const float PADDING = 10;
const float PI = 3.1415f;
@@ -31,7 +28,6 @@ Scatterplot::Scatterplot(QQuickItem *parent)
{
setClip(true);
setFlag(QQuickItem::ItemHasContents);
- setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
}
Scatterplot::~Scatterplot()
@@ -44,7 +40,12 @@ void Scatterplot::setData(const arma::mat &data)
return;
m_data = data;
+
m_colorScale.setExtents(m_data.col(2).min(), m_data.col(2).max());
+ m_selectedGlyphs.clear();
+ for (arma::uword i = 0; i < m_data.n_rows; i++)
+ m_selectedGlyphs.append(false);
+
update();
}
@@ -101,7 +102,7 @@ QSGNode *Scatterplot::newGlyphNodeTree() {
QSGGeometryNode *glyphNode = new QSGGeometryNode;
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount);
- geometry->setDrawingMode(GL_POLYGON);
+ geometry->setDrawingMode(GL_LINE_LOOP);
glyphNode->setGeometry(geometry);
glyphNode->setFlag(QSGNode::OwnsGeometry);
@@ -125,7 +126,7 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
xmax = m_data.col(0).max(),
ymin = m_data.col(1).min(),
ymax = m_data.col(1).max(),
- x, y;
+ x, y, xt, yt, selected;
QSGNode *root = 0;
if (!oldNode) {
@@ -136,12 +137,20 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
}
QSGNode *glyphNode = root->firstChild()->firstChild();
+ if (m_currentState != INTERACTION_MOVING)
+ xt = yt = 0;
+ else {
+ xt = m_dragCurrentPos.x() - m_dragOriginPos.x();
+ yt = m_dragCurrentPos.y() - m_dragOriginPos.y();
+ }
for (arma::uword i = 0; i < m_data.n_rows; i++) {
arma::rowvec row = m_data.row(i);
- x = PADDING + (row[0] - xmin) / (xmax - xmin) * (width() - 2*PADDING);
- y = PADDING + (row[1] - ymin) / (ymax - ymin) * (height() - 2*PADDING);
+ selected = m_selectedGlyphs[i] ? 1.0 : 0.0;
+ x = PADDING + (row[0] - xmin) / (xmax - xmin) * (width() - 2*PADDING) + xt * selected;
+ y = PADDING + (row[1] - ymin) / (ymax - ymin) * (height() - 2*PADDING) + yt * selected;
QSGGeometry *geometry = static_cast<QSGGeometryNode *>(glyphNode)->geometry();
+ geometry->setDrawingMode(m_selectedGlyphs[i] ? GL_POLYGON : GL_LINE_LOOP);
updateCircleGeometry(geometry, GLYPH_SIZE, x, y);
glyphNode->markDirty(QSGNode::DirtyGeometry);
glyphNode = glyphNode->nextSibling();
@@ -153,12 +162,12 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
if (!root->firstChild()->nextSibling()) {
selectionNode = new QSGGeometryNode;
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
- geometry->setDrawingMode(GL_POLYGON);
+ geometry->setDrawingMode(GL_LINE_LOOP);
selectionNode->setGeometry(geometry);
selectionNode->setFlag(QSGNode::OwnsGeometry);
QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
- material->setColor(QColor(128, 128, 128, 128));
+ material->setColor(QColor(0, 0, 0, 128));
selectionNode->setMaterial(material);
selectionNode->setFlag(QSGNode::OwnsMaterial);
@@ -169,7 +178,7 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
updateSelectionGeometry(selectionNode->geometry(), m_dragOriginPos, m_dragCurrentPos);
selectionNode->markDirty(QSGNode::DirtyGeometry);
- } else if (m_currentState == INTERACTION_NONE) {
+ } else {
if (root->firstChild()->nextSibling()) {
root->firstChild()->nextSibling()->markDirty(QSGNode::DirtyGeometry);
root->removeChildNode(root->firstChild()->nextSibling());
@@ -183,12 +192,12 @@ void Scatterplot::mousePressEvent(QMouseEvent *event)
{
switch (m_currentState) {
case INTERACTION_NONE:
- m_currentState = INTERACTION_SELECTING;
+ case INTERACTION_SELECTED:
+ m_currentState = (event->button() == Qt::MiddleButton) ? INTERACTION_MOVING
+ : INTERACTION_SELECTING;
m_dragOriginPos = event->localPos();
+ m_dragCurrentPos = m_dragOriginPos;
break;
- case INTERACTION_SELECTED:
- m_currentState = INTERACTION_MOVING;
- break; // TODO
case INTERACTION_SELECTING:
case INTERACTION_MOVING:
return; // should not be reached
@@ -197,25 +206,85 @@ void Scatterplot::mousePressEvent(QMouseEvent *event)
void Scatterplot::mouseMoveEvent(QMouseEvent *event)
{
- if (m_currentState != INTERACTION_SELECTING)
+ switch (m_currentState) {
+ case INTERACTION_NONE:
+ case INTERACTION_SELECTED:
return;
-
- m_dragCurrentPos = event->localPos();
- update();
+ case INTERACTION_SELECTING:
+ case INTERACTION_MOVING:
+ m_dragCurrentPos = event->localPos();
+ update();
+ }
}
void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
{
+ bool mergeSelection;
+
switch (m_currentState) {
case INTERACTION_SELECTING:
- m_currentState = INTERACTION_NONE;
+ mergeSelection = (event->button() == Qt::RightButton);
+ m_currentState = selectGlyphs(mergeSelection) ? INTERACTION_SELECTED
+ : INTERACTION_NONE;
update();
break;
case INTERACTION_MOVING:
+ m_currentState = INTERACTION_SELECTED;
+ updateData();
+ update();
break;
case INTERACTION_NONE:
case INTERACTION_SELECTED:
return; // should not be reached
}
}
+
+bool Scatterplot::selectGlyphs(bool mergeSelection)
+{
+ qreal xmin = m_data.col(0).min(),
+ xmax = m_data.col(0).max(),
+ ymin = m_data.col(1).min(),
+ ymax = m_data.col(1).max(),
+ x, y;
+
+ QRectF selectionRect(m_dragOriginPos, m_dragCurrentPos);
+ bool anySelected = false;
+ for (arma::uword i = 0; i < m_data.n_rows; i++) {
+ arma::rowvec row = m_data.row(i);
+ x = PADDING + (row[0] - xmin) / (xmax - xmin) * (width() - 2*PADDING);
+ y = PADDING + (row[1] - ymin) / (ymax - ymin) * (height() - 2*PADDING);
+
+ bool contains = selectionRect.contains(x, y);
+ anySelected = anySelected || contains;
+ m_selectedGlyphs[i] = (mergeSelection && m_selectedGlyphs[i]) || contains;
+ }
+
+ return anySelected;
+}
+
+void Scatterplot::updateData()
+{
+ qreal xmin = m_data.col(0).min(),
+ xmax = m_data.col(0).max(),
+ ymin = m_data.col(1).min(),
+ ymax = m_data.col(1).max();
+
+ float xt = m_dragCurrentPos.x() - m_dragOriginPos.x();
+ float yt = m_dragCurrentPos.y() - m_dragOriginPos.y();
+
+ xt /= (width() - PADDING);
+ yt /= (height() - PADDING);
+ for (arma::uword i = 0; i < m_data.n_rows; i++) {
+ if (!m_selectedGlyphs[i])
+ continue;
+
+ arma::rowvec row = m_data.row(i);
+ row[0] = ((row[0] - xmin) / (xmax - xmin) + xt) * (xmax - xmin) + xmin;
+ row[1] = ((row[1] - ymin) / (ymax - ymin) + yt) * (ymax - ymin) + ymin;
+ m_data.row(i) = row;
+ }
+
+ // does not send last column (labels)
+ emit dataChanged(m_data.cols(0, m_data.n_cols - 2));
+}