aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2015-10-13 13:04:29 -0300
committerSamuel Fadel <samuelfadel@gmail.com>2015-10-13 13:04:29 -0300
commit1f7281e153c656da8161ffb90605bc0595cf64ec (patch)
tree5f451614afd76b52dd164872cad2fc3e3fb1889e
parent84435fe73280dee3ea18e294b7ea1ef560baab01 (diff)
Argument handling and file saving.
-rw-r--r--main.cpp87
-rw-r--r--main.h65
-rw-r--r--main_view.qml34
-rw-r--r--pm.pro3
4 files changed, 136 insertions, 53 deletions
diff --git a/main.cpp b/main.cpp
index 5dbbe8b..e9492f7 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,10 +1,14 @@
#include <cmath>
-#include <memory>
#include <iostream>
-#include <QSurfaceFormat>
+#include <memory>
+#include <string>
+
#include <QApplication>
+#include <QtQml>
#include <QQmlApplicationEngine>
+#include <QSurfaceFormat>
+#include "main.h"
#include "mp.h"
#include "continuouscolorscale.h"
#include "scatterplot.h"
@@ -15,12 +19,44 @@
#include "distortionobserver.h"
#include "npdistortion.h"
+static QObject *mainProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+
+ return Main::instance();
+}
+
int main(int argc, char **argv)
{
QApplication app(argc, argv);
+ app.setApplicationName("pm");
+ app.setApplicationVersion("1.0");
qmlRegisterType<Scatterplot>("PM", 1, 0, "Scatterplot");
qmlRegisterType<HistoryGraph>("PM", 1, 0, "HistoryGraph");
+ qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Interactive multidimensional projections.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("dataset", "Dataset filename (.tbl)");
+
+ QCommandLineOption indicesFileOutputOption(QStringList() << "i" << "indices",
+ "Filename to store the subsample indices.",
+ "indices_path");
+ parser.addOption(indicesFileOutputOption);
+ QCommandLineOption subsampleFileOutputOption(QStringList() << "s" << "subsample",
+ "Filename to store subsample mapping.",
+ "subsample_path");
+ parser.addOption(subsampleFileOutputOption);
+
+ parser.process(app);
+ QStringList args = parser.positionalArguments();
+ if (args.size() != 1) {
+ parser.showHelp(1);
+ }
// Set up multisampling
QSurfaceFormat fmt;
@@ -28,19 +64,23 @@ int main(int argc, char **argv)
QSurfaceFormat::setDefaultFormat(fmt);
QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml"));
- arma::mat dataset;
- if (argc > 1) {
- dataset.load(argv[1], arma::raw_ascii);
- } else {
- dataset.load(std::cin, arma::raw_ascii);
+ Main *m = Main::instance();
+ if (parser.isSet(indicesFileOutputOption)) {
+ m->setIndicesSavePath(parser.value(indicesFileOutputOption));
}
+ if (parser.isSet(subsampleFileOutputOption)) {
+ m->setSubsampleSavePath(parser.value(subsampleFileOutputOption));
+ }
+ m->loadDataset(args[0].toStdString());
- arma::mat X = dataset.cols(0, dataset.n_cols - 2);
- arma::vec labels = dataset.col(dataset.n_cols - 1);
+ arma::mat X = m->X();
+ arma::vec labels = m->labels();
- arma::uword n = dataset.n_rows;
+ arma::uword n = X.n_rows;
arma::uword subsampleSize = (arma::uword) n / 10.f;
arma::uvec sampleIndices = arma::randi<arma::uvec>(subsampleSize, arma::distr_param(0, n-1));
+ m->setSubsampleIndices(sampleIndices);
+
arma::mat Ys(subsampleSize, 2, arma::fill::randn);
mp::forceScheme(mp::dist(X.rows(sampleIndices)), Ys);
@@ -65,7 +105,13 @@ int main(int argc, char **argv)
subsamplePlot->setColorScale(&colorScale);
Scatterplot *plot = engine.rootObjects()[0]->findChild<Scatterplot *>("plot");
- // connect both plots through interaction handler
+ // Keep track of the current subsample (in order to save them later, if requested)
+ QObject::connect(subsamplePlot, SIGNAL(xyChanged(const arma::mat &)),
+ m, SLOT(setSubsample(const arma::mat &)));
+ QObject::connect(subsamplePlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)),
+ m, SLOT(setSubsample(const arma::mat &)));
+
+ // Update LAMP projection as the subsample is modified
InteractionHandler interactionHandler(X, sampleIndices);
QObject::connect(subsamplePlot, SIGNAL(xyChanged(const arma::mat &)),
&interactionHandler, SLOT(setSubsample(const arma::mat &)));
@@ -74,20 +120,21 @@ int main(int argc, char **argv)
QObject::connect(&interactionHandler, SIGNAL(subsampleChanged(const arma::mat &)),
plot, SLOT(setXY(const arma::mat &)));
- // linking between selections in subsample plot and full dataset plot
+ // Linking between selections in subsample plot and full dataset plot
SelectionHandler selectionHandler(sampleIndices);
QObject::connect(subsamplePlot, SIGNAL(selectionChanged(const QSet<int> &)),
&selectionHandler, SLOT(setSelection(const QSet<int> &)));
QObject::connect(&selectionHandler, SIGNAL(selectionChanged(const QSet<int> &)),
plot, SLOT(setSelection(const QSet<int> &)));
+ // Connections between history graph and subsample plot
HistoryGraph *history = engine.rootObjects()[0]->findChild<HistoryGraph *>("history");
- // connections between history graph and subsample plot
QObject::connect(subsamplePlot, SIGNAL(xyInteractivelyChanged(const arma::mat &)),
history, SLOT(addHistoryItem(const arma::mat &)));
QObject::connect(history, SIGNAL(currentItemChanged(const arma::mat &)),
subsamplePlot, SLOT(setXY(const arma::mat &)));
+ // Map distortion as the glyph color
DistortionObserver distortionObs(X, sampleIndices);
std::unique_ptr<DistortionMeasure> distortionMeasure(new NPDistortion());
distortionObs.setMeasure(distortionMeasure.get());
@@ -96,13 +143,13 @@ int main(int argc, char **argv)
QObject::connect(&distortionObs, SIGNAL(mapChanged(const arma::vec &)),
plot, SLOT(setColorData(const arma::vec &)));
- EffectiveInteractionEnforcer enforcer(sampleIndices);
- QObject::connect(subsamplePlot, SIGNAL(selectionChanged(const QSet<int> &)),
- &enforcer, SLOT(setSelection(const QSet<int> &)));
- QObject::connect(plot, SIGNAL(colorDataChanged(const arma::vec &)),
- &enforcer, SLOT(setMeasureDifference(const arma::vec &)));
- QObject::connect(&enforcer, SIGNAL(effectivenessChanged(const arma::vec &)),
- subsamplePlot, SLOT(setColorData(const arma::vec &)));
+ //EffectiveInteractionEnforcer enforcer(sampleIndices);
+ //QObject::connect(subsamplePlot, SIGNAL(selectionChanged(const QSet<int> &)),
+ // &enforcer, SLOT(setSelection(const QSet<int> &)));
+ //QObject::connect(plot, SIGNAL(colorDataChanged(const arma::vec &)),
+ // &enforcer, SLOT(setMeasureDifference(const arma::vec &)));
+ //QObject::connect(&enforcer, SIGNAL(effectivenessChanged(const arma::vec &)),
+ // subsamplePlot, SLOT(setColorData(const arma::vec &)));
/*
ContinuousColorScale ccolorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
diff --git a/main.h b/main.h
new file mode 100644
index 0000000..01d62f7
--- /dev/null
+++ b/main.h
@@ -0,0 +1,65 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <QObject>
+#include <armadillo>
+
+class Main : public QObject
+{
+ Q_OBJECT
+public:
+ static Main *instance()
+ {
+ static Main *m = 0;
+ if (m == 0) {
+ m = new Main();
+ }
+
+ return m;
+ }
+
+ Q_INVOKABLE bool saveData() const
+ {
+ bool ret = true;
+ if (m_subsample.n_elem > 0 && m_indicesSavePath.size() > 0) {
+ ret = ret && m_subsample.save(m_subsampleSavePath, arma::raw_ascii);
+ }
+ if (m_subsampleIndices.n_elem > 0 && m_subsampleSavePath.size() > 0) {
+ ret = ret && m_subsampleIndices.save(m_indicesSavePath, arma::raw_ascii);
+ }
+
+ return ret;
+ }
+
+ Q_INVOKABLE bool loadDataset(const std::string &path) { return m_dataset.load(path, arma::raw_ascii); }
+
+ Q_INVOKABLE void setIndicesSavePath(const std::string &path) { m_indicesSavePath = path; }
+ Q_INVOKABLE void setIndicesSavePath(const QString &path) { setIndicesSavePath(path.toStdString()); }
+ Q_INVOKABLE void setSubsampleSavePath(const std::string &path) { m_subsampleSavePath = path; }
+ Q_INVOKABLE void setSubsampleSavePath(const QString &path) { setSubsampleSavePath(path.toStdString()); }
+
+ arma::mat X() const { return m_dataset.cols(0, m_dataset.n_cols - 2); }
+ arma::vec labels() const { return m_dataset.col(m_dataset.n_cols - 1); }
+
+public slots:
+ void setSubsampleIndices(const arma::uvec &indices) { m_subsampleIndices = indices; }
+ void setSubsample(const arma::mat &subsample) {
+ if (subsample.n_cols != 2
+ || subsample.n_rows != m_subsampleIndices.n_elem) {
+ return;
+ }
+
+ m_subsample = subsample;
+ }
+
+private:
+ Main(QObject *parent = 0) : QObject(parent) {}
+ ~Main() {}
+ Q_DISABLE_COPY(Main)
+
+ arma::mat m_dataset, m_subsample;
+ arma::uvec m_subsampleIndices;
+ std::string m_indicesSavePath, m_subsampleSavePath;
+};
+
+#endif // MAIN_H
diff --git a/main_view.qml b/main_view.qml
index 49448cb..de9ec64 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -14,7 +14,6 @@ ApplicationWindow {
menuBar: MenuBar {
Menu {
title: "File"
- MenuItem { action: openAction }
MenuItem { action: savePlotAction }
MenuItem { action: quitAction }
}
@@ -91,28 +90,6 @@ ApplicationWindow {
}
}
- FileDialog {
- id: fileOpenDialog
- title: "Choose a data set to load..."
- selectMultiple: false
- selectExisting: true
-
- onAccepted: {
- console.log("Loading data set: " + this.fileUrl)
- }
- }
-
- FileDialog {
- id: fileSaveDialog
- title: "Save subsample mapping..."
- selectMultiple: false
- selectExisting: false
-
- onAccepted: {
- subsamplePlot.saveToFile(this.fileUrl)
- }
- }
-
Action {
id: quitAction
text: "&Quit"
@@ -121,19 +98,12 @@ ApplicationWindow {
}
Action {
- id: openAction
- text: "&Open..."
- shortcut: "Ctrl+O"
- onTriggered: fileOpenDialog.open()
- }
-
- Action {
id: savePlotAction
- text: "&Save subsample"
+ text: "&Save data"
shortcut: "Ctrl+S"
onTriggered: {
console.log("Saving subsample mapping...")
- fileSaveDialog.open()
+ Main.saveData()
}
}
diff --git a/pm.pro b/pm.pro
index 92b13e7..281cb55 100644
--- a/pm.pro
+++ b/pm.pro
@@ -2,7 +2,8 @@ QT += qml quick widgets
QMAKE_CXXFLAGS += -std=c++11 -fopenmp
QMAKE_LIBS += -larmadillo -fopenmp
-HEADERS += colorscale.h \
+HEADERS += main.h \
+ colorscale.h \
continuouscolorscale.h \
geometry.h \
scale.h \