aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--barchart.cpp68
-rw-r--r--barchart.h11
-rw-r--r--main.cpp40
-rw-r--r--pm.pro2
-rw-r--r--projectionobserver.cpp22
-rw-r--r--projectionobserver.h9
-rw-r--r--scatterplot.cpp79
-rw-r--r--scatterplot.h12
-rw-r--r--selectionhandler.cpp32
-rw-r--r--selectionhandler.h18
10 files changed, 187 insertions, 106 deletions
diff --git a/barchart.cpp b/barchart.cpp
index eb75dc8..8bc3ee9 100644
--- a/barchart.cpp
+++ b/barchart.cpp
@@ -48,11 +48,18 @@ 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);
+ }
+
if (m_values.n_elem > 0) {
m_scale.setDomain(m_values.min(), m_values.max());
m_colorScale.setExtents(m_values.min(), m_values.max());
- for (int i = 0; i < m_originalIndices.size(); i++) {
+ for (std::vector<int>::size_type i = 0;
+ i < m_originalIndices.size(); i++) {
m_originalIndices[i] = i;
}
@@ -77,6 +84,19 @@ void BarChart::setColorScale(const ColorScale &scale)
update();
}
+void BarChart::setSelection(const std::vector<bool> &selection)
+{
+ if (m_selection.size() != selection.size()) {
+ return;
+ }
+
+ m_selection = selection;
+ emit selectionChanged(m_selection);
+
+ m_shouldUpdateSelectionRect = true;
+ update();
+}
+
QSGNode *BarChart::newSceneGraph() const
{
// NOTE: scene graph structure is as follows:
@@ -256,29 +276,37 @@ QSGNode *BarChart::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
return root;
}
-void BarChart::selectBarsInRange(float start, float end)
+int BarChart::itemAt(float x, bool includeSelectorWidth) const
+{
+ int numValues = m_values.n_elem;
+ float barWidth = 1.0f / numValues;
+ if (includeSelectorWidth) {
+ x += 1.0f / width();
+ }
+
+ return clamp(int(x / barWidth), 0, numValues - 1);
+}
+
+void BarChart::interactiveSelection(float start, float end)
{
if (start > end) {
std::swap(start, end);
}
- m_selection.clear();
- if (start > 0 && end > 0) {
- // Bars are located in ranges:
- // [0..barWidth] + barIndex / barWidth
- int numValues = m_values.n_elem;
- float barWidth = 1.0f / numValues;
- float selectorWidth = 1.0f / width();
- int firstIndex = int(start / barWidth);
- int lastIndex = std::min(numValues - 1, int((end + selectorWidth) / barWidth));
-
- for (int i = firstIndex; i <= lastIndex; i++) {
- m_selection.insert(m_originalIndices[i]);
- }
+ m_selection.assign(m_selection.size(), false);
+ if (start < 0.0f || end < 0.0f) {
+ return;
}
- emit selectionChanged(m_selection);
- update();
+ // Bars are located in ranges:
+ // [0..barWidth] + barIndex / barWidth
+ int firstIndex = itemAt(start);
+ int lastIndex = itemAt(end, true);
+ for (int i = firstIndex; i <= lastIndex; i++) {
+ m_selection[m_originalIndices[i]] = true;
+ }
+
+ emit selectionInteractivelyChanged(m_selection);
}
void BarChart::hoverEnterEvent(QHoverEvent *event)
@@ -328,7 +356,11 @@ void BarChart::mouseReleaseEvent(QMouseEvent *event)
float pos = float(event->pos().x()) / width();
m_dragLastPos = clamp(pos, 0.0f, 1.0f);
m_hoverPos = pos;
- selectBarsInRange(m_dragStartPos, m_dragLastPos);
+
+ if (m_values.n_elem > 0) {
+ interactiveSelection(m_dragStartPos, m_dragLastPos);
+ }
+
m_shouldUpdateSelectionRect = true;
update();
}
diff --git a/barchart.h b/barchart.h
index d785aa9..d49c001 100644
--- a/barchart.h
+++ b/barchart.h
@@ -4,7 +4,6 @@
#include <vector>
#include <QtQuick>
-#include <QSet>
#include <armadillo>
@@ -22,11 +21,13 @@ public:
signals:
void valuesChanged(const arma::vec &values) const;
void colorScaleChanged(const ColorScale &scale) const;
- void selectionChanged(const QSet<int> &selection) const;
+ void selectionChanged(const std::vector<bool> &selection) const;
+ void selectionInteractivelyChanged(const std::vector<bool> &selection) const;
public slots:
void setValues(const arma::vec &values);
void setColorScale(const ColorScale &scale);
+ void setSelection(const std::vector<bool> &selection);
protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
@@ -53,9 +54,11 @@ private:
void updateSelectionRect(QSGNode *node);
bool m_shouldUpdateSelectionRect;
- void selectBarsInRange(float start, float end);
+ void interactiveSelection(float start, float end);
float m_dragStartPos, m_dragLastPos;
- QSet<int> m_selection;
+ std::vector<bool> m_selection;
+
+ int itemAt(float x, bool includeSelectorWidth = false) const;
arma::vec m_values;
ColorScale m_colorScale;
diff --git a/main.cpp b/main.cpp
index b9daab9..6af65b3 100644
--- a/main.cpp
+++ b/main.cpp
@@ -17,6 +17,7 @@
#include "barchart.h"
#include "colormap.h"
#include "interactionhandler.h"
+#include "selectionhandler.h"
#include "projectionobserver.h"
#include "skelft.h"
@@ -153,13 +154,6 @@ int main(int argc, char **argv)
QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
m->splat, SLOT(setSites(const arma::mat &)));
- // Linking between selections in cp plot and rp plot
- //SelectionHandler selectionHandler(cpIndices);
- //QObject::connect(cpPlot, SIGNAL(selectionChanged(const QSet<int> &)),
- // &selectionHandler, SLOT(setSelection(const QSet<int> &)));
- //QObject::connect(&selectionHandler, SIGNAL(selectionChanged(const QSet<int> &)),
- // plot, SLOT(setSelection(const QSet<int> &)));
-
// Connections between history graph and cp plot
//HistoryGraph *history = engine.rootObjects()[0]->findChild<HistoryGraph *>("history");
//QObject::connect(cpPlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)),
@@ -181,26 +175,36 @@ int main(int argc, char **argv)
m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
m->setRPBarChartColorScale(Main::ColorScaleContinuous);
- QObject::connect(m->cpBarChart, SIGNAL(selectionChanged(const QSet<int> &)),
- m->cpPlot, SLOT(setSelection(const QSet<int> &)));
-
- QObject::connect(m->rpBarChart, SIGNAL(selectionChanged(const QSet<int> &)),
- m->rpPlot, SLOT(setSelection(const QSet<int> &)));
+ // Linking between selections
+ SelectionHandler cpSelectionHandler(cpIndices.n_elem);
+ QObject::connect(m->cpPlot, SIGNAL(selectionInteractivelyChanged(const std::vector<bool> &)),
+ &cpSelectionHandler, SLOT(setSelection(const std::vector<bool> &)));
+ QObject::connect(m->cpBarChart, SIGNAL(selectionInteractivelyChanged(const std::vector<bool> &)),
+ &cpSelectionHandler, SLOT(setSelection(const std::vector<bool> &)));
+ QObject::connect(&cpSelectionHandler, SIGNAL(selectionChanged(const std::vector<bool> &)),
+ m->cpPlot, SLOT(setSelection(const std::vector<bool> &)));
+
+ //SelectionHandler rpSelectionHandler(X.n_rows - cpIndices.n_elem);
+ //QObject::connect(m->rpPlot, SIGNAL(selectionInteractivelyChanged(const std::vector<bool> &)),
+ // &rpSelectionHandler, SLOT(setSelection(const std::vector<bool> &)));
+ //QObject::connect(m->rpBarChart, SIGNAL(selectionInteractivelyChanged(const std::vector<bool> &)),
+ // &rpSelectionHandler, SLOT(setSelection(const std::vector<bool> &)));
+ //QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector<bool> &)),
+ // m->rpPlot, SLOT(setSelection(const std::vector<bool> &)));
ProjectionObserver projectionObserver(X, cpIndices);
m->projectionObserver = &projectionObserver;
QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
m->projectionObserver, SLOT(setMap(const arma::mat &)));
- QObject::connect(m->projectionObserver, SIGNAL(valuesChanged(const arma::vec &)),
- m->rpPlot, SLOT(setColorData(const arma::vec &)));
+ //QObject::connect(m->projectionObserver, SIGNAL(valuesChanged(const arma::vec &)),
+ // m->rpPlot, SLOT(setColorData(const arma::vec &)));
QObject::connect(m->projectionObserver, SIGNAL(valuesChanged(const arma::vec &)),
m->splat, SLOT(setValues(const arma::vec &)));
- QObject::connect(m->projectionObserver, SIGNAL(valuesChanged(const arma::vec &)),
+ QObject::connect(m->projectionObserver, SIGNAL(cpValuesChanged(const arma::vec &)),
m->cpBarChart, SLOT(setValues(const arma::vec &)));
- QObject::connect(m->projectionObserver, SIGNAL(valuesChanged(const arma::vec &)),
- m->rpBarChart, SLOT(setValues(const arma::vec &)));
+ //QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
+ // m->rpBarChart, SLOT(setValues(const arma::vec &)));
- //history->addHistoryItem(Ys);
m->setColormapColorScale(Main::ColorScaleContinuous);
m->setCPPlotColorScale(Main::ColorScaleContinuous);
m->setRPPlotColorScale(Main::ColorScaleContinuous);
diff --git a/pm.pro b/pm.pro
index 3f3332b..f864b96 100644
--- a/pm.pro
+++ b/pm.pro
@@ -15,6 +15,7 @@ HEADERS += main.h \
historygraph.h \
barchart.h \
interactionhandler.h \
+ selectionhandler.h \
projectionobserver.h \
skelft.h \
skelftkernel.h \
@@ -29,6 +30,7 @@ SOURCES += main.cpp \
historygraph.cpp \
barchart.cpp \
interactionhandler.cpp \
+ selectionhandler.cpp \
projectionobserver.cpp \
skelft_core.cpp \
lamp.cpp \
diff --git a/projectionobserver.cpp b/projectionobserver.cpp
index 262332d..30f8325 100644
--- a/projectionobserver.cpp
+++ b/projectionobserver.cpp
@@ -1,5 +1,6 @@
#include "projectionobserver.h"
+#include <algorithm>
#include <cmath>
#include <QDebug>
@@ -36,9 +37,18 @@ ProjectionObserver::ProjectionObserver(const arma::mat &X,
: m_type(OBSERVER_CURRENT)
, m_X(X)
, m_cpIndices(cpIndices)
+ , m_rpIndices(X.n_rows - cpIndices.n_elem)
{
m_distX = mp::dist(m_X);
m_values.set_size(m_X.n_rows);
+
+ arma::uvec indices(m_X.n_rows);
+ for (arma::uword i = 0; i < indices.n_elem; i++) {
+ indices[i] = i;
+ }
+ std::set_difference(indices.cbegin(), indices.cend(),
+ m_cpIndices.cbegin(), m_cpIndices.cend(),
+ m_rpIndices.begin());
}
bool ProjectionObserver::setType(int type)
@@ -82,17 +92,25 @@ bool ProjectionObserver::emitValuesChanged() const
{
switch (m_type) {
case OBSERVER_CURRENT:
+ emit cpValuesChanged(m_values(m_cpIndices));
+ emit rpValuesChanged(m_values(m_rpIndices));
emit valuesChanged(m_values);
return true;
case OBSERVER_DIFF_PREVIOUS:
if (m_prevValues.n_elem > 0) {
- emit valuesChanged(m_values - m_prevValues);
+ arma::vec diff = m_values - m_prevValues;
+ emit cpValuesChanged(diff(m_cpIndices));
+ emit rpValuesChanged(diff(m_rpIndices));
+ emit valuesChanged(diff);
return true;
}
return false;
case OBSERVER_DIFF_ORIGINAL:
if (m_origValues.n_elem > 0) {
- emit valuesChanged(m_values - m_origValues);
+ arma::vec diff = m_values - m_origValues;
+ emit cpValuesChanged(diff(m_cpIndices));
+ emit rpValuesChanged(diff(m_rpIndices));
+ emit valuesChanged(diff);
return true;
}
return false;
diff --git a/projectionobserver.h b/projectionobserver.h
index 1c305bd..c0ccd8a 100644
--- a/projectionobserver.h
+++ b/projectionobserver.h
@@ -4,9 +4,8 @@
#include <QObject>
#include <armadillo>
-#include "distortionmeasure.h"
-
-class ProjectionObserver : public QObject
+class ProjectionObserver
+ : public QObject
{
Q_OBJECT
public:
@@ -18,6 +17,8 @@ public:
signals:
void valuesChanged(const arma::vec &values) const;
+ void cpValuesChanged(const arma::vec &values) const;
+ void rpValuesChanged(const arma::vec &values) const;
public slots:
void setMap(const arma::mat &Y);
@@ -29,7 +30,7 @@ private:
int m_type;
arma::mat m_X, m_Y, m_origY, m_prevY;
arma::mat m_distX, m_distY, m_origDistY, m_prevDistY;
- arma::uvec m_cpIndices;
+ arma::uvec m_cpIndices, m_rpIndices;
// TODO: one per implemented measure
arma::vec m_values, m_prevValues, m_origValues;
diff --git a/scatterplot.cpp b/scatterplot.cpp
index c5678ec..b547579 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -1,5 +1,6 @@
#include "scatterplot.h"
+#include <algorithm>
#include <cmath>
#include <QSGGeometryNode>
@@ -63,10 +64,6 @@ void Scatterplot::setXY(const arma::mat &xy, bool updateView)
return;
}
- if (m_xy.n_elem != xy.n_elem) {
- m_selectedGlyphs.clear();
- }
-
m_xy = xy;
emit xyChanged(m_xy);
@@ -74,6 +71,11 @@ void Scatterplot::setXY(const arma::mat &xy, bool updateView)
autoScale();
}
+ if (m_selection.size() != m_xy.n_rows) {
+ m_selection.resize(m_xy.n_rows);
+ m_selection.assign(m_selection.size(), false);
+ }
+
if (m_opacityData.n_elem != m_xy.n_rows) {
arma::vec opacityData(xy.n_rows);
opacityData.fill(GLYPH_OPACITY);
@@ -301,7 +303,7 @@ void Scatterplot::updateGlyphs(QSGNode *glyphsNode)
QSGNode *node = glyphsNode->firstChild();
for (arma::uword i = 0; i < m_xy.n_rows; i++) {
const arma::rowvec &row = m_xy.row(i);
- bool isSelected = m_selectedGlyphs.contains(i);
+ bool isSelected = m_selection[i];
QSGOpacityNode *glyphOpacityNode = static_cast<QSGOpacityNode *>(node);
glyphOpacityNode->setOpacity(m_opacityData[i]);
@@ -359,9 +361,6 @@ 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();
@@ -373,6 +372,7 @@ void Scatterplot::mouseMoveEvent(QMouseEvent *event)
m_shouldUpdateGeometry = true;
update();
break;
+ case INTERACTION_NONE:
case INTERACTION_SELECTED:
event->ignore();
return;
@@ -385,9 +385,11 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
case INTERACTION_SELECTING:
{
bool mergeSelection = (event->modifiers() == Qt::ControlModifier);
- m_currentInteractionState =
- updateSelection(mergeSelection) ? INTERACTION_SELECTED
- : INTERACTION_NONE;
+ bool anySelected = interactiveSelection(mergeSelection);
+ m_currentInteractionState = anySelected ? INTERACTION_SELECTED
+ : INTERACTION_NONE;
+ m_shouldUpdateMaterials = true;
+ update();
}
break;
case INTERACTION_BEGIN_MOVING:
@@ -402,53 +404,50 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
break;
case INTERACTION_NONE:
case INTERACTION_SELECTED:
- return; // should not be reached
+ break; // should not be reached
}
}
-bool Scatterplot::updateSelection(bool mergeSelection)
+bool Scatterplot::interactiveSelection(bool mergeSelection)
{
- QSet<int> selection;
- if (mergeSelection) {
- selection.unite(m_selectedGlyphs);
+ if (!mergeSelection) {
+ m_selection.assign(m_selection.size(), false);
}
m_sx.inverse();
m_sy.inverse();
-
- float originX = m_sx(m_dragOriginPos.x());
- float originY = m_sy(m_dragOriginPos.y());
- float currentX = m_sx(m_dragCurrentPos.x());
- float currentY = m_sy(m_dragCurrentPos.y());
-
+ QRectF selectionRect(QPointF(m_sx(m_dragOriginPos.x()),
+ m_sy(m_dragOriginPos.y())),
+ QPointF(m_sx(m_dragCurrentPos.x()),
+ m_sy(m_dragCurrentPos.y())));
m_sy.inverse();
m_sx.inverse();
- QRectF selectionRect(QPointF(originX, originY), QPointF(currentX, currentY));
-
+ bool anySelected = false;
for (arma::uword i = 0; i < m_xy.n_rows; i++) {
const arma::rowvec &row = m_xy.row(i);
if (selectionRect.contains(row[0], row[1])) {
- selection.insert(i);
+ m_selection[i] = true;
+ anySelected = true;
}
}
- setSelection(selection);
- return !selection.isEmpty();
+ emit selectionInteractivelyChanged(m_selection);
+ return anySelected;
}
-void Scatterplot::setSelection(const QSet<int> &selection)
+void Scatterplot::setSelection(const std::vector<bool> &selection)
{
- m_selectedGlyphs = selection;
- for (auto it = m_selectedGlyphs.cbegin();
- it != m_selectedGlyphs.cend(); it++) {
- m_opacityData[*it] = GLYPH_OPACITY_SELECTED;
+ if (m_selection.size() != selection.size()) {
+ return;
}
+
+ m_selection = selection;
+ emit selectionChanged(m_selection);
+
m_shouldUpdateMaterials = true;
update();
-
- emit selectionChanged(selection);
}
void Scatterplot::applyManipulation()
@@ -463,11 +462,13 @@ void Scatterplot::applyManipulation()
float tx = m_dragCurrentPos.x() - m_dragOriginPos.x();
float ty = m_dragCurrentPos.y() - m_dragOriginPos.y();
- for (auto it = m_selectedGlyphs.cbegin(); it != m_selectedGlyphs.cend(); it++) {
- arma::rowvec row = m_xy.row(*it);
- row[0] = rx(m_sx(row[0]) + tx);
- row[1] = ry(m_sy(row[1]) + ty);
- m_xy.row(*it) = row;
+ for (std::vector<bool>::size_type i = 0; i < m_selection.size(); i++) {
+ if (m_selection[i]) {
+ arma::rowvec row = m_xy.row(i);
+ row[0] = rx(m_sx(row[0]) + tx);
+ row[1] = ry(m_sy(row[1]) + ty);
+ m_xy.row(i) = row;
+ }
}
emit xyInteractivelyChanged(m_xy);
diff --git a/scatterplot.h b/scatterplot.h
index c15215b..0fb12ba 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -1,8 +1,9 @@
#ifndef SCATTERPLOT_H
#define SCATTERPLOT_H
+#include <vector>
+
#include <QtQuick>
-#include <QSet>
#include <armadillo>
@@ -35,7 +36,8 @@ signals:
void xyInteractivelyChanged(const arma::mat &XY) const;
void colorDataChanged(const arma::vec &colorData) const;
void opacityDataChanged(const arma::vec &opacityData) const;
- void selectionChanged(const QSet<int> &selection) const;
+ void selectionChanged(const std::vector<bool> &selection) const;
+ void selectionInteractivelyChanged(const std::vector<bool> &selection) const;
void scaleChanged(const LinearScale<float> &sx, const LinearScale<float> &sy) const;
void glyphSizeChanged(float glyphSize) const;
@@ -43,7 +45,7 @@ public slots:
void setXY(const arma::mat &xy);
void setColorData(const arma::vec &colorData);
void setOpacityData(const arma::vec &opacityData);
- void setSelection(const QSet<int> &selection);
+ void setSelection(const std::vector<bool> &selection);
void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy);
Q_INVOKABLE void setGlyphSize(float glyphSize);
@@ -74,8 +76,8 @@ private:
LinearScale<float> m_sx, m_sy;
// Internal state
- bool updateSelection(bool mergeSelection);
- QSet<int> m_selectedGlyphs;
+ bool interactiveSelection(bool mergeSelection);
+ std::vector<bool> m_selection;
enum InteractionState {
INTERACTION_NONE,
diff --git a/selectionhandler.cpp b/selectionhandler.cpp
index 44eef28..190040d 100644
--- a/selectionhandler.cpp
+++ b/selectionhandler.cpp
@@ -1,19 +1,33 @@
#include "selectionhandler.h"
-SelectionHandler::SelectionHandler(const arma::uvec &sampleIndices)
- : m_sampleIndices(sampleIndices)
+#include <QDebug>
+
+SelectionHandler::SelectionHandler(int numItems)
+ : m_selection(numItems, false)
+{
+}
+
+void SelectionHandler::setSelection(const std::vector<bool> &selection)
{
+ if (m_selection.size() != selection.size()) {
+ return;
+ }
+
+ m_selection = selection;
+ emit selectionChanged(m_selection);
}
-void SelectionHandler::setSelection(const QSet<int> &selection)
+void SelectionHandler::setSelected(int item, bool selected)
{
- QSet<int> newSelection;
+ m_selection[item] = selected;
+ emit selectionChanged(m_selection);
+}
- // The selecion happens over the sample indices. We use the original dataset
- // indices in sampleIndices to translate indices.
- for (auto it = selection.begin(); it != selection.end(); it++) {
- newSelection.insert(m_sampleIndices[*it]);
+void SelectionHandler::setSelected(const std::set<int> &items, bool selected)
+{
+ for (auto it = items.cbegin(); it != items.cend(); it++) {
+ m_selection[*it] = selected;
}
- emit selectionChanged(newSelection);
+ emit selectionChanged(m_selection);
}
diff --git a/selectionhandler.h b/selectionhandler.h
index e5b9686..dad59cc 100644
--- a/selectionhandler.h
+++ b/selectionhandler.h
@@ -1,24 +1,28 @@
#ifndef SELECTIONHANDLER_H
#define SELECTIONHANDLER_H
+#include <set>
+#include <vector>
+
#include <QObject>
-#include <QSet>
-#include <armadillo>
-class SelectionHandler : public QObject
+class SelectionHandler
+ : public QObject
{
Q_OBJECT
public:
- SelectionHandler(const arma::uvec &sampleIndices);
+ SelectionHandler(int numItems);
signals:
- void selectionChanged(const QSet<int> &selection);
+ void selectionChanged(const std::vector<bool> &selection);
public slots:
- void setSelection(const QSet<int> &selection);
+ void setSelection(const std::vector<bool> &selection);
+ void setSelected(int item, bool selected = true);
+ void setSelected(const std::set<int> &items, bool selected = true);
private:
- arma::uvec m_sampleIndices;
+ std::vector<bool> m_selection;
};
#endif // SELECTIONHANDLER_H