diff options
author | Samuel Fadel <samuelfadel@gmail.com> | 2016-02-09 17:38:28 -0200 |
---|---|---|
committer | Samuel Fadel <samuelfadel@gmail.com> | 2016-02-09 17:38:28 -0200 |
commit | 886bdd0fa43a2fcdeca306648b643b623af99f88 (patch) | |
tree | 7ffa1e622724862b8b99387199fb1ef7cbf79b28 | |
parent | 962114b867c044240919ebc558c0b25f105db963 (diff) |
Added TransitionControl and plot rewinding.
New component overlays main view and handles middle clicks/drags
to performing rewinding. Also sports smooth transitioning back to
current projection whenever the mouse button is lifted.
Next up, the same kind of transitions in the displayed values.
-rw-r--r-- | main.cpp | 7 | ||||
-rw-r--r-- | main_view.qml | 9 | ||||
-rw-r--r-- | manipulationhandler.cpp | 31 | ||||
-rw-r--r-- | manipulationhandler.h | 3 | ||||
-rw-r--r-- | pm.pro | 5 | ||||
-rw-r--r-- | rewindworkerthread.cpp | 23 | ||||
-rw-r--r-- | rewindworkerthread.h | 21 | ||||
-rw-r--r-- | transitioncontrol.cpp | 61 | ||||
-rw-r--r-- | transitioncontrol.h | 41 |
9 files changed, 197 insertions, 4 deletions
@@ -16,6 +16,7 @@ #include "voronoisplat.h" #include "barchart.h" #include "colormap.h" +#include "transitioncontrol.h" #include "manipulationhandler.h" #include "mapscalehandler.h" #include "selectionhandler.h" @@ -142,6 +143,7 @@ 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<TransitionControl>("PM", 1, 0, "TransitionControl"); qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider); QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml")); @@ -153,6 +155,7 @@ int main(int argc, char **argv) m->splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat"); m->cpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("cpBarChart"); m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart"); + TransitionControl *plotTC = engine.rootObjects()[0]->findChild<TransitionControl *>("plotTC"); // Keep track of the current cp (in order to save them later, if requested) QObject::connect(m->cpPlot, SIGNAL(xyChanged(const arma::mat &)), @@ -171,6 +174,8 @@ int main(int argc, char **argv) m->rpPlot, SLOT(setXY(const arma::mat &))); QObject::connect(&manipulationHandler, SIGNAL(rpChanged(const arma::mat &)), m->splat, SLOT(setSites(const arma::mat &))); + QObject::connect(plotTC, SIGNAL(tChanged(double)), + &manipulationHandler, SLOT(setRewind(double))); // Keep both scatterplots and the splat scaled equally and relative to the // full plot @@ -246,10 +251,12 @@ int main(int argc, char **argv) &projectionObserver, SLOT(setRPSelection(const std::vector<bool> &))); // General component set up + plotTC->setAcceptedMouseButtons(Qt::MiddleButton); m->cpPlot->setDragEnabled(true); m->cpPlot->setAutoScale(false); m->rpPlot->setGlyphSize(3.0f); m->rpPlot->setAutoScale(false); + m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); m->setSelectCPs(); m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); diff --git a/main_view.qml b/main_view.qml index bcf64a5..cfbea4f 100644 --- a/main_view.qml +++ b/main_view.qml @@ -109,6 +109,15 @@ ApplicationWindow { color: "transparent" } } + + TransitionControl { + id: plotTC + objectName: "plotTC" + x: parent.x + y: parent.y + z: 3 + anchors.fill: parent + } } Rectangle { diff --git a/manipulationhandler.cpp b/manipulationhandler.cpp index b298860..7d3e3c0 100644 --- a/manipulationhandler.cpp +++ b/manipulationhandler.cpp @@ -4,6 +4,8 @@ #include <iostream> #include <numeric> +#include <QDebug> + #include "mp.h" #include "numericrange.h" @@ -30,24 +32,47 @@ void ManipulationHandler::setTechnique(ManipulationHandler::Technique technique) void ManipulationHandler::setCP(const arma::mat &Ys) { + m_prevY = m_Y; + switch (m_technique) { case TECHNIQUE_PLMP: - mp::plmp(m_X, m_cpIndices, Ys, m_Y); + // TODO? + // mp::plmp(m_X, m_cpIndices, Ys, m_Y); break; case TECHNIQUE_LSP: - // TODO + // 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 + // TODO? // mp::pekalska(m_X, m_cpIndices, Ys, m_Y); break; } + if (m_firstY.n_rows != m_Y.n_rows) { + m_firstY = m_Y; + } + emit cpChanged(m_Y.rows(m_cpIndices)); emit rpChanged(m_Y.rows(m_rpIndices)); emit mapChanged(m_Y); } + +void ManipulationHandler::setRewind(double t) +{ + if (m_prevY.n_rows != m_Y.n_rows) { + return; + } + + arma::mat Y = m_Y * t + m_prevY * (1.0 - t); + emit cpChanged(Y.rows(m_cpIndices)); + emit rpChanged(Y.rows(m_rpIndices)); + + // NOTE: this signal was supposed to be emitted, but since we don't want + // anything besides graphical objects to know the projection is being + // rewound, this is (for now) left out. + // emit mapChanged(Y); +} diff --git a/manipulationhandler.h b/manipulationhandler.h index 78ca1c9..1e17446 100644 --- a/manipulationhandler.h +++ b/manipulationhandler.h @@ -27,9 +27,10 @@ signals: public slots: void setCP(const arma::mat &Ys); + void setRewind(double t); private: - arma::mat m_X, m_Y; + arma::mat m_X, m_Y, m_prevY, m_firstY; arma::uvec m_cpIndices, m_rpIndices; Technique m_technique; }; @@ -28,6 +28,8 @@ HEADERS += main.h \ colormap.h \ historygraph.h \ barchart.h \ + transitioncontrol.h \ + rewindworkerthread.h \ manipulationhandler.h \ mapscalehandler.h \ numericrange.h \ @@ -37,6 +39,7 @@ HEADERS += main.h \ skelft.h \ skelftkernel.h \ mp.h + SOURCES += main.cpp \ colorscale.cpp \ continuouscolorscale.cpp \ @@ -46,6 +49,8 @@ SOURCES += main.cpp \ colormap.cpp \ historygraph.cpp \ barchart.cpp \ + transitioncontrol.cpp \ + rewindworkerthread.cpp \ manipulationhandler.cpp \ mapscalehandler.cpp \ selectionhandler.cpp \ diff --git a/rewindworkerthread.cpp b/rewindworkerthread.cpp new file mode 100644 index 0000000..f9fa999 --- /dev/null +++ b/rewindworkerthread.cpp @@ -0,0 +1,23 @@ +#include "rewindworkerthread.h" + +// The full duration (usecs) of the restoration animation +static const double DURATION = 250000; + +// The time to wait (usecs) before the next animation tick +static const double TICK_TIME = DURATION / 60.0; + +// The amount to increase 't' per time step +static const double TICK_SIZE = TICK_TIME / DURATION; + +void RewindWorkerThread::run() +{ + double t = m_control->t(); + + while (t + TICK_SIZE < 1.0) { + t += TICK_SIZE; + m_control->setT(t); + QThread::usleep(TICK_TIME); + } + + m_control->setT(1.0); +} diff --git a/rewindworkerthread.h b/rewindworkerthread.h new file mode 100644 index 0000000..0bbaeb6 --- /dev/null +++ b/rewindworkerthread.h @@ -0,0 +1,21 @@ +#ifndef REWINDWORKERTHREAD_H +#define REWINDWORKERTHREAD_H + +#include "transitioncontrol.h" + +#include <QObject> +#include <QThread> + +class RewindWorkerThread + : public QThread +{ + Q_OBJECT +public: + RewindWorkerThread(TransitionControl *control) { m_control = control; } + void run(); + +private: + TransitionControl *m_control; +}; + +#endif // REWINDWORKERTHREAD_H diff --git a/transitioncontrol.cpp b/transitioncontrol.cpp new file mode 100644 index 0000000..cf8f644 --- /dev/null +++ b/transitioncontrol.cpp @@ -0,0 +1,61 @@ +#include "transitioncontrol.h" + +#include <QObject> +#include <QThread> + +#include "rewindworkerthread.h" + +// The mouse button used for interaction +static const Qt::MouseButton MOUSE_BUTTON = Qt::MiddleButton; + +TransitionControl::TransitionControl() + : m_t(1.0) + , m_startPos(-1) +{ +} + +void TransitionControl::setT(double t) +{ + if (t < 0.0 || t > 1.0) { + return; + } + + m_t = t; + emit tChanged(m_t); +} + +void TransitionControl::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != MOUSE_BUTTON) { + return; + } + + m_t = 1.0; + m_startPos = event->pos().x(); +} + +void TransitionControl::mouseMoveEvent(QMouseEvent *event) +{ + int x = event->pos().x(); + if (!(event->buttons() & MOUSE_BUTTON) || x > m_startPos || x < 0) { + return; + } + + m_t = double(x) / m_startPos; + emit tChanged(m_t); +} + +void TransitionControl::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != MOUSE_BUTTON) { + return; + } + + // Back to initial state + m_startPos = -1; + + // We now have to smoothly go back to m_t == 1.0 + m_rewindThread = new RewindWorkerThread(this); + connect(m_rewindThread, &QThread::finished, m_rewindThread, &QObject::deleteLater); + m_rewindThread->start(); +} diff --git a/transitioncontrol.h b/transitioncontrol.h new file mode 100644 index 0000000..ac84786 --- /dev/null +++ b/transitioncontrol.h @@ -0,0 +1,41 @@ +#ifndef TRANSITIONCONTROL_H +#define TRANSITIONCONTROL_H + +#include <QQuickItem> + +/* + * This component emits signals indicating how far from its left edge is the + * mouse since the mouse button was pressed (starting from t == 1.0 with 0.0 + * being exactly at the left edge). As the mouse is released, it emits periodic + * signals incrementing the value until it is restored to the default. + */ +class TransitionControl : + public QQuickItem +{ + Q_OBJECT +public: + TransitionControl(); + double t() const { return m_t; } + +signals: + void tChanged(double t) const; + +public slots: + void setT(double t); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + double m_t; + + // The x pos where interaction started + int m_startPos; + + // Controls the smooth rewind transition + QThread *m_rewindThread; +}; + +#endif // TRANSITIONCONTROL_H |