aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-01-25 19:55:52 +0100
committerSamuel Fadel <samuelfadel@gmail.com>2016-01-25 19:55:52 +0100
commit60a0bfb863aa2a05bafd943423c284f5c2d68863 (patch)
treef6faae437cbdd6b10a37a64161865f69650b13e1
parent6adb0f62c7a51b77725dc5cfe37ba59ab8f8cc73 (diff)
Scatterplots & splat now share the same scaling.
-rw-r--r--interactionhandler.cpp42
-rw-r--r--interactionhandler.h37
-rw-r--r--main.cpp99
-rw-r--r--main_view.qml13
-rw-r--r--manipulationhandler.cpp53
-rw-r--r--manipulationhandler.h37
-rw-r--r--mapscalehandler.cpp15
-rw-r--r--mapscalehandler.h32
-rw-r--r--numericrange.h79
-rw-r--r--pm.pro7
-rw-r--r--projectionobserver.cpp14
11 files changed, 296 insertions, 132 deletions
diff --git a/interactionhandler.cpp b/interactionhandler.cpp
deleted file mode 100644
index 64304c7..0000000
--- a/interactionhandler.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "interactionhandler.h"
-
-#include "mp.h"
-
-InteractionHandler::InteractionHandler(const arma::mat &X,
- const arma::uvec &cpIndices)
- : m_X(X)
- , m_Y(X.n_rows, 2)
- , m_cpIndices(cpIndices)
- , m_technique(TECHNIQUE_LAMP)
-{
-}
-
-void InteractionHandler::setTechnique(InteractionHandler::Technique technique)
-{
- if (m_technique == technique)
- return;
-
- m_technique = technique;
-}
-
-void InteractionHandler::setCP(const arma::mat &Ys)
-{
- switch (m_technique) {
- case TECHNIQUE_PLMP:
- mp::plmp(m_X, m_cpIndices, Ys, m_Y);
- break;
- case TECHNIQUE_LSP:
- // TODO
- // mp::lsp(m_X, m_cpIndices, Ys, m_Y);
- break;
- case TECHNIQUE_LAMP:
- mp::lamp(m_X, m_cpIndices, Ys, m_Y);
- break;
- case TECHNIQUE_PEKALSKA:
- // TODO
- // mp::pekalska(m_X, m_cpIndices, Ys, m_Y);
- break;
- }
-
- emit cpChanged(m_Y);
-}
diff --git a/interactionhandler.h b/interactionhandler.h
deleted file mode 100644
index ace192a..0000000
--- a/interactionhandler.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef INTERACTIONHANDLER_H
-#define INTERACTIONHANDLER_H
-
-#include <QObject>
-#include <armadillo>
-
-class InteractionHandler : public QObject
-{
- Q_OBJECT
-public:
- Q_ENUMS(Technique)
- enum Technique {
- TECHNIQUE_PLMP,
- TECHNIQUE_LAMP,
- TECHNIQUE_LSP,
- TECHNIQUE_PEKALSKA
- };
-
- InteractionHandler(const arma::mat &X, const arma::uvec &cpIndices);
- void setTechnique(Technique technique);
-
-signals:
- void cpChanged(const arma::mat &Y);
-
-public slots:
- void setCP(const arma::mat &Ys);
-
-protected:
- InteractionHandler() {}
-
-private:
- arma::mat m_X, m_Y;
- arma::uvec m_cpIndices;
- Technique m_technique;
-};
-
-#endif // INTERACTIONHANDLER_H
diff --git a/main.cpp b/main.cpp
index 6af65b3..34eb240 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,6 +1,6 @@
#include <cmath>
#include <iostream>
-#include <memory>
+#include <numeric>
#include <string>
#include <QApplication>
@@ -16,7 +16,8 @@
#include "historygraph.h"
#include "barchart.h"
#include "colormap.h"
-#include "interactionhandler.h"
+#include "manipulationhandler.h"
+#include "mapscalehandler.h"
#include "selectionhandler.h"
#include "projectionobserver.h"
#include "skelft.h"
@@ -71,6 +72,7 @@ int main(int argc, char **argv)
arma::uvec cpIndices;
arma::mat Ys;
+ // Load/generate indices
QString indicesFilename;
if (parser.isSet(indicesFileOutputOption)) {
indicesFilename = parser.value(indicesFileOutputOption);
@@ -83,8 +85,7 @@ int main(int argc, char **argv)
cpIndices = extractCPs(X);
}
- arma::sort(cpIndices);
-
+ // Load/generate CPs
QString cpFilename;
if (parser.isSet(cpFileOutputOption)) {
cpFilename = parser.value(cpFileOutputOption);
@@ -97,11 +98,17 @@ int main(int argc, char **argv)
Ys.set_size(cpIndices.n_elem, 2);
mp::forceScheme(mp::dist(X.rows(cpIndices)), Ys);
}
+
if (cpIndices.n_elem != Ys.n_rows) {
std::cerr << "The number of CP indices and the CP map do not match." << std::endl;
return 1;
}
+ // Sort indices so some operations become easier later
+ arma::uvec cpSortedIndices = arma::sort_index(cpIndices);
+ cpIndices = cpIndices(cpSortedIndices);
+ Ys = Ys.rows(cpSortedIndices);
+
m->setCPIndices(cpIndices);
m->setCP(Ys);
@@ -110,7 +117,6 @@ int main(int argc, char **argv)
qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
qmlRegisterType<Colormap>("PM", 1, 0, "Colormap");
- qmlRegisterType<InteractionHandler>("PM", 1, 0, "InteractionHandler");
qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
// Set up multisampling
@@ -126,15 +132,13 @@ int main(int argc, char **argv)
QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml"));
+ // Initialize pointers to visual components
m->cpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("cpPlot");
- m->cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
-
m->rpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("rpPlot");
-
- m->splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat");
- skelft2DInitialization(m->splat->width());
-
m->colormap = engine.rootObjects()[0]->findChild<Colormap *>("colormap");
+ m->splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat");
+ m->cpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("cpBarChart");
+ m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart");
// Keep track of the current cp (in order to save them later, if requested)
QObject::connect(m->cpPlot, SIGNAL(xyChanged(const arma::mat &)),
@@ -143,15 +147,16 @@ int main(int argc, char **argv)
m, SLOT(setCP(const arma::mat &)));
// Update projection as the cp are modified
- InteractionHandler interactionHandler(X, cpIndices);
- interactionHandler.setTechnique(InteractionHandler::TECHNIQUE_LAMP);
- QObject::connect(m->cpPlot, SIGNAL(xyChanged(const arma::mat &)),
- &interactionHandler, SLOT(setCP(const arma::mat &)));
+ ManipulationHandler manipulationHandler(X, cpIndices);
+ //QObject::connect(m->cpPlot, SIGNAL(xyChanged(const arma::mat &)),
+ // &manipulationHandler, SLOT(setCP(const arma::mat &)));
QObject::connect(m->cpPlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)),
- &interactionHandler, SLOT(setCP(const arma::mat &)));
- QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
+ &manipulationHandler, SLOT(setCP(const arma::mat &)));
+ QObject::connect(&manipulationHandler, SIGNAL(cpChanged(const arma::mat &)),
+ m->cpPlot, SLOT(setXY(const arma::mat &)));
+ QObject::connect(&manipulationHandler, SIGNAL(rpChanged(const arma::mat &)),
m->rpPlot, SLOT(setXY(const arma::mat &)));
- QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
+ QObject::connect(&manipulationHandler, SIGNAL(rpChanged(const arma::mat &)),
m->splat, SLOT(setSites(const arma::mat &)));
// Connections between history graph and cp plot
@@ -161,20 +166,20 @@ int main(int argc, char **argv)
//QObject::connect(history, SIGNAL(currentItemChanged(const arma::mat &)),
// cpPlot, SLOT(setXY(const arma::mat &)));
- QObject::connect(m->rpPlot, SIGNAL(scaleChanged(const LinearScale<float> &, const LinearScale<float> &)),
+ // Keep both scatterplots scaled equally and to the full plot
+ MapScaleHandler mapScaleHandler;
+ QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale<float> &, const LinearScale<float> &)),
m->cpPlot, SLOT(setScale(const LinearScale<float> &, const LinearScale<float> &)));
+ QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale<float> &, const LinearScale<float> &)),
+ m->rpPlot, SLOT(setScale(const LinearScale<float> &, const LinearScale<float> &)));
+ QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale<float> &, const LinearScale<float> &)),
+ m->splat, SLOT(setScale(const LinearScale<float> &, const LinearScale<float> &)));
+ QObject::connect(&manipulationHandler, SIGNAL(mapChanged(const arma::mat &)),
+ &mapScaleHandler, SLOT(scaleToMap(const arma::mat &)));
QObject::connect(m->splat, SIGNAL(colorScaleChanged(const ColorScale &)),
m->colormap, SLOT(setColorScale(const ColorScale &)));
- m->cpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("cpBarChart");
- m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
- m->setCPBarChartColorScale(Main::ColorScaleContinuous);
-
- m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart");
- m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
- m->setRPBarChartColorScale(Main::ColorScaleContinuous);
-
// Linking between selections
SelectionHandler cpSelectionHandler(cpIndices.n_elem);
QObject::connect(m->cpPlot, SIGNAL(selectionInteractivelyChanged(const std::vector<bool> &)),
@@ -184,36 +189,46 @@ int main(int argc, char **argv)
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> &)));
+ 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> &)));
+ // Recompute values whenever projection changes
ProjectionObserver projectionObserver(X, cpIndices);
m->projectionObserver = &projectionObserver;
- QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
+ QObject::connect(&manipulationHandler, SIGNAL(mapChanged(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 &)),
+ QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(const arma::vec &)),
+ m->rpPlot, 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 &)),
m->cpBarChart, SLOT(setValues(const arma::vec &)));
- //QObject::connect(m->projectionObserver, SIGNAL(rpValuesChanged(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 &)));
+
+ skelft2DInitialization(m->splat->width());
+
+ m->cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
+ m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
+ m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton);
m->setColormapColorScale(Main::ColorScaleContinuous);
m->setCPPlotColorScale(Main::ColorScaleContinuous);
m->setRPPlotColorScale(Main::ColorScaleContinuous);
m->setSplatColorScale(Main::ColorScaleContinuous);
+ m->setCPBarChartColorScale(Main::ColorScaleContinuous);
+ m->setRPBarChartColorScale(Main::ColorScaleContinuous);
m->cpPlot->setAutoScale(false);
+ m->rpPlot->setAutoScale(false);
m->cpPlot->setColorData(labels(cpIndices), false);
- m->cpPlot->setXY(Ys, false);
- m->cpPlot->update();
+
+ manipulationHandler.setCP(Ys);
auto ret = app.exec();
diff --git a/main_view.qml b/main_view.qml
index 4c5c7a7..56e3f59 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -125,6 +125,12 @@ ApplicationWindow {
id: cpBarChart
objectName: "cpBarChart"
anchors.fill: parent
+
+ Label {
+ width: parent.width
+ horizontalAlignment: Text.AlignRight
+ text: "Control points"
+ }
}
//HistoryGraph {
@@ -156,6 +162,13 @@ ApplicationWindow {
id: rpBarChart
objectName: "rpBarChart"
anchors.fill: parent
+
+ Label {
+ anchors.margins: 5
+ width: parent.width
+ horizontalAlignment: Text.AlignRight
+ text: "Regular points"
+ }
}
//HistoryGraph {
diff --git a/manipulationhandler.cpp b/manipulationhandler.cpp
new file mode 100644
index 0000000..bb0e043
--- /dev/null
+++ b/manipulationhandler.cpp
@@ -0,0 +1,53 @@
+#include "manipulationhandler.h"
+
+#include <algorithm>
+#include <iostream>
+#include <numeric>
+
+#include "mp.h"
+#include "numericrange.h"
+
+ManipulationHandler::ManipulationHandler(const arma::mat &X,
+ const arma::uvec &cpIndices)
+ : m_X(X)
+ , m_Y(X.n_rows, 2)
+ , m_cpIndices(cpIndices)
+ , m_rpIndices(X.n_rows - cpIndices.n_elem)
+ , m_technique(TECHNIQUE_LAMP)
+{
+ NumericRange<unsigned long long> range(0, m_X.n_rows);
+ std::set_symmetric_difference(range.cbegin(), range.cend(),
+ m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin());
+}
+
+void ManipulationHandler::setTechnique(ManipulationHandler::Technique technique)
+{
+ if (m_technique == technique)
+ return;
+
+ m_technique = technique;
+}
+
+void ManipulationHandler::setCP(const arma::mat &Ys)
+{
+ switch (m_technique) {
+ case TECHNIQUE_PLMP:
+ mp::plmp(m_X, m_cpIndices, Ys, m_Y);
+ break;
+ case TECHNIQUE_LSP:
+ // TODO
+ // mp::lsp(m_X, m_cpIndices, Ys, m_Y);
+ break;
+ case TECHNIQUE_LAMP:
+ mp::lamp(m_X, m_cpIndices, Ys, m_Y);
+ break;
+ case TECHNIQUE_PEKALSKA:
+ // TODO
+ // mp::pekalska(m_X, m_cpIndices, Ys, m_Y);
+ break;
+ }
+
+ emit cpChanged(m_Y.rows(m_cpIndices));
+ emit rpChanged(m_Y.rows(m_rpIndices));
+ emit mapChanged(m_Y);
+}
diff --git a/manipulationhandler.h b/manipulationhandler.h
new file mode 100644
index 0000000..78ca1c9
--- /dev/null
+++ b/manipulationhandler.h
@@ -0,0 +1,37 @@
+#ifndef MANIPULATIONHANDLER_H
+#define MANIPULATIONHANDLER_H
+
+#include <QObject>
+#include <armadillo>
+
+class ManipulationHandler
+ : public QObject
+{
+ Q_OBJECT
+public:
+ Q_ENUMS(Technique)
+ enum Technique {
+ TECHNIQUE_PLMP,
+ TECHNIQUE_LAMP,
+ TECHNIQUE_LSP,
+ TECHNIQUE_PEKALSKA
+ };
+
+ ManipulationHandler(const arma::mat &X, const arma::uvec &cpIndices);
+ void setTechnique(Technique technique);
+
+signals:
+ void cpChanged(const arma::mat &cpY) const;
+ void rpChanged(const arma::mat &rpY) const;
+ void mapChanged(const arma::mat &Y) const;
+
+public slots:
+ void setCP(const arma::mat &Ys);
+
+private:
+ arma::mat m_X, m_Y;
+ arma::uvec m_cpIndices, m_rpIndices;
+ Technique m_technique;
+};
+
+#endif // MANIPULATIONHANDLER_H
diff --git a/mapscalehandler.cpp b/mapscalehandler.cpp
new file mode 100644
index 0000000..9e25c29
--- /dev/null
+++ b/mapscalehandler.cpp
@@ -0,0 +1,15 @@
+#include "mapscalehandler.h"
+
+MapScaleHandler::MapScaleHandler()
+ : m_sx(0.0f, 1.0f, 0.0f, 1.0f)
+ , m_sy(0.0f, 1.0f, 0.0f, 1.0f)
+{
+}
+
+void MapScaleHandler::scaleToMap(const arma::mat &Y)
+{
+ m_sx.setDomain(Y.col(0).min(), Y.col(0).max());
+ m_sy.setDomain(Y.col(1).min(), Y.col(1).max());
+
+ emit scaleChanged(m_sx, m_sy);
+}
diff --git a/mapscalehandler.h b/mapscalehandler.h
new file mode 100644
index 0000000..3cd8dbd
--- /dev/null
+++ b/mapscalehandler.h
@@ -0,0 +1,32 @@
+#ifndef MAPSCALEHANDLER_H
+#define MAPSCALEHANDLER_H
+
+#include <QObject>
+#include <armadillo>
+
+#include "scale.h"
+
+class MapScaleHandler
+ : public QObject
+{
+ Q_OBJECT
+public:
+ MapScaleHandler();
+
+ void getScales(LinearScale<float> &sx, LinearScale<float> &sy) const {
+ sx = m_sx;
+ sy = m_sy;
+ }
+
+signals:
+ void scaleChanged(const LinearScale<float> &sx,
+ const LinearScale<float> &sy) const;
+
+public slots:
+ void scaleToMap(const arma::mat &Y);
+
+private:
+ LinearScale<float> m_sx, m_sy;
+};
+
+#endif // MAPSCALEHANDLER_H
diff --git a/numericrange.h b/numericrange.h
new file mode 100644
index 0000000..d86e578
--- /dev/null
+++ b/numericrange.h
@@ -0,0 +1,79 @@
+#ifndef NUMERICRANGE_H
+#define NUMERICRANGE_H
+
+#include <iterator>
+#include <stdexcept>
+
+/*
+ * A range in [first, last) in steps of 1.
+ */
+template<typename T>
+class NumericRange
+{
+public:
+ class iterator
+ {
+ friend class NumericRange;
+ public:
+ typedef iterator self_type;
+ typedef T value_type;
+ typedef T& reference;
+ typedef T* pointer;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef int difference_type;
+
+ iterator(const T &first): m_value(first) {}
+
+ T operator *() const { return m_value; }
+ const iterator &operator ++() {
+ ++m_value;
+ return *this;
+ }
+ iterator operator++(int) {
+ iterator copy(*this);
+ ++m_value;
+ return copy;
+ }
+ const iterator &operator --() {
+ --m_value;
+ return *this;
+ }
+ iterator operator--(int) {
+ iterator copy(*this);
+ --m_value;
+ return copy;
+ }
+
+ bool operator ==(const iterator &other) const {
+ return m_value == other.m_value;
+ }
+ bool operator !=(const iterator &other) const {
+ return m_value != other.m_value;
+ }
+
+ private:
+ T m_value;
+ };
+
+ NumericRange(const T &first, const T &last)
+ : m_begin(first)
+ , m_end(last)
+ {
+ if (first > last) {
+ throw std::logic_error("first > last");
+ }
+ }
+
+ typedef const iterator const_iterator;
+
+ iterator begin() const { return m_begin; }
+ iterator end() const { return m_end; }
+
+ const_iterator cbegin() const { return m_begin; }
+ const_iterator cend() const { return m_end; }
+
+private:
+ iterator m_begin, m_end;
+};
+
+#endif // NUMERICRANGE_H
diff --git a/pm.pro b/pm.pro
index f864b96..5678c22 100644
--- a/pm.pro
+++ b/pm.pro
@@ -14,7 +14,9 @@ HEADERS += main.h \
colormap.h \
historygraph.h \
barchart.h \
- interactionhandler.h \
+ manipulationhandler.h \
+ mapscalehandler.h \
+ numericrange.h \
selectionhandler.h \
projectionobserver.h \
skelft.h \
@@ -29,7 +31,8 @@ SOURCES += main.cpp \
colormap.cpp \
historygraph.cpp \
barchart.cpp \
- interactionhandler.cpp \
+ manipulationhandler.cpp \
+ mapscalehandler.cpp \
selectionhandler.cpp \
projectionobserver.cpp \
skelft_core.cpp \
diff --git a/projectionobserver.cpp b/projectionobserver.cpp
index 30f8325..395110c 100644
--- a/projectionobserver.cpp
+++ b/projectionobserver.cpp
@@ -2,10 +2,10 @@
#include <algorithm>
#include <cmath>
-
-#include <QDebug>
+#include <numeric>
#include "mp.h"
+#include "numericrange.h"
static const float EPSILON = 1e-6f;
@@ -42,13 +42,9 @@ ProjectionObserver::ProjectionObserver(const arma::mat &X,
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());
+ NumericRange<unsigned long long> range(0, m_X.n_rows);
+ std::set_symmetric_difference(range.cbegin(), range.cend(),
+ m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin());
}
bool ProjectionObserver::setType(int type)