aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-04-13 12:20:01 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2016-04-13 12:20:01 -0300
commit3c92c6256fd796f24db177caf813ba52e2389ff8 (patch)
tree4f9f0c3c08c07eb2ffcaa868a3f57ff4dcc66beb
parent11a58ca2c92fc3c44daa1643fbc5148bf96b407c (diff)
Bundling now supports selection.
Selection is implemented via main() updating the line plot using the current active selection. Beware of bug (issue #23). Also adds support for setting the line width, while changing the default line width to 2 pixels wide. ProjectionHistory: now reports changes to selections relative to the whole dataset, not only CPs/RPs.
-rw-r--r--lineplot.cpp222
-rw-r--r--lineplot.h30
-rw-r--r--main.cpp130
-rw-r--r--main_view.qml10
-rw-r--r--projectionhistory.cpp54
-rw-r--r--projectionhistory.h11
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]);
diff --git a/lineplot.h b/lineplot.h
index 422bd2e..61e5988 100644
--- a/lineplot.h
+++ b/lineplot.h
@@ -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
diff --git a/main.cpp b/main.cpp
index bf9042a..8cc9323 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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();