diff options
-rw-r--r-- | main.cpp | 14 | ||||
-rw-r--r-- | main.h | 7 | ||||
-rw-r--r-- | manipulationhandler.cpp | 49 | ||||
-rw-r--r-- | manipulationhandler.h | 13 | ||||
-rw-r--r-- | pm.pro | 2 | ||||
-rw-r--r-- | projectionhistory.cpp | 45 | ||||
-rw-r--r-- | projectionhistory.h | 36 | ||||
-rw-r--r-- | projectionobserver.cpp | 50 | ||||
-rw-r--r-- | projectionobserver.h | 10 |
9 files changed, 154 insertions, 72 deletions
@@ -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); @@ -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; }; @@ -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 ¤tY) 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 |