diff options
-rw-r--r-- | lineplot.cpp | 222 | ||||
-rw-r--r-- | lineplot.h | 30 | ||||
-rw-r--r-- | main.cpp | 130 | ||||
-rw-r--r-- | main_view.qml | 10 | ||||
-rw-r--r-- | projectionhistory.cpp | 54 | ||||
-rw-r--r-- | projectionhistory.h | 11 |
6 files changed, 367 insertions, 90 deletions
diff --git a/lineplot.cpp b/lineplot.cpp index 35654be..10fff9b 100644 --- a/lineplot.cpp +++ b/lineplot.cpp @@ -19,15 +19,19 @@ #include "geometry.h" #include "scatterplot.h" +static const float EPSILON = 1e-5f; static const int SAMPLES = 128; LinePlot::LinePlot(QQuickItem *parent) : QQuickFramebufferObject(parent) , m_sx(0, 1, 0, 1) , m_sy(0, 1, 0, 1) + , m_brushedItem(-1) + , m_anySelected(false) , m_linesChanged(false) , m_valuesChanged(false) , m_colorScaleChanged(false) + , m_updateOffsets(false) // Q_PROPERTY's , m_iterations(15) @@ -44,6 +48,7 @@ LinePlot::LinePlot(QQuickItem *parent) , m_advectionSpeed(0.5) , m_relaxation(0) , m_bundleGPU(true) + , m_lineWidth(2.0) { setFlag(QQuickItem::ItemHasContents); setTextureFollowsItemSize(false); @@ -66,6 +71,27 @@ void LinePlot::relax() setLinesChanged(true); } +void LinePlot::buildGraph() +{ + Graph g(m_Y.n_rows); + PointSet points; + + m_sx.setRange(0, width()); + m_sy.setRange(0, height()); + for (arma::uword i = 0; i < m_Y.n_rows; i++) { + points.push_back(Point2d(m_sx(m_Y(i, 0)), m_sy(m_Y(i, 1)))); + } + + for (arma::uword k = 0; k < m_indices.n_elem; k += 2) { + arma::uword i = m_indices[k + 0], + j = m_indices[k + 1]; + g(i, j) = g(j, i) = m_values[k]; + } + + m_gdPtr.reset(new GraphDrawing); + m_gdPtr.get()->build(&g, &points); +} + void LinePlot::bundle() { m_gdBundle = *m_gdPtr.get(); @@ -91,6 +117,7 @@ void LinePlot::bundle() } else { bundling.bundleCPU(); } + m_updateOffsets = true; relax(); } @@ -101,45 +128,33 @@ void LinePlot::setLines(const arma::uvec &indices, const arma::mat &Y) return; } - m_lines = Y.rows(indices); - - // Build the line plot's internal representation: a graph where each - // endpoint of a line is a node, each line is a link... - Graph g(Y.n_rows); - PointSet points; - - m_sx.setRange(0, width()); - m_sy.setRange(0, height()); - for (arma::uword i = 0; i < Y.n_rows; i++) { - points.push_back(Point2d(m_sx(Y(i, 0)), m_sy(Y(i, 1)))); - } + m_indices = indices; + emit indicesChanged(m_indices); - for (arma::uword k = 0; k < m_values.size(); k++) { - arma::uword i = indices(2*k + 0), - j = indices(2*k + 1); + m_Y = Y; + emit pointsChanged(m_Y); - g(i, j) = g(j, i) = m_values[k]; - } + // Clear current selection + m_anySelected = false; + m_selection.assign(m_Y.n_rows, false); + emit selectionChanged(m_selection); - m_gdPtr.reset(new GraphDrawing); - m_gdPtr.get()->build(&g, &points); + // Build the line plot's internal representation: a graph where each + // endpoint of a line is a node, each line is a link... + buildGraph(); // ... then bundle the edges bundle(); - emit linesChanged(m_lines); update(); } void LinePlot::setValues(const arma::vec &values) { - if (m_lines.n_rows > 0 - && (values.n_elem > 0 && values.n_elem != m_lines.n_rows)) { - return; - } - m_values.resize(values.n_elem); - std::copy(values.begin(), values.end(), m_values.begin()); + std::transform(values.begin(), values.end(), m_values.begin(), [](float v) { + return std::max(v, EPSILON); + }); emit valuesChanged(values); setValuesChanged(true); @@ -155,6 +170,27 @@ void LinePlot::setScale(const LinearScale<float> &sx, update(); } +void LinePlot::brushItem(int item) +{ + m_brushedItem = item; + m_updateOffsets = true; + update(); +} + +void LinePlot::setSelection(const std::vector<bool> &selection) +{ + m_selection = selection; + m_anySelected = std::any_of(m_selection.cbegin(), + m_selection.cend(), + [](bool b) { return b; }); + emit selectionChanged(m_selection); + + // XXX: *possibly* needed; doesn't seem to make much of a difference + //bundle(); + m_updateOffsets = true; + update(); +} + // Q_PROPERTY's void LinePlot::setIterations(int iterations) { if (m_iterations == iterations) { @@ -311,6 +347,18 @@ void LinePlot::setBundleGPU(bool bundleGPU) update(); } +void LinePlot::setLineWidth(float lineWidth) +{ + if (m_lineWidth == lineWidth) { + return; + } + + m_lineWidth = lineWidth; + emit lineWidthChanged(m_lineWidth); + + update(); +} + // ---------------------------------------------------------------------------- class LinePlotRenderer @@ -334,11 +382,15 @@ private: void updateValues(); void updateColormap(); - void copyPolylines(const GraphDrawing *gd); + void copyPolylines(const LinePlot *plot); + void computeOffsets(const LinePlot *plot); + void computeBrushOffsets(const LinePlot *plot); + void computeAllOffsets(const LinePlot *plot); QSize m_size; std::vector<float> m_points; const std::vector<float> *m_values, *m_cmap; + float m_lineWidth; std::vector<int> m_offsets; QQuickWindow *m_window; // used to reset OpenGL state (as per docs) @@ -360,6 +412,9 @@ LinePlotRenderer::LinePlotRenderer() : gl(QOpenGLContext::currentContext()) , m_sx(0.0f, 1.0f, 0.0f, 1.0f) , m_sy(0.0f, 1.0f, 0.0f, 1.0f) + , m_pointsChanged(false) + , m_valuesChanged(false) + , m_colormapChanged(false) { std::fill(&m_transform[0][0], &m_transform[0][0] + 16, 0.0f); m_transform[3][3] = 1.0f; @@ -471,10 +526,6 @@ QOpenGLFramebufferObject *LinePlotRenderer::createFramebufferObject(const QSize void LinePlotRenderer::render() { - if (!m_pointsChanged && !m_valuesChanged && !m_colormapChanged) { - return; - } - // Update OpenGL buffers and textures as needed if (m_pointsChanged) { updatePoints(); @@ -486,11 +537,6 @@ void LinePlotRenderer::render() updateColormap(); } - if (m_offsets.size() < 2) { - // Nothing to draw - return; - } - m_program->bind(); gl.glActiveTexture(GL_TEXTURE0); @@ -506,8 +552,9 @@ void LinePlotRenderer::render() gl.glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); gl.glEnable(GL_BLEND); gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (int i = 1; i < m_offsets.size(); i++) { - gl.glDrawArrays(GL_LINE_STRIP, m_offsets[i - 1], m_offsets[i] - m_offsets[i - 1]); + gl.glLineWidth(m_lineWidth); + for (int i = 0; i < m_offsets.size(); i += 2) { + gl.glDrawArrays(GL_LINE_STRIP, m_offsets[i], m_offsets[i + 1]); } gl.glDisable(GL_LINE_SMOOTH); gl.glDisable(GL_BLEND); @@ -517,23 +564,83 @@ void LinePlotRenderer::render() m_window->resetOpenGLState(); } -void LinePlotRenderer::copyPolylines(const GraphDrawing *gd) +void LinePlotRenderer::synchronize(QQuickFramebufferObject *item) { - if (!gd || gd->draw_order.empty()) { - return; + LinePlot *plot = static_cast<LinePlot *>(item); + + m_pointsChanged = plot->m_linesChanged; + m_valuesChanged = plot->m_valuesChanged; + m_colormapChanged = plot->m_colorScaleChanged; + + m_values = &(plot->values()); + m_cmap = &(plot->colorScale()); + m_lineWidth = plot->m_lineWidth; + m_window = plot->window(); + + if (plot->m_updateOffsets) { + plot->m_updateOffsets = false; + computeOffsets(plot); + } + + if (m_pointsChanged) { + copyPolylines(plot); + } + + plot->m_linesChanged = false; + plot->m_valuesChanged = false; + plot->m_colorScaleChanged = false; +} + +void LinePlotRenderer::computeOffsets(const LinePlot *plot) +{ + if (plot->m_brushedItem >= 0) { + computeBrushOffsets(plot); + } else { + computeAllOffsets(plot); } +} - int pointsNum = 0; +void LinePlotRenderer::computeBrushOffsets(const LinePlot *plot) +{ + int offset = 0; m_offsets.clear(); - m_offsets.reserve(gd->draw_order.size() + 1); - m_offsets.push_back(0); - for (auto &p : gd->draw_order) { - pointsNum += p.second->size(); + for (auto &p : plot->graphDrawing()->draw_order) { + int pointsNum = p.second->size(); + //int item1 = plot->m_indices[2*i]; + //int item2 = plot->m_indices[2*i + 1]; + //if (item1 == plot->m_brushedItem + // || item2 == plot->m_brushedItem) { + m_offsets.push_back(offset); + m_offsets.push_back(pointsNum); + //} + + offset += pointsNum; + } +} + +void LinePlotRenderer::computeAllOffsets(const LinePlot *plot) +{ + int offset = 0; + m_offsets.clear(); + m_offsets.reserve(2 * plot->graphDrawing()->draw_order.size()); + for (auto &p : plot->graphDrawing()->draw_order) { + int pointsNum = p.second->size(); + m_offsets.push_back(offset); m_offsets.push_back(pointsNum); + + offset += pointsNum; + } +} + +void LinePlotRenderer::copyPolylines(const LinePlot *plot) +{ + const GraphDrawing *gd = plot->graphDrawing(); + if (!gd || gd->draw_order.empty()) { + return; } m_points.clear(); - m_points.reserve(2 * pointsNum); + //m_points.reserve(2 * totalPoints); for (auto &p : gd->draw_order) { for (auto &point : *p.second) { m_points.push_back(point.x); @@ -542,27 +649,6 @@ void LinePlotRenderer::copyPolylines(const GraphDrawing *gd) } } -void LinePlotRenderer::synchronize(QQuickFramebufferObject *item) -{ - LinePlot *plot = static_cast<LinePlot *>(item); - - m_pointsChanged = plot->linesChanged(); - m_valuesChanged = plot->valuesChanged(); - m_colormapChanged = plot->colorScaleChanged(); - - if (m_pointsChanged) { - copyPolylines(plot->graphDrawing()); - } - m_values = &(plot->values()); - m_cmap = &(plot->colorScale()); - m_window = plot->window(); - - // Reset so that we have the correct values by the next synchronize() - plot->setLinesChanged(false); - plot->setValuesChanged(false); - plot->setColorScaleChanged(false); -} - void LinePlotRenderer::updatePoints() { gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[0]); @@ -2,6 +2,7 @@ #define LINEPLOT_H #include <memory> +#include <set> #include <vector> #include <QQuickFramebufferObject> @@ -14,6 +15,9 @@ #include "colorscale.h" #include "scale.h" +// (private) Implementation of QQuickFramebufferObject::Renderer +class LinePlotRenderer; + class LinePlot : public QQuickFramebufferObject { @@ -36,6 +40,7 @@ class LinePlot Q_PROPERTY(float advectionSpeed READ advectionSpeed WRITE setAdvectionSpeed NOTIFY advectionSpeedChanged) Q_PROPERTY(float relaxation READ relaxation WRITE setRelaxation NOTIFY relaxationChanged) Q_PROPERTY(bool bundleGPU READ bundleGPU WRITE setBundleGPU NOTIFY bundleGPUChanged) + Q_PROPERTY(float lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged) public: static const int PADDING = 20; @@ -45,6 +50,8 @@ public: void setColorScale(const ColorScale *scale); const GraphDrawing *graphDrawing() const { return &m_gdFinal; } + const arma::uvec &indices() const { return m_indices; } + const arma::mat &points() const { return m_Y; } const std::vector<float> &values() const { return m_values; } const std::vector<float> &colorScale() const { return m_cmap; } LinearScale<float> scaleX() const { return m_sx; } @@ -91,17 +98,22 @@ public: float advectionSpeed() const { return m_advectionSpeed; } float relaxation() const { return m_relaxation; } bool bundleGPU() const { return m_bundleGPU; } + float lineWidth() const { return m_lineWidth; } void setEdgeSampling(float edgeSampling); void setAdvectionSpeed(float advectionSpeed); void setRelaxation(float relaxation); void setBundleGPU(bool bundleGPU); + void setLineWidth(float lineWidth); signals: - void linesChanged(const arma::mat &xy); + void indicesChanged(const arma::uvec &indices); + void pointsChanged(const arma::mat &Y); void valuesChanged(const arma::vec &values) const; void scaleChanged(const LinearScale<float> &sx, const LinearScale<float> &sy) const; + void itemBrushed(int item) const; + void selectionChanged(const std::vector<bool> &selection) const; // Q_PROPERTY's void iterationsChanged(int iterations) const; @@ -118,6 +130,7 @@ signals: void advectionSpeedChanged(float advectionSpeed) const; void relaxationChanged(float relaxation) const; void bundleGPUChanged(bool bundleGPU) const; + void lineWidthChanged(float lineWidth) const; public slots: // Lines are two consecutive elements in 'indices' (ref. points in 'Y') @@ -129,12 +142,17 @@ public slots: void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy); + void brushItem(int item); + void setSelection(const std::vector<bool> &selection); + private: + void buildGraph(); void bundle(); void relax(); // Data - arma::mat m_lines; + arma::uvec m_indices; + arma::mat m_Y; std::vector<float> m_values; // Visuals @@ -144,7 +162,10 @@ private: GraphDrawing m_gdBundle, m_gdFinal; // Internal state - bool m_linesChanged, m_valuesChanged, m_colorScaleChanged; + int m_brushedItem; + std::vector<bool> m_selection; + bool m_anySelected; + bool m_linesChanged, m_valuesChanged, m_colorScaleChanged, m_updateOffsets; // Q_PROPERTY's int m_iterations; @@ -161,6 +182,9 @@ private: float m_advectionSpeed; float m_relaxation; bool m_bundleGPU; + float m_lineWidth; + + friend class LinePlotRenderer; }; #endif // LINEPLOT_H @@ -43,6 +43,32 @@ arma::uvec extractCPs(const arma::mat &X) return indices.subvec(0, numCPs-1); } +void overviewBundles(const Main *m) +{ + const arma::mat &unreliability = m->projectionHistory->unreliability(); + arma::uvec indicesLargest = arma::sort_index(unreliability, "descending"); + arma::uword numLargest = m->projectionHistory->Y().n_rows * 0.1f; + indicesLargest = indicesLargest.subvec(0, numLargest-1); + m->bundlePlot->setValues(unreliability(indicesLargest)); + + const arma::uvec &cpIndices = m->projectionHistory->cpIndices(); + arma::uvec CPs = cpIndices(indicesLargest / unreliability.n_rows); + + const arma::uvec &rpIndices = m->projectionHistory->rpIndices(); + arma::uvec RPs = indicesLargest; + RPs.transform([&unreliability](arma::uword v) { + return v % unreliability.n_rows; + }); + RPs = rpIndices(RPs); + + arma::uvec indices(CPs.n_elem + RPs.n_elem); + for (arma::uword i = 0; i < CPs.n_elem; i++) { + indices[2*i + 0] = CPs(i); + indices[2*i + 1] = RPs(i); + } + m->bundlePlot->setLines(indices, m->projectionHistory->Y()); +} + int main(int argc, char **argv) { QApplication app(argc, argv); @@ -201,14 +227,47 @@ int main(int argc, char **argv) QObject::connect(m->projectionHistory, &ProjectionHistory::currentMapChanged, [m](const arma::mat &Y) { // ... and bundling + overviewBundles(m); + }); + + // Linking between selections + SelectionHandler cpSelectionHandler(cpIndices.n_elem); + QObject::connect(m->cpPlot, &Scatterplot::selectionInteractivelyChanged, + &cpSelectionHandler, &SelectionHandler::setSelection); + QObject::connect(m->cpBarChart, &BarChart::selectionInteractivelyChanged, + &cpSelectionHandler, &SelectionHandler::setSelection); + QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, + m->cpPlot, &Scatterplot::setSelection); + QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, + m->cpBarChart, &BarChart::setSelection); + QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, + [m](const std::vector<bool> &cpSelection) { + // given some CPs, see unexpected RPs *influenced by* them + std::vector<arma::uword> selectedCPIndices; + for (int i = 0; i < cpSelection.size(); i++) { + if (cpSelection[i]) { + selectedCPIndices.push_back(i); + } + } + + if (selectedCPIndices.empty()) { + overviewBundles(m); + return; + } + + arma::uvec selectedCPs(selectedCPIndices.size()); + std::copy(selectedCPIndices.begin(), selectedCPIndices.end(), + selectedCPs.begin()); + + // Only the 10% largest values, filtered by the selected RPs const arma::mat &unreliability = m->projectionHistory->unreliability(); - arma::uvec indicesLargest = arma::sort_index(unreliability, "descending"); - auto numLargest = Y.n_rows * 0.1f; + arma::uvec indicesLargest = arma::sort_index(unreliability.cols(selectedCPs), "descending"); + arma::uword numLargest = indicesLargest.n_elem * 0.1f; indicesLargest = indicesLargest.subvec(0, numLargest-1); m->bundlePlot->setValues(unreliability(indicesLargest)); const arma::uvec &cpIndices = m->projectionHistory->cpIndices(); - arma::uvec CPs = cpIndices(indicesLargest / unreliability.n_rows); + arma::uvec CPs = cpIndices(selectedCPs(indicesLargest / unreliability.n_rows)); const arma::uvec &rpIndices = m->projectionHistory->rpIndices(); arma::uvec RPs = indicesLargest; @@ -219,23 +278,12 @@ int main(int argc, char **argv) arma::uvec indices(CPs.n_elem + RPs.n_elem); for (arma::uword i = 0; i < CPs.n_elem; i++) { - indices(2*i + 0) = CPs(i); - indices(2*i + 1) = RPs(i); + indices[2*i + 0] = CPs(i); + indices[2*i + 1] = RPs(i); } - m->bundlePlot->setLines(indices, Y); + m->bundlePlot->setLines(indices, m->projectionHistory->Y()); }); - // Linking between selections - SelectionHandler cpSelectionHandler(cpIndices.n_elem); - QObject::connect(m->cpPlot, &Scatterplot::selectionInteractivelyChanged, - &cpSelectionHandler, &SelectionHandler::setSelection); - QObject::connect(m->cpBarChart, &BarChart::selectionInteractivelyChanged, - &cpSelectionHandler, &SelectionHandler::setSelection); - QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, - m->cpPlot, &Scatterplot::setSelection); - QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, - m->cpBarChart, &BarChart::setSelection); - SelectionHandler rpSelectionHandler(X.n_rows - cpIndices.n_elem); QObject::connect(m->rpPlot, &Scatterplot::selectionInteractivelyChanged, &rpSelectionHandler, &SelectionHandler::setSelection); @@ -245,6 +293,49 @@ int main(int argc, char **argv) m->rpPlot, &Scatterplot::setSelection); QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged, m->rpBarChart, &BarChart::setSelection); + QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged, + [m](const std::vector<bool> &rpSelection) { + // given some RPs, see unexpected CPs *influencing* them + std::vector<arma::uword> selectedRPIndices; + for (int i = 0; i < rpSelection.size(); i++) { + if (rpSelection[i]) { + selectedRPIndices.push_back(i); + } + } + + if (selectedRPIndices.empty()) { + overviewBundles(m); + return; + } + + arma::uvec selectedRPs(selectedRPIndices.size()); + std::copy(selectedRPIndices.begin(), selectedRPIndices.end(), + selectedRPs.begin()); + + // Only the 10% largest values, filtered by the selected RPs + const arma::mat &unreliability = m->projectionHistory->unreliability(); + arma::uvec indicesLargest = arma::sort_index(unreliability.rows(selectedRPs), "descending"); + arma::uword numLargest = indicesLargest.n_elem * 0.1f; + indicesLargest = indicesLargest.subvec(0, numLargest-1); + m->bundlePlot->setValues(unreliability(indicesLargest)); + + const arma::uvec &cpIndices = m->projectionHistory->cpIndices(); + arma::uvec CPs = cpIndices(indicesLargest / selectedRPs.n_elem); + + const arma::uvec &rpIndices = m->projectionHistory->rpIndices(); + arma::uvec RPs = indicesLargest; + RPs.transform([&selectedRPs](arma::uword v) { + return v % selectedRPs.n_elem; + }); + RPs = rpIndices(selectedRPs(RPs)); + + arma::uvec indices(CPs.n_elem + RPs.n_elem); + for (arma::uword i = 0; i < CPs.n_elem; i++) { + indices[2*i + 0] = CPs(i); + indices[2*i + 1] = RPs(i); + } + m->bundlePlot->setLines(indices, m->projectionHistory->Y()); + }); // Brushing between each bar chart and respective scatterplot BrushingHandler cpBrushHandler; @@ -304,16 +395,17 @@ int main(int argc, char **argv) QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesChanged, m->rpBarChart, &BarChart::setValues); - // Recompute values whenever selection changes + // ProjectionHistory takes special care of separate CP/RP selections QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged, m->projectionHistory, &ProjectionHistory::setCPSelection); QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged, m->projectionHistory, &ProjectionHistory::setRPSelection); + //QObject::connect(m->projectionHistory, &ProjectionHistory::selectionChanged, + // m->bundlePlot, LinePlot::selectionChanged); // Connect projection components to rewinding mechanism QObject::connect(plotTC, &TransitionControl::tChanged, m->projectionHistory, &ProjectionHistory::setRewind); - QObject::connect(m->projectionHistory, &ProjectionHistory::mapRewound, m, &Main::updateMap); QObject::connect(m->projectionHistory, &ProjectionHistory::cpValuesRewound, diff --git a/main_view.qml b/main_view.qml index 60a85aa..ea93f77 100644 --- a/main_view.qml +++ b/main_view.qml @@ -593,6 +593,16 @@ ApplicationWindow { GridLayout { columns: 2 + Label { text: "Line width:" } + SpinBox { + maximumValue: 100 + minimumValue: 1 + decimals: 1 + stepSize: 1 + value: bundlePlot.lineWidth + onValueChanged: bundlePlot.lineWidth = this.value + } + Label { text: "Opacity:" } Slider { tickmarksEnabled: true diff --git a/projectionhistory.cpp b/projectionhistory.cpp index 20a006b..e57ef41 100644 --- a/projectionhistory.cpp +++ b/projectionhistory.cpp @@ -105,6 +105,8 @@ void ProjectionHistory::addMap(const arma::mat &Y) m_firstY = m_Y; m_firstDistY = m_distY; m_firstValues = m_values; + + m_selection.assign(m_values.n_elem, false); } emit currentMapChanged(m_Y); @@ -135,14 +137,26 @@ bool ProjectionHistory::setType(ObserverType type) void ProjectionHistory::setCPSelection(const std::vector<bool> &cpSelection) { + if (cpSelection.size() != m_cpIndices.n_elem) { + return; + } + m_cpSelection.clear(); for (int i = 0; i < cpSelection.size(); i++) { + m_selection[m_cpIndices[i]] = cpSelection[i]; if (cpSelection[i]) { m_cpSelection.push_back(i); } } m_cpSelectionEmpty = m_cpSelection.empty(); + cpSelectionPostProcess(); + + emit selectionChanged(m_selection); +} + +void ProjectionHistory::cpSelectionPostProcess() +{ if (!m_cpSelectionEmpty) { // compute the influence of CP selection on each RP for (arma::uword rp = 0; rp < m_rpIndices.n_elem; rp++) { @@ -157,18 +171,31 @@ void ProjectionHistory::setCPSelection(const std::vector<bool> &cpSelection) } else { emitValuesChanged(); } + + // TODO: emit cpSelectionChanged()? } void ProjectionHistory::setRPSelection(const std::vector<bool> &rpSelection) { + if (rpSelection.size() != m_rpIndices.n_elem) { + return; + } + m_rpSelection.clear(); for (int i = 0; i < rpSelection.size(); i++) { + m_selection[m_rpIndices[i]] = rpSelection[i]; if (rpSelection[i]) { m_rpSelection.push_back(i); } } m_rpSelectionEmpty = m_rpSelection.empty(); + rpSelectionPostProcess(); + emit selectionChanged(m_selection); +} + +void ProjectionHistory::rpSelectionPostProcess() +{ if (!m_rpSelectionEmpty) { // compute how influent is each CP on RP selection for (arma::uword cp = 0; cp < m_cpIndices.n_elem; cp++) { @@ -182,6 +209,33 @@ void ProjectionHistory::setRPSelection(const std::vector<bool> &rpSelection) } else { emit cpValuesChanged(arma::vec(), false); } + + // TODO: emit rpSelectionChanged()? +} + +void ProjectionHistory::setSelection(const std::vector<bool> &selection) +{ + m_selection = selection; + + m_rpSelection.clear(); + for (auto i: m_rpIndices) { + if (m_selection[i]) { + m_rpSelection.push_back(i); + } + } + m_rpSelectionEmpty = m_rpSelection.empty(); + rpSelectionPostProcess(); + + m_cpSelection.clear(); + for (auto i: m_cpIndices) { + if (m_selection[i]) { + m_cpSelection.push_back(i); + } + } + m_cpSelectionEmpty = m_cpSelection.empty(); + cpSelectionPostProcess(); + + emit selectionChanged(m_selection); } bool ProjectionHistory::emitValuesChanged() const diff --git a/projectionhistory.h b/projectionhistory.h index e20f16c..4fc2ef4 100644 --- a/projectionhistory.h +++ b/projectionhistory.h @@ -1,6 +1,8 @@ #ifndef PROJECTIONHISTORY_H #define PROJECTIONHISTORY_H +#include <vector> + #include <QObject> #include <armadillo> @@ -46,12 +48,17 @@ signals: void cpValuesRewound(const arma::vec &values) const; void rpValuesRewound(const arma::vec &values) const; + void cpSelectionChanged(const std::vector<bool> &cpSelection) const; + void rpSelectionChanged(const std::vector<bool> &rpSelection) const; + void selectionChanged(const std::vector<bool> &selection) const; + public slots: void addMap(const arma::mat &Y); bool setType(ObserverType type); void setCPSelection(const std::vector<bool> &cpSelection); void setRPSelection(const std::vector<bool> &rpSelection); + void setSelection(const std::vector<bool> &selection); void setRewind(double t); @@ -59,6 +66,9 @@ private: bool emitValuesChanged() const; void updateUnreliability(); + void cpSelectionPostProcess(); + void rpSelectionPostProcess(); + ObserverType m_type; arma::mat m_X, m_Y, m_firstY, m_prevY; @@ -68,6 +78,7 @@ private: bool m_cpSelectionEmpty, m_rpSelectionEmpty; std::vector<int> m_cpSelection, m_rpSelection; + std::vector<bool> m_selection; // alpha(i, j): the influence CP j has on RP i void computeAlphas(); |