aboutsummaryrefslogtreecommitdiff
path: root/scatterplot.cpp
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2015-05-19 18:54:20 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2015-05-21 18:09:16 -0300
commit02e2ebf10c30ca278dc8a85649c6a7db87858cde (patch)
treeb46d14818c3e4fba0de968f7fde55eddc7d743a1 /scatterplot.cpp
parenta96f9f1a2688c215c478cfbee5748b4bb2043a43 (diff)
Initial selection implementation.
Diffstat (limited to 'scatterplot.cpp')
-rw-r--r--scatterplot.cpp150
1 files changed, 125 insertions, 25 deletions
diff --git a/scatterplot.cpp b/scatterplot.cpp
index a3a0dfc..414f46a 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -16,9 +16,22 @@ const float PI = 3.1415f;
Scatterplot::Scatterplot(QQuickItem *parent)
: QQuickItem(parent)
- , m_colorScale{QColor("red"), QColor("green"), QColor("blue")}
+ , m_dragOriginPos(-1.0, -1.0)
+ , m_colorScale{
+ QColor("#1f77b4"),
+ QColor("#ff7f0e"),
+ QColor("#2ca02c"),
+ QColor("#d62728"),
+ QColor("#9467bd"),
+ QColor("#8c564b"),
+ QColor("#e377c2"),
+ QColor("#7f7f7f"),
+ QColor("#bcbd22"),
+ }
{
+ setClip(true);
setFlag(QQuickItem::ItemHasContents);
+ setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
}
Scatterplot::~Scatterplot()
@@ -71,51 +84,138 @@ void updateSquareGeometry(QSGGeometry *geometry, float size, float cx, float cy)
vertexData[3].set(cx - r, cy + r);
}
+void updateSelectionGeometry(QSGGeometry *geometry, const QPointF &p1, const QPointF &p2)
+{
+ QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D();
+ vertexData[0].set(p1.x(), p1.y());
+ vertexData[1].set(p2.x(), p1.y());
+ vertexData[2].set(p2.x(), p2.y());
+ vertexData[3].set(p1.x(), p2.y());
+}
+
+QSGNode *Scatterplot::newGlyphNodeTree() {
+ QSGNode *node = new QSGNode;
+ int vertexCount = calculateCircleVertexCount(GLYPH_SIZE / 2);
+
+ for (arma::uword i = 0; i < m_data.n_rows; i++) {
+ QSGGeometryNode *glyphNode = new QSGGeometryNode;
+
+ QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount);
+ geometry->setDrawingMode(GL_POLYGON);
+ glyphNode->setGeometry(geometry);
+ glyphNode->setFlag(QSGNode::OwnsGeometry);
+
+ QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
+ material->setColor(m_colorScale.color(m_data(i, 2)));
+ glyphNode->setMaterial(material);
+ glyphNode->setFlag(QSGNode::OwnsMaterial);
+
+ node->appendChildNode(glyphNode);
+ }
+
+ return node;
+}
+
QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
if (m_data.n_rows < 1)
return 0;
- QSGNode *node = 0;
- int vertexCount = calculateCircleVertexCount(GLYPH_SIZE / 2);
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;
+ QSGNode *root = 0;
if (!oldNode) {
- node = new QSGNode;
- for (arma::uword i = 0; i < m_data.n_rows; i++) {
- QSGGeometryNode *childNode = new QSGGeometryNode;
-
- QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount);
- geometry->setDrawingMode(GL_POLYGON);
- childNode->setGeometry(geometry);
- childNode->setFlag(QSGNode::OwnsGeometry);
-
- QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
- material->setColor(m_colorScale.color(m_data(i, 2)));
- childNode->setMaterial(material);
- childNode->setFlag(QSGNode::OwnsMaterial);
-
- node->appendChildNode(childNode);
- }
+ root = new QSGNode;
+ root->appendChildNode(newGlyphNodeTree());
} else {
- node = oldNode;
+ root = oldNode;
}
- QSGNode *childNode = node->firstChild();
+ QSGNode *glyphNode = root->firstChild()->firstChild();
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);
- QSGGeometry *geometry = static_cast<QSGGeometryNode *>(childNode)->geometry();
+ QSGGeometry *geometry = static_cast<QSGGeometryNode *>(glyphNode)->geometry();
updateCircleGeometry(geometry, GLYPH_SIZE, x, y);
- childNode->markDirty(QSGNode::DirtyGeometry);
- childNode = childNode->nextSibling();
+ glyphNode->markDirty(QSGNode::DirtyGeometry);
+ glyphNode = glyphNode->nextSibling();
}
- return node;
+ // Draw selection
+ if (m_currentState == INTERACTION_SELECTING) {
+ QSGGeometryNode *selectionNode = 0;
+ if (!root->firstChild()->nextSibling()) {
+ selectionNode = new QSGGeometryNode;
+ QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+ geometry->setDrawingMode(GL_POLYGON);
+ selectionNode->setGeometry(geometry);
+ selectionNode->setFlag(QSGNode::OwnsGeometry);
+
+ QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
+ material->setColor(QColor(128, 128, 128, 128));
+ selectionNode->setMaterial(material);
+ selectionNode->setFlag(QSGNode::OwnsMaterial);
+
+ root->appendChildNode(selectionNode);
+ } else {
+ selectionNode = static_cast<QSGGeometryNode *>(root->firstChild()->nextSibling());
+ }
+
+ updateSelectionGeometry(selectionNode->geometry(), m_dragOriginPos, m_dragCurrentPos);
+ selectionNode->markDirty(QSGNode::DirtyGeometry);
+ } else if (m_currentState == INTERACTION_NONE) {
+ if (root->firstChild()->nextSibling()) {
+ root->firstChild()->nextSibling()->markDirty(QSGNode::DirtyGeometry);
+ root->removeChildNode(root->firstChild()->nextSibling());
+ }
+ }
+
+ return root;
+}
+
+void Scatterplot::mousePressEvent(QMouseEvent *event)
+{
+ switch (m_currentState) {
+ case INTERACTION_NONE:
+ m_currentState = INTERACTION_SELECTING;
+ m_dragOriginPos = event->localPos();
+ break;
+ case INTERACTION_SELECTED:
+ m_currentState = INTERACTION_MOVING;
+ break; // TODO
+ case INTERACTION_SELECTING:
+ case INTERACTION_MOVING:
+ return; // should not be reached
+ }
+}
+
+void Scatterplot::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_currentState != INTERACTION_SELECTING)
+ return;
+
+ m_dragCurrentPos = event->localPos();
+ update();
+}
+
+void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
+{
+ switch (m_currentState) {
+ case INTERACTION_SELECTING:
+ m_currentState = INTERACTION_NONE;
+ update();
+ break;
+
+ case INTERACTION_MOVING:
+ break;
+ case INTERACTION_NONE:
+ case INTERACTION_SELECTED:
+ return; // should not be reached
+ }
}