aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-02-09 17:38:28 -0200
committerSamuel Fadel <samuelfadel@gmail.com>2016-02-09 17:38:28 -0200
commit886bdd0fa43a2fcdeca306648b643b623af99f88 (patch)
tree7ffa1e622724862b8b99387199fb1ef7cbf79b28
parent962114b867c044240919ebc558c0b25f105db963 (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.cpp7
-rw-r--r--main_view.qml9
-rw-r--r--manipulationhandler.cpp31
-rw-r--r--manipulationhandler.h3
-rw-r--r--pm.pro5
-rw-r--r--rewindworkerthread.cpp23
-rw-r--r--rewindworkerthread.h21
-rw-r--r--transitioncontrol.cpp61
-rw-r--r--transitioncontrol.h41
9 files changed, 197 insertions, 4 deletions
diff --git a/main.cpp b/main.cpp
index 47bea3d..b1a8e8b 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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;
};
diff --git a/pm.pro b/pm.pro
index e38d3a9..a53e7e1 100644
--- a/pm.pro
+++ b/pm.pro
@@ -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