From b3c7ac156e1c4ac5d7455ce61e89549291ac85b1 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Wed, 20 May 2015 18:53:07 -0300 Subject: Nearly complete implementation of interaction. Interaction still does not work due to signals not being correctly emitted. --- scatterplot.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 21 deletions(-) (limited to 'scatterplot.cpp') 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 - #include -#include #include #include #include #include #include -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(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)); +} -- cgit v1.2.3