From 60a0bfb863aa2a05bafd943423c284f5c2d68863 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Mon, 25 Jan 2016 19:55:52 +0100 Subject: Scatterplots & splat now share the same scaling. --- interactionhandler.cpp | 42 --------------------- interactionhandler.h | 37 ------------------ main.cpp | 99 ++++++++++++++++++++++++++++--------------------- main_view.qml | 13 +++++++ manipulationhandler.cpp | 53 ++++++++++++++++++++++++++ manipulationhandler.h | 37 ++++++++++++++++++ mapscalehandler.cpp | 15 ++++++++ mapscalehandler.h | 32 ++++++++++++++++ numericrange.h | 79 +++++++++++++++++++++++++++++++++++++++ pm.pro | 7 +++- projectionobserver.cpp | 14 +++---- 11 files changed, 296 insertions(+), 132 deletions(-) delete mode 100644 interactionhandler.cpp delete mode 100644 interactionhandler.h create mode 100644 manipulationhandler.cpp create mode 100644 manipulationhandler.h create mode 100644 mapscalehandler.cpp create mode 100644 mapscalehandler.h create mode 100644 numericrange.h 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 -#include - -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 #include -#include +#include #include #include @@ -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("PM", 1, 0, "BarChart"); qmlRegisterType("PM", 1, 0, "VoronoiSplat"); qmlRegisterType("PM", 1, 0, "Colormap"); - qmlRegisterType("PM", 1, 0, "InteractionHandler"); qmlRegisterSingletonType
("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("cpPlot"); - m->cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); - m->rpPlot = engine.rootObjects()[0]->findChild("rpPlot"); - - m->splat = engine.rootObjects()[0]->findChild("splat"); - skelft2DInitialization(m->splat->width()); - m->colormap = engine.rootObjects()[0]->findChild("colormap"); + m->splat = engine.rootObjects()[0]->findChild("splat"); + m->cpBarChart = engine.rootObjects()[0]->findChild("cpBarChart"); + m->rpBarChart = engine.rootObjects()[0]->findChild("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 &, const LinearScale &)), + // Keep both scatterplots scaled equally and to the full plot + MapScaleHandler mapScaleHandler; + QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale &, const LinearScale &)), m->cpPlot, SLOT(setScale(const LinearScale &, const LinearScale &))); + QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale &, const LinearScale &)), + m->rpPlot, SLOT(setScale(const LinearScale &, const LinearScale &))); + QObject::connect(&mapScaleHandler, SIGNAL(scaleChanged(const LinearScale &, const LinearScale &)), + m->splat, SLOT(setScale(const LinearScale &, const LinearScale &))); + 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("cpBarChart"); - m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton); - m->setCPBarChartColorScale(Main::ColorScaleContinuous); - - m->rpBarChart = engine.rootObjects()[0]->findChild("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 &)), @@ -184,36 +189,46 @@ int main(int argc, char **argv) QObject::connect(&cpSelectionHandler, SIGNAL(selectionChanged(const std::vector &)), m->cpPlot, SLOT(setSelection(const std::vector &))); - //SelectionHandler rpSelectionHandler(X.n_rows - cpIndices.n_elem); - //QObject::connect(m->rpPlot, SIGNAL(selectionInteractivelyChanged(const std::vector &)), - // &rpSelectionHandler, SLOT(setSelection(const std::vector &))); - //QObject::connect(m->rpBarChart, SIGNAL(selectionInteractivelyChanged(const std::vector &)), - // &rpSelectionHandler, SLOT(setSelection(const std::vector &))); - //QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector &)), - // m->rpPlot, SLOT(setSelection(const std::vector &))); + SelectionHandler rpSelectionHandler(X.n_rows - cpIndices.n_elem); + QObject::connect(m->rpPlot, SIGNAL(selectionInteractivelyChanged(const std::vector &)), + &rpSelectionHandler, SLOT(setSelection(const std::vector &))); + QObject::connect(m->rpBarChart, SIGNAL(selectionInteractivelyChanged(const std::vector &)), + &rpSelectionHandler, SLOT(setSelection(const std::vector &))); + QObject::connect(&rpSelectionHandler, SIGNAL(selectionChanged(const std::vector &)), + m->rpPlot, SLOT(setSelection(const std::vector &))); + // 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 +#include +#include + +#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 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 +#include + +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 +#include + +#include "scale.h" + +class MapScaleHandler + : public QObject +{ + Q_OBJECT +public: + MapScaleHandler(); + + void getScales(LinearScale &sx, LinearScale &sy) const { + sx = m_sx; + sy = m_sy; + } + +signals: + void scaleChanged(const LinearScale &sx, + const LinearScale &sy) const; + +public slots: + void scaleToMap(const arma::mat &Y); + +private: + LinearScale 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 +#include + +/* + * A range in [first, last) in steps of 1. + */ +template +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 #include - -#include +#include #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 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) -- cgit v1.2.3