aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2015-09-17 12:20:24 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2015-09-17 12:20:24 -0300
commit0518b806d8ce25c69847ec8c403276193611b2e1 (patch)
treef008385354f24ab2fadca171d27d8f2656244d89
parent8b1e2720b1253a91e3b75a08e2f5a38deb55affa (diff)
Additional interactive functionalities.
- Selection linking between subsample plot and main plot - Dumb "effectiveness" coloring
-rw-r--r--effectivenessobserver.cpp42
-rw-r--r--effectivenessobserver.h26
-rw-r--r--main.cpp46
-rw-r--r--pm.pro4
-rw-r--r--scatterplot.cpp71
-rw-r--r--scatterplot.h4
-rw-r--r--selectionhandler.cpp19
-rw-r--r--selectionhandler.h23
8 files changed, 199 insertions, 36 deletions
diff --git a/effectivenessobserver.cpp b/effectivenessobserver.cpp
new file mode 100644
index 0000000..305b3f8
--- /dev/null
+++ b/effectivenessobserver.cpp
@@ -0,0 +1,42 @@
+#include "effectivenessobserver.h"
+
+EffectiveInteractionEnforcer::EffectiveInteractionEnforcer(const arma::uvec &sampleIndices)
+ : m_sampleIndices(sampleIndices)
+ , m_effectiveness(arma::zeros<arma::vec>(sampleIndices.n_elem))
+{
+}
+
+void EffectiveInteractionEnforcer::setSelection(const arma::uvec &selection)
+{
+ m_selection = selection;
+}
+
+void EffectiveInteractionEnforcer::setMeasureDifference(const arma::vec &measure)
+{
+ m_measure = measure;
+
+ if (m_selection.n_elem == 0) {
+ return;
+ }
+
+ arma::uvec selectionIndices(m_selection);
+ for (auto it = selectionIndices.begin(); it != selectionIndices.end(); it++) {
+ *it = m_sampleIndices[*it];
+ }
+
+ double diff = arma::mean(m_measure(selectionIndices));
+ int effectiveness;
+ if (diff > 0) {
+ effectiveness = 1;
+ } else if (diff < 0) {
+ effectiveness = -1;
+ } else {
+ effectiveness = 0;
+ }
+
+ for (auto it = m_selection.cbegin(); it != m_selection.cend(); it++) {
+ m_effectiveness[*it] = effectiveness;
+ }
+
+ emit effectivenessChanged(m_effectiveness);
+}
diff --git a/effectivenessobserver.h b/effectivenessobserver.h
new file mode 100644
index 0000000..8331916
--- /dev/null
+++ b/effectivenessobserver.h
@@ -0,0 +1,26 @@
+#ifndef EFFECTIVEINTERACTIONENFORCER_H
+#define EFFECTIVEINTERACTIONENFORCER_H
+
+#include <QObject>
+#include <armadillo>
+
+class EffectiveInteractionEnforcer : public QObject
+{
+ Q_OBJECT
+public:
+ EffectiveInteractionEnforcer(const arma::uvec &sampleIndices);
+
+signals:
+ void effectivenessChanged(const arma::vec &effectiveness);
+
+public slots:
+ void setSelection(const arma::uvec &selection);
+ void setMeasureDifference(const arma::vec &measure);
+
+private:
+ arma::mat m_effectiveness;
+ arma::uvec m_sampleIndices, m_selection;
+ arma::vec m_measure;
+};
+
+#endif // EFFECTIVEINTERACTIONENFORCER_H
diff --git a/main.cpp b/main.cpp
index 8bd0318..58578ac 100644
--- a/main.cpp
+++ b/main.cpp
@@ -9,6 +9,8 @@
#include "continuouscolorscale.h"
#include "scatterplot.h"
#include "interactionhandler.h"
+#include "selectionhandler.h"
+#include "effectivenessobserver.h"
#include "distortionobserver.h"
#include "npdistortion.h"
@@ -40,6 +42,7 @@ int main(int argc, char **argv)
arma::mat Ys(subsampleSize, 2, arma::fill::randn);
Ys = mp::forceScheme(mp::dist(X.rows(sampleIndices)), Ys);
+ /*
ColorScale colorScale{
QColor("#1f77b4"),
QColor("#ff7f0e"),
@@ -52,10 +55,14 @@ int main(int argc, char **argv)
QColor("#7f7f7f"),
};
colorScale.setExtents(labels.min(), labels.max());
+ */
+
+ ContinuousColorScale colorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
+ colorScale.setExtents(-1, 1);
Scatterplot *subsamplePlot = engine.rootObjects()[0]->findChild<Scatterplot *>("subsamplePlot");
subsamplePlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
subsamplePlot->setXY(Ys);
- subsamplePlot->setColorData(labels(sampleIndices));
+ subsamplePlot->setColorData(arma::zeros<arma::vec>(subsampleSize));
subsamplePlot->setColorScale(&colorScale);
Scatterplot *plot = engine.rootObjects()[0]->findChild<Scatterplot *>("plot");
@@ -66,21 +73,32 @@ int main(int argc, char **argv)
QObject::connect(&interactionHandler, SIGNAL(subsampleChanged(const arma::mat &)),
plot, SLOT(setXY(const arma::mat &)));
- //DistortionObserver distortionObs(X, sampleIndices);
- //std::unique_ptr<DistortionMeasure> distortionMeasure(new NPDistortion());
- //distortionObs.setMeasure(distortionMeasure.get());
- //QObject::connect(&interactionHandler, SIGNAL(subsampleChanged(const arma::mat &)),
- // &distortionObs, SLOT(setMap(const arma::mat &)));
- //QObject::connect(&distortionObs, SIGNAL(mapChanged(const arma::vec &)),
- // plot, SLOT(setColorData(const arma::vec &)));
+ SelectionHandler selectionHandler(sampleIndices);
+ QObject::connect(subsamplePlot, SIGNAL(selectionChanged(const arma::uvec &)),
+ &selectionHandler, SLOT(setSelection(const arma::uvec &)));
+ QObject::connect(&selectionHandler, SIGNAL(selectionChanged(const arma::uvec &)),
+ plot, SLOT(setSelection(const arma::uvec &)));
- //ContinuousColorScale ccolorScale = ContinuousColorScale::builtin(ContinuousColorScale::HEATED_OBJECTS);
- //ccolorScale.setExtents(-1, 1);
- plot->setColorScale(&colorScale);
- interactionHandler.setSubsample(Ys);
+ DistortionObserver distortionObs(X, sampleIndices);
+ std::unique_ptr<DistortionMeasure> distortionMeasure(new NPDistortion());
+ distortionObs.setMeasure(distortionMeasure.get());
+ QObject::connect(&interactionHandler, SIGNAL(subsampleChanged(const arma::mat &)),
+ &distortionObs, SLOT(setMap(const arma::mat &)));
+ QObject::connect(&distortionObs, SIGNAL(mapChanged(const arma::vec &)),
+ plot, SLOT(setColorData(const arma::vec &)));
- // TODO: remove when proper measure coloring is done
- plot->setColorData(labels);
+ EffectiveInteractionEnforcer enforcer(sampleIndices);
+ QObject::connect(subsamplePlot, SIGNAL(selectionChanged(const arma::uvec &)),
+ &enforcer, SLOT(setSelection(const arma::uvec &)));
+ QObject::connect(plot, SIGNAL(colorDataChanged(const arma::vec &)),
+ &enforcer, SLOT(setMeasureDifference(const arma::vec &)));
+ QObject::connect(&enforcer, SIGNAL(effectivenessChanged(const arma::vec &)),
+ subsamplePlot, SLOT(setColorData(const arma::vec &)));
+
+ ContinuousColorScale ccolorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
+ ccolorScale.setExtents(-1, 1);
+ plot->setColorScale(&ccolorScale);
+ interactionHandler.setSubsample(Ys);
return app.exec();
}
diff --git a/pm.pro b/pm.pro
index de669cd..48c4d35 100644
--- a/pm.pro
+++ b/pm.pro
@@ -6,6 +6,8 @@ HEADERS += colorscale.h \
continuouscolorscale.h \
scatterplot.h \
interactionhandler.h \
+ selectionhandler.h \
+ effectivenessobserver.h \
distortionobserver.h \
distortionmeasure.h \
npdistortion.h \
@@ -15,6 +17,8 @@ SOURCES += main.cpp \
continuouscolorscale.cpp \
scatterplot.cpp \
interactionhandler.cpp \
+ selectionhandler.cpp \
+ effectivenessobserver.cpp \
distortionobserver.cpp \
npdistortion.cpp \
lamp.cpp \
diff --git a/scatterplot.cpp b/scatterplot.cpp
index 36f6a38..9bbe09e 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -41,15 +41,19 @@ void Scatterplot::setXY(const arma::mat &xy)
return;
}
+ if (m_xy.n_elem != xy.n_elem) {
+ m_selectedGlyphs.clear();
+ }
+
m_xy = xy;
m_xmin = xy.col(0).min();
m_xmax = xy.col(0).max();
m_ymin = xy.col(1).min();
m_ymax = xy.col(1).max();
- m_selectedGlyphs.clear();
- emit xyChanged(m_xy);
updateGeometry();
+
+ emit xyChanged(m_xy);
}
void Scatterplot::setColorData(const arma::vec &colorData)
@@ -120,12 +124,12 @@ QSGNode *Scatterplot::createGlyphNodeTree()
m_glyphGeometryPtr.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount));
QSGGeometry *glyphGeometry = m_glyphGeometryPtr.get();
glyphGeometry->setDrawingMode(GL_POLYGON);
- updateCircleGeometry(glyphGeometry, GLYPH_SIZE, 0, 0);
+ updateCircleGeometry(glyphGeometry, GLYPH_SIZE - 1, 0, 0);
m_glyphOutlineGeometryPtr.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount));
QSGGeometry *glyphOutlineGeometry = m_glyphOutlineGeometryPtr.get();
glyphOutlineGeometry->setDrawingMode(GL_LINE_LOOP);
- updateCircleGeometry(glyphOutlineGeometry, GLYPH_SIZE + 1, 0, 0);
+ updateCircleGeometry(glyphOutlineGeometry, GLYPH_SIZE, 0, 0);
for (arma::uword i = 0; i < m_xy.n_rows; i++) {
QSGGeometryNode *glyphOutlineNode = new QSGGeometryNode;
@@ -214,7 +218,7 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
m_shouldUpdateMaterials = false;
}
- // Draw selection
+ // Draw selection rect
if (m_currentInteractionState == INTERACTION_SELECTING) {
QSGSimpleRectNode *selectionNode = 0;
if (!root->firstChild()->nextSibling()) {
@@ -230,8 +234,9 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
} else {
node = root->firstChild()->nextSibling();
if (node) {
- node->markDirty(QSGNode::DirtyGeometry);
+ // node->markDirty(QSGNode::DirtyGeometry);
root->removeChildNode(node);
+ delete node;
}
}
@@ -261,6 +266,9 @@ void Scatterplot::mousePressEvent(QMouseEvent *event)
void Scatterplot::mouseMoveEvent(QMouseEvent *event)
{
switch (m_currentInteractionState) {
+ case INTERACTION_NONE:
+ // event->localPos()
+ break;
case INTERACTION_SELECTING:
m_dragCurrentPos = event->localPos();
update();
@@ -271,7 +279,6 @@ void Scatterplot::mouseMoveEvent(QMouseEvent *event)
updateGeometry();
m_dragOriginPos = m_dragCurrentPos;
break;
- case INTERACTION_NONE:
case INTERACTION_SELECTED:
event->ignore();
return;
@@ -281,16 +288,18 @@ void Scatterplot::mouseMoveEvent(QMouseEvent *event)
void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
{
bool mergeSelection;
+ arma::uvec selection;
switch (m_currentInteractionState) {
case INTERACTION_SELECTING:
mergeSelection = (event->modifiers() == Qt::ControlModifier);
- if (selectGlyphs(mergeSelection)) {
+ selection = findSelection(mergeSelection);
+ if (selection.n_elem > 0) {
+ setSelection(selection);
m_currentInteractionState = INTERACTION_SELECTED;
} else {
m_currentInteractionState = INTERACTION_NONE;
}
- update();
break;
case INTERACTION_MOVING:
@@ -304,27 +313,47 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
}
}
-bool Scatterplot::selectGlyphs(bool mergeSelection)
+arma::uvec Scatterplot::findSelection(bool mergeSelection)
{
- if (!mergeSelection)
- m_selectedGlyphs.clear();
+ QSet<int> selectedGlyphs(m_selectedGlyphs);
+ if (!mergeSelection) {
+ selectedGlyphs.clear();
+ }
- qreal x, y;
+ QPointF dragOrigin(m_dragOriginPos.x() / width() * (m_xmax - m_xmin) + m_xmin,
+ m_dragOriginPos.y() / height() * (m_ymax - m_ymin) + m_ymin);
+ QPointF dragCurrent(m_dragCurrentPos.x() / width() * (m_xmax - m_xmin) + m_xmin,
+ m_dragCurrentPos.y() / height() * (m_ymax - m_ymin) + m_ymin);
+ QRectF selectionRect(dragOrigin, dragCurrent);
- QRectF selectionRect(m_dragOriginPos, m_dragCurrentPos);
- bool anySelected = false;
for (arma::uword i = 0; i < m_xy.n_rows; i++) {
arma::rowvec row = m_xy.row(i);
- x = fromDataXToScreenX(row[0]);
- y = fromDataYToScreenY(row[1]);
- if (selectionRect.contains(x, y)) {
- m_selectedGlyphs.insert(i);
- anySelected = true;
+ if (selectionRect.contains(row[0], row[1])) {
+ selectedGlyphs.insert(i);
}
}
- return anySelected;
+ arma::uvec selection(selectedGlyphs.size());
+ int i = 0;
+ for (auto it = selectedGlyphs.cbegin(); it != selectedGlyphs.cend(); it++, i++) {
+ selection[i] = *it;
+ }
+
+ return selection;
+}
+
+void Scatterplot::setSelection(const arma::uvec &selection)
+{
+ m_selectedGlyphs.clear();
+
+ for (auto it = selection.cbegin(); it != selection.cend(); it++) {
+ m_selectedGlyphs.insert(*it);
+ }
+
+ update();
+
+ emit selectionChanged(selection);
}
void Scatterplot::applyManipulation()
diff --git a/scatterplot.h b/scatterplot.h
index 4275561..2d91a63 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -18,10 +18,12 @@ public:
signals:
void xyChanged(const arma::mat &XY);
void colorDataChanged(const arma::vec &colorData);
+ void selectionChanged(const arma::uvec &selection);
public slots:
void setXY(const arma::mat &xy);
void setColorData(const arma::vec &colorData);
+ void setSelection(const arma::uvec &selection);
protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
@@ -31,7 +33,7 @@ protected:
private:
QSGNode *createGlyphNodeTree();
- bool selectGlyphs(bool mergeSelection);
+ arma::uvec findSelection(bool mergeSelection);
float fromDataXToScreenX(float x);
float fromDataYToScreenY(float y);
diff --git a/selectionhandler.cpp b/selectionhandler.cpp
new file mode 100644
index 0000000..4c488a0
--- /dev/null
+++ b/selectionhandler.cpp
@@ -0,0 +1,19 @@
+#include "selectionhandler.h"
+
+SelectionHandler::SelectionHandler(const arma::uvec &sampleIndices)
+ : m_sampleIndices(sampleIndices)
+{
+}
+
+void SelectionHandler::setSelection(const arma::uvec &selection)
+{
+ arma::uvec newSelection(selection);
+
+ // The selecion happens over the sample indices. We use the original dataset
+ // indices in sampleIndices to translate indices.
+ for (auto it = newSelection.begin(); it != newSelection.end(); it++) {
+ *it = m_sampleIndices[*it];
+ }
+
+ emit selectionChanged(newSelection);
+}
diff --git a/selectionhandler.h b/selectionhandler.h
new file mode 100644
index 0000000..4c4a2ca
--- /dev/null
+++ b/selectionhandler.h
@@ -0,0 +1,23 @@
+#ifndef SELECTIONHANDLER_H
+#define SELECTIONHANDLER_H
+
+#include <QObject>
+#include <armadillo>
+
+class SelectionHandler : public QObject
+{
+ Q_OBJECT
+public:
+ SelectionHandler(const arma::uvec &sampleIndices);
+
+signals:
+ void selectionChanged(const arma::uvec &selection);
+
+public slots:
+ void setSelection(const arma::uvec &selection);
+
+private:
+ arma::uvec m_sampleIndices;
+};
+
+#endif // SELECTIONHANDLER_H