aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-02-10 18:31:48 -0200
committerSamuel Fadel <samuelfadel@gmail.com>2016-02-10 18:31:48 -0200
commitb45a21976bece19da81166324dc1cc4260a0e0f4 (patch)
tree2f21da285edb214766fa559081b76a6754cfa8b0
parent8d4e5ff43fd9d51dc05d8d2dd87f69ab35bee423 (diff)
Added ProjectionHistory object for history tracking.
-rw-r--r--main.cpp14
-rw-r--r--main.h7
-rw-r--r--manipulationhandler.cpp49
-rw-r--r--manipulationhandler.h13
-rw-r--r--pm.pro2
-rw-r--r--projectionhistory.cpp45
-rw-r--r--projectionhistory.h36
-rw-r--r--projectionobserver.cpp50
-rw-r--r--projectionobserver.h10
9 files changed, 154 insertions, 72 deletions
diff --git a/main.cpp b/main.cpp
index 3e62963..81362a6 100644
--- a/main.cpp
+++ b/main.cpp
@@ -139,13 +139,13 @@ int main(int argc, char **argv)
fmt.setSamples(8);
QSurfaceFormat::setDefaultFormat(fmt);
+ // Register our custom QML types & init QML engine
qmlRegisterType<Scatterplot>("PM", 1, 0, "Scatterplot");
qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
qmlRegisterType<Colormap>("PM", 1, 0, "Colormap");
qmlRegisterType<TransitionControl>("PM", 1, 0, "TransitionControl");
qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
-
QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml"));
// Initialize pointers to visual components
@@ -157,6 +157,10 @@ int main(int argc, char **argv)
m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart");
TransitionControl *plotTC = engine.rootObjects()[0]->findChild<TransitionControl *>("plotTC");
+ // Shared object which stores modifications to projections
+ ProjectionHistory history;
+ m->projectionHistory = &history;
+
// Keep track of the current cp (in order to save them later, if requested)
QObject::connect(m->cpPlot, &Scatterplot::xyChanged,
m, &Main::setCP);
@@ -165,7 +169,7 @@ int main(int argc, char **argv)
// Update projection as the cp are modified (either directly in the
// manipulationHandler object or interactively in cpPlot
- ManipulationHandler manipulationHandler(X, cpIndices);
+ ManipulationHandler manipulationHandler(X, cpIndices, m->projectionHistory);
QObject::connect(m->cpPlot, &Scatterplot::xyInteractivelyChanged,
&manipulationHandler, &ManipulationHandler::setCP);
QObject::connect(&manipulationHandler, &ManipulationHandler::cpChanged,
@@ -175,6 +179,10 @@ int main(int argc, char **argv)
QObject::connect(&manipulationHandler, &ManipulationHandler::rpChanged,
m->splat, &VoronoiSplat::setSites);
+ // Update history whenever a new projection is computed
+ QObject::connect(&manipulationHandler, &ManipulationHandler::mapChanged,
+ m->projectionHistory, &ProjectionHistory::addMap);
+
// Keep both scatterplots and the splat scaled equally and relative to the
// full plot
MapScaleHandler mapScaleHandler;
@@ -229,7 +237,7 @@ int main(int argc, char **argv)
m->rpBarChart, &BarChart::brushItem);
// Recompute values whenever projection changes
- ProjectionObserver projectionObserver(X, cpIndices);
+ ProjectionObserver projectionObserver(X, cpIndices, m->projectionHistory);
m->projectionObserver = &projectionObserver;
QObject::connect(&manipulationHandler, &ManipulationHandler::mapChanged,
m->projectionObserver, &ProjectionObserver::setMap);
diff --git a/main.h b/main.h
index 67b7024..52f94e6 100644
--- a/main.h
+++ b/main.h
@@ -121,7 +121,7 @@ public:
splat->setColorScale(getColorScale(colorScaleType));
}
- // Pointer to visual components whose values are set in the main() function
+ // Pointers to visual components whose values are set in the main() function
// after components are instantiated by the QtQuick engine
BarChart *cpBarChart, *rpBarChart;
Colormap *colormap;
@@ -130,6 +130,11 @@ public:
ProjectionObserver *projectionObserver;
+ // Shared object that controls manipulation history
+ ProjectionHistory *projectionHistory;
+ Q_INVOKABLE void undoManipulation() { projectionHistory->undo(); }
+ Q_INVOKABLE void resetManipulation() { projectionHistory->undoAll(); }
+
public slots:
void setCPIndices(const arma::uvec &indices) { m_cpIndices = indices; }
void setCP(const arma::mat &cp) {
diff --git a/manipulationhandler.cpp b/manipulationhandler.cpp
index 5bd916a..f764eaf 100644
--- a/manipulationhandler.cpp
+++ b/manipulationhandler.cpp
@@ -6,72 +6,51 @@
#include "numericrange.h"
ManipulationHandler::ManipulationHandler(const arma::mat &X,
- const arma::uvec &cpIndices)
+ const arma::uvec &cpIndices,
+ ProjectionHistory *history)
: m_X(X)
- , m_Y(X.n_rows, 2)
- , m_firstY(X.n_rows, 2)
- , m_prevY(X.n_rows, 2)
, m_cpIndices(cpIndices)
, m_rpIndices(X.n_rows - cpIndices.n_elem)
- , m_hasFirst(false)
- , m_hasPrev(false)
+ , m_history(history)
, m_technique(TECHNIQUE_LAMP)
{
- 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());
-}
-
-void ManipulationHandler::setTechnique(ManipulationHandler::Technique technique)
-{
- if (m_technique == technique)
- return;
+ Q_ASSERT(history);
- m_technique = technique;
+ NumericRange<arma::uword> allIndices(0, m_X.n_rows);
+ std::set_symmetric_difference(allIndices.cbegin(), allIndices.cend(),
+ m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin());
}
void ManipulationHandler::setCP(const arma::mat &Ys)
{
- if (m_hasFirst) {
- m_prevY = m_Y;
- m_hasPrev = true;
- }
-
+ arma::mat Y(m_X.n_rows, 2);
switch (m_technique) {
case TECHNIQUE_PLMP:
// TODO?
- // 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);
+ mp::lamp(m_X, m_cpIndices, Ys, Y);
break;
case TECHNIQUE_PEKALSKA:
// TODO?
- // mp::pekalska(m_X, m_cpIndices, Ys, m_Y);
break;
}
- if (!m_hasFirst) {
- m_hasFirst = true;
- m_firstY = m_Y;
- }
-
- emit cpChanged(m_Y.rows(m_cpIndices));
- emit rpChanged(m_Y.rows(m_rpIndices));
- emit mapChanged(m_Y);
+ emit cpChanged(Y.rows(m_cpIndices));
+ emit rpChanged(Y.rows(m_rpIndices));
+ emit mapChanged(Y);
}
void ManipulationHandler::setRewind(double t)
{
- if (!m_hasPrev) {
+ if (!m_history->hasPrev()) {
return;
}
- arma::mat Y = m_Y * t + m_prevY * (1.0 - t);
+ arma::mat Y = m_history->Y() * t + m_history->prev() * (1.0 - t);
emit cpRewound(Y.rows(m_cpIndices));
emit rpRewound(Y.rows(m_rpIndices));
emit mapRewound(Y);
diff --git a/manipulationhandler.h b/manipulationhandler.h
index f900ec8..8bb8a98 100644
--- a/manipulationhandler.h
+++ b/manipulationhandler.h
@@ -4,6 +4,8 @@
#include <QObject>
#include <armadillo>
+#include "projectionhistory.h"
+
class ManipulationHandler
: public QObject
{
@@ -17,8 +19,11 @@ public:
TECHNIQUE_PEKALSKA
};
- ManipulationHandler(const arma::mat &X, const arma::uvec &cpIndices);
- void setTechnique(Technique technique);
+ ManipulationHandler(const arma::mat &X,
+ const arma::uvec &cpIndices,
+ ProjectionHistory *history);
+
+ void setTechnique(Technique technique) { m_technique = technique; }
signals:
void cpChanged(const arma::mat &cpY) const;
@@ -33,9 +38,9 @@ public slots:
void setRewind(double t);
private:
- arma::mat m_X, m_Y, m_firstY, m_prevY;
+ arma::mat m_X;
arma::uvec m_cpIndices, m_rpIndices;
- bool m_hasFirst, m_hasPrev;
+ ProjectionHistory *m_history;
Technique m_technique;
};
diff --git a/pm.pro b/pm.pro
index a53e7e1..426cb23 100644
--- a/pm.pro
+++ b/pm.pro
@@ -36,6 +36,7 @@ HEADERS += main.h \
selectionhandler.h \
brushinghandler.h \
projectionobserver.h \
+ projectionhistory.h \
skelft.h \
skelftkernel.h \
mp.h
@@ -56,6 +57,7 @@ SOURCES += main.cpp \
selectionhandler.cpp \
brushinghandler.cpp \
projectionobserver.cpp \
+ projectionhistory.cpp \
skelft_core.cpp \
lamp.cpp \
plmp.cpp \
diff --git a/projectionhistory.cpp b/projectionhistory.cpp
new file mode 100644
index 0000000..b8ffb57
--- /dev/null
+++ b/projectionhistory.cpp
@@ -0,0 +1,45 @@
+#include "projectionhistory.h"
+
+ProjectionHistory::ProjectionHistory(QObject *parent)
+ : QObject(parent)
+ , m_hasFirst(false)
+ , m_hasPrev(false)
+{
+}
+
+inline void ProjectionHistory::undo()
+{
+ if (m_hasPrev) {
+ m_hasPrev = false;
+ m_Y = m_prevY;
+
+ emit historyChanged(m_Y);
+ }
+}
+
+inline void ProjectionHistory::undoAll()
+{
+ if (m_hasFirst) {
+ m_hasPrev = false;
+ m_Y = m_firstY;
+
+ emit historyChanged(m_Y);
+ }
+}
+
+void ProjectionHistory::addMap(const arma::mat &Y)
+{
+ if (m_hasFirst) {
+ m_hasPrev = true;
+ m_prevY = m_Y;
+ }
+
+ m_Y = Y;
+
+ if (!m_hasFirst) {
+ m_hasFirst = true;
+ m_firstY = m_Y;
+ }
+
+ emit historyChanged(m_Y);
+}
diff --git a/projectionhistory.h b/projectionhistory.h
new file mode 100644
index 0000000..28a9b4b
--- /dev/null
+++ b/projectionhistory.h
@@ -0,0 +1,36 @@
+#ifndef PROJECTIONHISTORY_H
+#define PROJECTIONHISTORY_H
+
+#include <QObject>
+
+#include <armadillo>
+
+class ProjectionHistory
+ : public QObject
+{
+ Q_OBJECT
+public:
+ explicit ProjectionHistory(QObject *parent = 0);
+
+ const arma::mat &Y() const { return m_Y; }
+ const arma::mat &first() const { return m_firstY; }
+ const arma::mat &prev() const { return m_prevY; }
+
+ bool hasFirst() const { return m_hasFirst; }
+ bool hasPrev() const { return m_hasPrev; }
+
+ void undo();
+ void undoAll();
+
+signals:
+ void historyChanged(const arma::mat &currentY) const;
+
+public slots:
+ void addMap(const arma::mat &Y);
+
+private:
+ arma::mat m_Y, m_firstY, m_prevY;
+ bool m_hasFirst, m_hasPrev;
+};
+
+#endif // PROJECTIONHISTORY_H
diff --git a/projectionobserver.cpp b/projectionobserver.cpp
index 0c809b0..2e6773d 100644
--- a/projectionobserver.cpp
+++ b/projectionobserver.cpp
@@ -7,7 +7,7 @@
#include "numericrange.h"
ProjectionObserver::ProjectionObserver(const arma::mat &X,
- const arma::uvec &cpIndices)
+ const arma::uvec &cpIndices, ProjectionHistory *history)
: m_type(OBSERVER_CURRENT)
, m_X(X)
, m_cpIndices(cpIndices)
@@ -17,13 +17,14 @@ ProjectionObserver::ProjectionObserver(const arma::mat &X,
, m_values(X.n_rows)
, m_prevValues(X.n_rows)
, m_firstValues(X.n_rows)
- , m_hasFirst(false)
- , m_hasPrev(false)
+ , m_history(history)
{
+ Q_ASSERT(history);
+
m_distX = mp::dist(m_X);
- NumericRange<arma::uword> range(0, m_X.n_rows);
- std::set_symmetric_difference(range.cbegin(), range.cend(),
+ NumericRange<arma::uword> allIndices(0, m_X.n_rows);
+ std::set_symmetric_difference(allIndices.cbegin(), allIndices.cend(),
m_cpIndices.cbegin(), m_cpIndices.cend(), m_rpIndices.begin());
computeAlphas();
@@ -39,7 +40,7 @@ void ProjectionObserver::computeAlphas()
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);
+ m_alphas(i, j) = 1.0 / std::max(norm * norm, 1e-6);
sum += m_alphas(i, j);
}
@@ -51,23 +52,18 @@ void ProjectionObserver::computeAlphas()
void ProjectionObserver::setMap(const arma::mat &Y)
{
- // update previous map
- if (m_hasFirst) {
- m_hasPrev = true;
-
- m_prevY = m_Y;
+ // update data of previous map
+ if (m_history->hasFirst()) {
m_prevDistY = m_distY;
m_prevValues = m_values;
}
- m_Y = Y;
m_distY = mp::dist(Y);
- mp::aggregatedError(m_distX, m_distY, m_values);
- if (!m_hasFirst) {
- m_hasFirst = true;
+ mp::aggregatedError(m_distX, m_distY, m_values);
- m_firstY = m_Y;
+ // update data of first map
+ if (!m_history->hasFirst()) {
m_firstDistY = m_distY;
m_firstValues = m_values;
}
@@ -83,15 +79,17 @@ bool ProjectionObserver::setType(int type)
return true;
}
- if (type != OBSERVER_DIFF_PREVIOUS || m_prevValues.n_elem != 0) {
- m_type = type;
- if (!m_cpSelectionEmpty || !m_rpSelectionEmpty) {
- return true;
- }
- return emitValuesChanged();
+ if (type == OBSERVER_DIFF_PREVIOUS && !m_history->hasPrev()) {
+ return false;
}
- return false;
+ m_type = type;
+ if (!m_cpSelectionEmpty || !m_rpSelectionEmpty) {
+ // We changed our type, but cannot emit values since we have non-empty
+ // selections
+ return true;
+ }
+ return emitValuesChanged();
}
void ProjectionObserver::setCPSelection(const std::vector<bool> &cpSelection)
@@ -153,7 +151,7 @@ bool ProjectionObserver::emitValuesChanged() const
emit valuesChanged(m_values);
return true;
case OBSERVER_DIFF_PREVIOUS:
- if (m_prevValues.n_elem == m_values.n_elem) {
+ if (m_history->hasPrev()) {
arma::vec diff = m_values - m_prevValues;
emit rpValuesChanged(diff(m_rpIndices));
emit valuesChanged(diff);
@@ -161,7 +159,7 @@ bool ProjectionObserver::emitValuesChanged() const
}
return false;
case OBSERVER_DIFF_ORIGINAL:
- if (m_firstValues.n_elem == m_values.n_elem) {
+ if (m_history->hasFirst()) {
arma::vec diff = m_values - m_firstValues;
emit rpValuesChanged(diff(m_rpIndices));
emit valuesChanged(diff);
@@ -175,7 +173,7 @@ bool ProjectionObserver::emitValuesChanged() const
void ProjectionObserver::setRewind(double t)
{
- if (!m_hasPrev || !m_cpSelectionEmpty || !m_rpSelectionEmpty) {
+ if (!m_history->hasPrev() || !m_cpSelectionEmpty || !m_rpSelectionEmpty) {
return;
}
diff --git a/projectionobserver.h b/projectionobserver.h
index 6d8b0f9..11338f5 100644
--- a/projectionobserver.h
+++ b/projectionobserver.h
@@ -4,6 +4,8 @@
#include <QObject>
#include <armadillo>
+#include "projectionhistory.h"
+
class ProjectionObserver
: public QObject
{
@@ -13,7 +15,9 @@ public:
static const int OBSERVER_DIFF_PREVIOUS = 1;
static const int OBSERVER_DIFF_ORIGINAL = 2;
- ProjectionObserver(const arma::mat &X, const arma::uvec &cpIndices);
+ ProjectionObserver(const arma::mat &X,
+ const arma::uvec &cpIndices,
+ ProjectionHistory *history);
signals:
void valuesChanged(const arma::vec &values) const;
@@ -34,7 +38,7 @@ private:
bool emitValuesChanged() const;
int m_type;
- arma::mat m_X, m_Y, m_firstY, m_prevY;
+ arma::mat m_X;
arma::mat m_distX, m_distY, m_firstDistY, m_prevDistY;
arma::uvec m_cpIndices, m_rpIndices;
@@ -47,7 +51,7 @@ private:
// TODO: one per implemented measure
arma::vec m_values, m_firstValues, m_prevValues;
- bool m_hasFirst, m_hasPrev;
+ ProjectionHistory *m_history;
};
#endif // PROJECTIONOBSERVER_H