From ff10df2f8bfc71afe8637eec67d11d6ce87b673b Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Thu, 28 Jan 2016 15:52:39 +0100 Subject: 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 --- main.cpp | 15 ++++--- projectionobserver.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++------- projectionobserver.h | 9 +++++ scatterplot.cpp | 20 +++++----- 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 &)), + &projectionObserver, SLOT(setCPSelection(const std::vector &))); + QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector &)), + &projectionObserver, SLOT(setRPSelection(const std::vector &))); + // 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 range(0, m_X.n_rows); + NumericRange 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 &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 &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 &cpSelection); + void setRPSelection(const std::vector &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 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 &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: { -- cgit v1.2.3