aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-01-28 15:52:39 +0100
committerSamuel Fadel <samuelfadel@gmail.com>2016-01-28 15:55:07 +0100
commitff10df2f8bfc71afe8637eec67d11d6ce87b673b (patch)
tree3e7ba65d7c2cff27a2d13511f25857a4e5326e1d
parent5404374a510b4c09bd4d38331a1255583abb28d4 (diff)
ProjectionObsever now responds to selection changes.
* Scatterplot: small fix to selection updates * ProjectionObserver: whenever CP selection changes, compute influence of CPs over all RPs. Whenever RP selection changes, compute influence of RPs by all CPs. * ProjectionObserver: with empty selections, go back to normal mode
-rw-r--r--main.cpp15
-rw-r--r--projectionobserver.cpp106
-rw-r--r--projectionobserver.h9
-rw-r--r--scatterplot.cpp20
4 files changed, 122 insertions, 28 deletions
diff --git a/main.cpp b/main.cpp
index a9a5a86..3631061 100644
--- a/main.cpp
+++ b/main.cpp
@@ -216,8 +216,8 @@ int main(int argc, char **argv)
m->projectionObserver = &projectionObserver;
QObject::connect(&manipulationHandler, SIGNAL(mapChanged(const arma::mat &)),
m->projectionObserver, SLOT(setMap(const arma::mat &)));
- QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
- m->rpPlot, SLOT(setColorData(const arma::vec &)));
+ QObject::connect(m->projectionObserver, SIGNAL(cpValuesChanged(const arma::vec &)),
+ m->cpPlot, SLOT(setColorData(const arma::vec &)));
QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
m->splat, SLOT(setValues(const arma::vec &)));
QObject::connect(m->projectionObserver, SIGNAL(cpValuesChanged(const arma::vec &)),
@@ -225,11 +225,17 @@ int main(int argc, char **argv)
QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
m->rpBarChart, SLOT(setValues(const arma::vec &)));
+ // Recompute values whenever selection changes
+ QObject::connect(&cpSelectionHandler, SIGNAL(selectionChanged(const std::vector<bool> &)),
+ &projectionObserver, SLOT(setCPSelection(const std::vector<bool> &)));
+ QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector<bool> &)),
+ &projectionObserver, SLOT(setRPSelection(const std::vector<bool> &)));
+
// General component set up
m->cpPlot->setAcceptHoverEvents(true);
m->cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
- m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
- m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
+ m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
m->setColormapColorScale(Main::ColorScaleContinuous);
m->setCPPlotColorScale(Main::ColorScaleContinuous);
@@ -241,7 +247,6 @@ int main(int argc, char **argv)
m->cpPlot->setAutoScale(false);
m->rpPlot->setAutoScale(false);
m->rpPlot->setGlyphSize(3.0f);
- m->cpPlot->setColorData(labels(cpIndices), false);
// This sets the initial CP configuration, triggering all the necessary
// signals to set up the helper objects and visual components
diff --git a/projectionobserver.cpp b/projectionobserver.cpp
index 395110c..1293128 100644
--- a/projectionobserver.cpp
+++ b/projectionobserver.cpp
@@ -38,27 +38,37 @@ ProjectionObserver::ProjectionObserver(const arma::mat &X,
, m_X(X)
, m_cpIndices(cpIndices)
, m_rpIndices(X.n_rows - cpIndices.n_elem)
+ , m_cpSelectionEmpty(true)
+ , m_rpSelectionEmpty(true)
{
m_distX = mp::dist(m_X);
m_values.set_size(m_X.n_rows);
- NumericRange<unsigned long long> range(0, m_X.n_rows);
+ NumericRange<arma::uword> range(0, m_X.n_rows);
std::set_symmetric_difference(range.cbegin(), range.cend(),
m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin());
+
+ computeAlphas();
}
-bool ProjectionObserver::setType(int type)
+void ProjectionObserver::computeAlphas()
{
- if (m_type == type) {
- return true;
- }
+ m_influences.set_size(m_X.n_rows);
+ m_alphas.set_size(m_rpIndices.n_elem, m_cpIndices.n_elem);
+
+ for (arma::uword i = 0; i < m_rpIndices.n_elem; i++) {
+ double sum = 0;
+ const arma::rowvec &x = m_X.row(m_rpIndices[i]);
+ for (arma::uword j = 0; j < m_cpIndices.n_elem; j++) {
+ double norm = arma::norm(x - m_X.row(m_cpIndices[j]));
+ m_alphas(i, j) = 1.0f / std::max(norm * norm, 1e-6);
+ sum += m_alphas(i, j);
+ }
- if (type != OBSERVER_DIFF_PREVIOUS || m_prevValues.n_elem != 0) {
- m_type = type;
- return emitValuesChanged();
+ for (arma::uword j = 0; j < m_cpIndices.n_elem; j++) {
+ m_alphas(i, j) /= sum;
+ }
}
-
- return false;
}
void ProjectionObserver::setMap(const arma::mat &Y)
@@ -81,21 +91,90 @@ void ProjectionObserver::setMap(const arma::mat &Y)
m_origValues = m_values;
}
- emitValuesChanged();
+ if (m_cpSelectionEmpty && m_rpSelectionEmpty) {
+ emitValuesChanged();
+ }
+}
+
+bool ProjectionObserver::setType(int type)
+{
+ if (m_type == type) {
+ return true;
+ }
+
+ if (type != OBSERVER_DIFF_PREVIOUS || m_prevValues.n_elem != 0) {
+ m_type = type;
+ if (m_cpSelectionEmpty && m_rpSelectionEmpty) {
+ return emitValuesChanged();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void ProjectionObserver::setCPSelection(const std::vector<bool> &cpSelection)
+{
+ m_cpSelection.clear();
+ for (int i = 0; i < cpSelection.size(); i++) {
+ if (cpSelection[i]) {
+ m_cpSelection.push_back(i);
+ }
+ }
+ m_cpSelectionEmpty = m_cpSelection.empty();
+
+ if (!m_cpSelectionEmpty) {
+ // compute the influence of CP selection on each RP
+ for (arma::uword rp = 0; rp < m_rpIndices.n_elem; rp++) {
+ m_influences[m_rpIndices[rp]] = 0;
+ const arma::rowvec &row = m_alphas.row(rp);
+ for (auto cp: m_cpSelection) {
+ m_influences[m_rpIndices[rp]] += row[cp];
+ }
+ }
+
+ emit rpValuesChanged(m_influences(m_rpIndices));
+ } else {
+ emitValuesChanged();
+ }
+}
+
+void ProjectionObserver::setRPSelection(const std::vector<bool> &rpSelection)
+{
+ m_rpSelection.clear();
+ for (int i = 0; i < rpSelection.size(); i++) {
+ if (rpSelection[i]) {
+ m_rpSelection.push_back(i);
+ }
+ }
+ m_rpSelectionEmpty = m_rpSelection.empty();
+
+ if (!m_rpSelectionEmpty) {
+ // compute how influent is each CP on RP selection
+ for (arma::uword cp = 0; cp < m_cpIndices.n_elem; cp++) {
+ m_influences[m_cpIndices[cp]] = 0;
+ for (auto rp: m_rpSelection) {
+ m_influences[m_cpIndices[cp]] += m_alphas(rp, cp);
+ }
+ }
+
+ emit cpValuesChanged(m_influences(m_cpIndices));
+ } else {
+ emit cpValuesChanged(arma::vec());
+ }
}
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) {
arma::vec diff = m_values - m_prevValues;
- emit cpValuesChanged(diff(m_cpIndices));
emit rpValuesChanged(diff(m_rpIndices));
emit valuesChanged(diff);
return true;
@@ -104,7 +183,6 @@ bool ProjectionObserver::emitValuesChanged() const
case OBSERVER_DIFF_ORIGINAL:
if (m_origValues.n_elem > 0) {
arma::vec diff = m_values - m_origValues;
- emit cpValuesChanged(diff(m_cpIndices));
emit rpValuesChanged(diff(m_rpIndices));
emit valuesChanged(diff);
return true;
diff --git a/projectionobserver.h b/projectionobserver.h
index c0ccd8a..badca4d 100644
--- a/projectionobserver.h
+++ b/projectionobserver.h
@@ -23,6 +23,8 @@ signals:
public slots:
void setMap(const arma::mat &Y);
bool setType(int type);
+ void setCPSelection(const std::vector<bool> &cpSelection);
+ void setRPSelection(const std::vector<bool> &rpSelection);
private:
bool emitValuesChanged() const;
@@ -32,6 +34,13 @@ private:
arma::mat m_distX, m_distY, m_origDistY, m_prevDistY;
arma::uvec m_cpIndices, m_rpIndices;
+ bool m_cpSelectionEmpty, m_rpSelectionEmpty;
+ std::vector<int> m_cpSelection, m_rpSelection;
+
+ // alpha(i, j): the influence CP j has on RP i
+ void computeAlphas();
+ arma::mat m_alphas, m_influences;
+
// TODO: one per implemented measure
arma::vec m_values, m_prevValues, m_origValues;
};
diff --git a/scatterplot.cpp b/scatterplot.cpp
index 260825c..97f2f41 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -195,7 +195,7 @@ void QuadTree::query(const QRectF &rect, std::vector<int> &result) const
m_ne->query(rect, result);
m_sw->query(rect, result);
m_se->query(rect, result);
- } else if (rect.contains(m_x, m_y)) {
+ } else if (rect.contains(m_x, m_y) && m_value != -1) {
result.push_back(m_value);
}
}
@@ -278,14 +278,18 @@ void Scatterplot::setXY(const arma::mat &xy)
void Scatterplot::setColorData(const arma::vec &colorData, bool updateView)
{
- if (m_xy.n_rows > 0 && colorData.n_elem != m_xy.n_rows) {
+ if (m_xy.n_rows > 0
+ && (colorData.n_elem > 0 && colorData.n_elem != m_xy.n_rows)) {
return;
}
m_colorData = colorData;
emit colorDataChanged(m_colorData);
- m_colorScale.setExtents(m_colorData.min(), m_colorData.max());
+ if (m_colorData.n_elem > 0) {
+ m_colorScale.setExtents(m_colorData.min(), m_colorData.max());
+ }
+
m_shouldUpdateMaterials = true;
if (updateView) {
update();
@@ -623,8 +627,6 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
if (m_brushedItem < 0 || event->button() == Qt::RightButton) {
// Mouse clicked with no brush target; clear selection, if any
m_selection.assign(m_selection.size(), false);
- m_shouldUpdateMaterials = true;
- update();
} else {
// Mouse clicked with brush target; set new selection or append to
// current
@@ -633,11 +635,11 @@ void Scatterplot::mouseReleaseEvent(QMouseEvent *event)
m_selection.assign(m_selection.size(), false);
}
m_selection[m_brushedItem] = true;
- emit selectionInteractivelyChanged(m_selection);
-
- m_shouldUpdateMaterials = true;
- update();
}
+ emit selectionInteractivelyChanged(m_selection);
+
+ m_shouldUpdateMaterials = true;
+ update();
break;
case INTERACTION_SELECTING:
{