aboutsummaryrefslogtreecommitdiff
path: root/main.cpp
diff options
context:
space:
mode:
authorSamuel Fadel <samuel@nihil.ws>2023-05-23 11:22:33 +0200
committerSamuel Fadel <samuel@nihil.ws>2023-05-23 11:22:33 +0200
commit0f34fd437efb936ef29ac91186321aa7251fbfb1 (patch)
tree271e994828f4bb19c35b2630f2705cb64b8d4552 /main.cpp
parentbedf6936885694688ddb8bd3452f6bd68ef8d29c (diff)
Massive changes in initial port away from Qt.
Diffstat (limited to 'main.cpp')
-rw-r--r--main.cpp758
1 files changed, 498 insertions, 260 deletions
diff --git a/main.cpp b/main.cpp
index 5f7693d..5f76a74 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,38 +1,242 @@
#include <cmath>
+#include <functional>
#include <iostream>
+#include <memory>
#include <numeric>
#include <random>
#include <string>
-#include <QApplication>
-#include <QtQml>
-#include <QQmlApplicationEngine>
-#include <QSurfaceFormat>
+#define GLAD_GL_IMPLEMENTATION
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#include "nuklear.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
-#include "main.h"
#include "mp.h"
#include "continuouscolorscale.h"
+#include "divergentcolorscale.h"
+#include "numericrange.h"
#include "scatterplot.h"
#include "voronoisplat.h"
-#include "lineplot.h"
#include "barchart.h"
#include "colormap.h"
-#include "transitioncontrol.h"
+// #include "transitioncontrol.h"
#include "projectionhistory.h"
#include "manipulationhandler.h"
#include "mapscalehandler.h"
-#include "selectionhandler.h"
+// #include "selectionhandler.h"
#include "brushinghandler.h"
static const int RNG_SEED = 123;
+static const int WINDOW_WIDTH = 1200;
+static const int WINDOW_HEIGHT = 800;
-static QObject *mainProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
+class Main
{
- Q_UNUSED(engine)
- Q_UNUSED(scriptEngine)
+public:
+ Main(Main const&) = delete;
+ void operator=(Main const&) = delete;
- return Main::instance();
-}
+ static Main &instance() {
+ static Main instance;
+ return instance;
+ }
+
+ bool saveData() const {
+ bool ret = true;
+ if (m_cp.n_elem > 0 && m_indicesSavePath.size() > 0) {
+ ret = ret && m_cp.save(m_cpSavePath, arma::raw_ascii);
+ }
+ if (m_cpIndices.n_elem > 0 && m_cpSavePath.size() > 0) {
+ ret = ret && m_cpIndices.save(m_indicesSavePath, arma::raw_ascii);
+ }
+
+ return ret;
+ }
+
+ bool loadDataset(const std::string &path) {
+ return m_X.load(path, arma::raw_ascii);
+ }
+
+ void setIndicesSavePath(const std::string &path) {
+ m_indicesSavePath = path;
+ }
+
+ void setCPSavePath(const std::string &path) {
+ m_cpSavePath = path;
+ }
+
+ arma::mat X() const { return m_X; }
+
+ void setSelectRPs() {
+ // cpPlot->setAcceptedMouseButtons(Qt::NoButton);
+ // cpPlot->setAcceptHoverEvents(false);
+
+ // rpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ // rpPlot->setAcceptHoverEvents(true);
+ }
+
+ void setSelectCPs() {
+ // rpPlot->setAcceptedMouseButtons(Qt::NoButton);
+ // rpPlot->setAcceptHoverEvents(false);
+
+ // cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ // cpPlot->setAcceptHoverEvents(true);
+ }
+
+ enum ColorScaleType {
+ ColorScaleCategorical,
+ ColorScaleContinuous,
+ ColorScaleDivergent,
+ ColorScaleRainbow
+ };
+
+ void setCPColorScale(ColorScaleType colorScaleType) {
+ float min = 0.0f;
+ float max = 1.0f;
+ if (colorScaleCPs) {
+ min = colorScaleCPs->min();
+ max = colorScaleCPs->max();
+ }
+ colorScaleCPs = getColorScale(colorScaleType);
+ colorScaleCPs->setExtents(min, max);
+
+ cpPlot->setColorScale(colorScaleCPs);
+ cpBarChart->setColorScale(colorScaleCPs);
+ cpColormap->setColorScale(colorScaleCPs);
+ // bundlePlot->setColorScale(colorScaleCPs);
+ }
+
+ void setRPColorScale(ColorScaleType colorScaleType) {
+ float min = 0.0f;
+ float max = 1.0f;
+ // TODO: if weird behaviors are found, check this (was CPs)
+ if (colorScaleRPs) {
+ min = colorScaleRPs->min();
+ max = colorScaleRPs->max();
+ }
+ colorScaleRPs = getColorScale(colorScaleType);
+ colorScaleRPs->setExtents(min, max);
+
+ rpPlot->setColorScale(colorScaleRPs);
+ splat->setColorScale(colorScaleRPs);
+ rpBarChart->setColorScale(colorScaleRPs);
+ rpColormap->setColorScale(colorScaleRPs);
+ }
+
+ // Pointers to visual components whose values are set in the main() function
+ // after components are instantiated by the QtQuick engine
+ BarChart *cpBarChart, *rpBarChart;
+ Colormap *cpColormap, *rpColormap;
+ Scatterplot *cpPlot, *rpPlot;
+ VoronoiSplat *splat;
+ // LinePlot *bundlePlot;
+
+ // Color scales in use
+ std::shared_ptr<ColorScale> colorScaleCPs, colorScaleRPs;
+
+ // Object that controls manipulation history
+ ProjectionHistory *projectionHistory;
+
+ void undoManipulation() { projectionHistory->undo(); }
+ void resetManipulation() { projectionHistory->reset(); }
+
+ enum ObserverType {
+ ObserverCurrent = ProjectionHistory::ObserverCurrent,
+ ObserverDiffPrevious = ProjectionHistory::ObserverDiffPrevious,
+ ObserverDiffFirst = ProjectionHistory::ObserverDiffFirst
+ };
+
+ bool setObserverType(ObserverType observerType) {
+ switch (observerType) {
+ case ObserverCurrent:
+ return projectionHistory->setType(ProjectionHistory::ObserverCurrent);
+ case ObserverDiffPrevious:
+ return projectionHistory->setType(ProjectionHistory::ObserverDiffPrevious);
+ case ObserverDiffFirst:
+ return projectionHistory->setType(ProjectionHistory::ObserverDiffFirst);
+ }
+
+ return false;
+ }
+
+ void setCPIndices(const arma::uvec &indices) {
+ m_cpIndices = indices;
+
+ m_rpIndices.set_size(m_X.n_rows - m_cpIndices.n_elem);
+ 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 setCP(const arma::mat &cp) {
+ if (cp.n_cols != 2
+ || cp.n_rows != m_cpIndices.n_elem) {
+ return;
+ }
+
+ m_cp = cp;
+ }
+
+ void updateMap(const arma::mat &Y) {
+ cpPlot->setXY(Y.rows(m_cpIndices));
+
+ const arma::mat &regularPoints = Y.rows(m_rpIndices);
+ rpPlot->setXY(regularPoints);
+ splat->setSites(regularPoints);
+ }
+
+private:
+ Main()
+ : cpPlot(0)
+ , rpPlot(0)
+ , splat(0)
+ , cpBarChart(0)
+ , rpBarChart(0)
+ , cpColormap(0)
+ , rpColormap(0)
+ // , bundlePlot(0)
+ , projectionHistory(0)
+ {
+ }
+
+ std::shared_ptr<ColorScale> getColorScale(ColorScaleType colorScaleType) {
+ switch (colorScaleType) {
+ case ColorScaleCategorical:
+ return std::shared_ptr<ColorScale>(new ColorScale{
+ // QColor("#1f77b4"), QColor("#ff7f0e"), QColor("#2ca02c"),
+ Color(31, 119, 180), Color(255, 127, 14), Color(44, 160, 44),
+ // QColor("#d62728"), QColor("#9467bd"), QColor("#8c564b"),
+ Color(214, 39, 40), Color(148, 103, 189), Color(140, 86, 75),
+ // QColor("#e377c2"), QColor("#17becf"), QColor("#7f7f7f"),
+ Color(227, 119, 194), Color(23, 190, 207), Color(127, 127, 127),
+ });
+ case ColorScaleContinuous:
+ return std::shared_ptr<ColorScale>(
+ ContinuousColorScale::builtin(
+ ContinuousColorScale::HeatedObjects, nullptr));
+ case ColorScaleDivergent:
+ return std::shared_ptr<ColorScale>(
+ DivergentColorScale::builtin(
+ DivergentColorScale::RedGrayBlue, nullptr));
+ case ColorScaleRainbow:
+ // fall-through
+ default:
+ return std::shared_ptr<ColorScale>(
+ ContinuousColorScale::builtin(
+ ContinuousColorScale::Rainbow, nullptr));
+ }
+ }
+
+ arma::mat m_X, m_cp;
+ arma::uvec m_cpIndices, m_rpIndices;
+ std::string m_indicesSavePath, m_cpSavePath;
+};
arma::uvec extractCPs(const arma::mat &X)
{
@@ -55,104 +259,110 @@ arma::mat standardize(const arma::mat &X)
return stdX;
}
-void overviewBundles(const Main *m)
+// GLFW callbacks
+static void glfw_errorCallback(int err, const char *description)
{
- const arma::mat &unreliability = m->projectionHistory->unreliability();
- arma::uvec indicesLargest = arma::sort_index(unreliability, "descending");
- arma::uword numLargest = m->projectionHistory->Y().n_rows * 0.1f;
- indicesLargest = indicesLargest.subvec(0, numLargest-1);
- m->bundlePlot->setValues(unreliability(indicesLargest));
-
- const arma::uvec &cpIndices = m->projectionHistory->cpIndices();
- arma::uvec CPs = cpIndices(indicesLargest / unreliability.n_rows);
-
- const arma::uvec &rpIndices = m->projectionHistory->rpIndices();
- arma::uvec RPs = indicesLargest;
- RPs.transform([&unreliability](arma::uword v) {
- return v % unreliability.n_rows;
- });
- RPs = rpIndices(RPs);
+ std::cerr << "glfw: Error: " << err << ": " << description << std::endl;
+}
- arma::uvec indices(CPs.n_elem + RPs.n_elem);
- for (arma::uword i = 0; i < CPs.n_elem; i++) {
- indices[2*i + 0] = CPs(i);
- indices[2*i + 1] = RPs(i);
- }
- m->bundlePlot->setLines(indices, m->projectionHistory->Y());
+static void text_input(GLFWwindow *win, unsigned int codepoint)
+{
+ nk_input_unicode((struct nk_context *) glfwGetWindowUserPointer(win), codepoint);
}
-int main(int argc, char **argv)
+static void scroll_input(GLFWwindow *win, double _, double yoff)
{
- QApplication app(argc, argv);
- app.setApplicationName("pm");
- app.setApplicationVersion("1.0");
- // app.setAttribute(Qt::AA_ShareOpenGLContexts);
-
- // Command line parser
- QCommandLineParser parser;
- parser.setApplicationDescription("Interactive multidimensional projections.");
- parser.addHelpOption();
- parser.addVersionOption();
- parser.addPositionalArgument("dataset", "Dataset filename (.tbl file)");
-
- QCommandLineOption indicesFileOutputOption(QStringList() << "i" << "indices",
- "Filename to store the control points' indices. Omitting this option disables saving indices.",
- "filename");
- parser.addOption(indicesFileOutputOption);
- QCommandLineOption cpFileOutputOption(QStringList() << "c" << "cpoints",
- "Filename to store the control points' map. Omitting this option disables saving this map.",
- "filename");
- parser.addOption(cpFileOutputOption);
-
- parser.process(app);
- QStringList args = parser.positionalArguments();
- if (args.size() != 1) {
- parser.showHelp(1);
+ (void) _;
+ nk_input_scroll((struct nk_context *) glfwGetWindowUserPointer(win),
+ nk_vec2(0, (float) yoff));
+}
+
+int main(int argc, char *argv[])
+{
+ static GLFWwindow *win;
+ int width = 0, height = 0;
+ int display_width = 0, display_height = 0;
+
+ // GUI
+ // struct device device;
+ // struct nk_font_atlas atlas;
+ // struct media media;
+ struct nk_context ctx;
+
+ NK_UNUSED(argc);
+ NK_UNUSED(argv);
+
+ glfwSetErrorCallback(glfw_errorCallback);
+ if (!glfwInit()) {
+ std::cerr << "glfw: init failed" << std::endl;
+ exit(1);
+ }
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+#ifdef __APPLE__
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#endif
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GL_TRUE);
+ glfwWindowHint(GLFW_RED_BITS, 8);
+ glfwWindowHint(GLFW_GREEN_BITS, 8);
+ glfwWindowHint(GLFW_BLUE_BITS, 8);
+ glfwWindowHint(GLFW_ALPHA_BITS, 8);
+ glfwWindowHint(GLFW_SAMPLES, 8);
+
+ win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "pm", NULL, NULL);
+ if (!win) {
+ std::cerr << "glfw: failed to create window" << std::endl;
+ glfwTerminate();
+ exit(1);
}
- // Load dataset
- Main *m = Main::instance();
- if (!m->loadDataset(args[0].toStdString())) {
- std::cerr << "Could not load dataset.\n";
+ glfwMakeContextCurrent(win);
+ glfwSetWindowUserPointer(win, &ctx);
+ glfwSetCharCallback(win, text_input);
+ glfwSetScrollCallback(win, scroll_input);
+ glfwGetWindowSize(win, &width, &height);
+ glfwGetFramebufferSize(win, &display_width, &display_height);
+
+ GLFWmonitor* primary = glfwGetPrimaryMonitor();
+ float xscale, yscale;
+ glfwGetMonitorContentScale(primary, &xscale, &yscale);
+ std::cout << "glfw: Detected scaling: "
+ << xscale << "x" << yscale
+ << std::endl;
+ const GLFWvidmode * mode = glfwGetVideoMode(primary);
+ std::cout << "glfw: Detected resolution: "
+ << mode->width << "x" << mode->height
+ << std::endl;
+
+ if (!gladLoadGL(glfwGetProcAddress)) {
+ std::cerr << "GLAD load failed" << std::endl;
+ glfwTerminate();
+ exit(1);
+ }
+
+ glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
+
+ Main &m = Main::instance();
+ if (!m.loadDataset("wdbc-std.tbl")) {
+ std::cerr << "Could not load data." << std::endl;
return 1;
}
- const arma::mat &X = standardize(m->X());
+ const arma::mat &X = standardize(m.X());
arma::arma_rng::set_seed(RNG_SEED);
arma::uvec cpIndices;
arma::mat Ys;
- // Load/generate indices
- QString indicesFilename;
- if (parser.isSet(indicesFileOutputOption)) {
- indicesFilename = parser.value(indicesFileOutputOption);
- }
- QFile indicesFile(indicesFilename);
- if (indicesFile.exists()) {
- m->setIndicesSavePath(indicesFilename);
- cpIndices.load(indicesFilename.toStdString(), arma::raw_ascii);
- cpIndices -= 1;
- } else {
- std::cerr << "No indices file, generating indices...\n";
- cpIndices = extractCPs(X);
- }
+ // Load/generate control point indices
+ cpIndices = extractCPs(X);
- // Load/generate CPs
- QString cpFilename;
- if (parser.isSet(cpFileOutputOption)) {
- cpFilename = parser.value(cpFileOutputOption);
- }
- QFile cpFile(cpFilename);
- if (cpFile.exists()) {
- m->setCPSavePath(cpFilename);
- Ys.load(cpFilename.toStdString(), arma::raw_ascii);
- } else {
- std::cerr << "No CP file, generating initial projection...\n";
- Ys.set_size(cpIndices.n_elem, 2);
- Ys.randu();
- mp::forceScheme(mp::dist(X.rows(cpIndices)), Ys);
- }
+ // Load/generate control points
+ Ys.set_size(cpIndices.n_elem, 2);
+ Ys.randn();
+ 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;
@@ -168,95 +378,61 @@ int main(int argc, char **argv)
cpIndices = cpIndices(cpSortedIndices);
Ys = Ys.rows(cpSortedIndices);
- m->setCPIndices(cpIndices);
- m->setCP(Ys);
-
- // Set up multisampling
- QSurfaceFormat fmt;
- fmt.setRenderableType(QSurfaceFormat::OpenGL);
- //fmt.setVersion(4, 5);
- fmt.setRedBufferSize(8);
- fmt.setGreenBufferSize(8);
- fmt.setBlueBufferSize(8);
- fmt.setAlphaBufferSize(8);
- 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<LinePlot>("PM", 1, 0, "LinePlot");
- 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
- m->cpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("cpPlot");
- m->rpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("rpPlot");
- m->splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat");
- m->bundlePlot = engine.rootObjects()[0]->findChild<LinePlot *>("bundlePlot");
- m->cpColormap = engine.rootObjects()[0]->findChild<Colormap *>("cpColormap");
- m->rpColormap = engine.rootObjects()[0]->findChild<Colormap *>("rpColormap");
- m->cpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("cpBarChart");
- m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart");
- TransitionControl *plotTC = engine.rootObjects()[0]->findChild<TransitionControl *>("plotTC");
-
- // Shared object which stores modifications to projections
+ m.setCPIndices(cpIndices);
+ m.setCP(Ys);
+
+ // Keeps track of what happened to the visualization
ProjectionHistory history(X, cpIndices);
- 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);
- QObject::connect(m->cpPlot, &Scatterplot::xyInteractivelyChanged,
- m, &Main::setCP);
+ // Visual components
+ Scatterplot cpPlot, rpPlot;
+ VoronoiSplat splat;
+ Colormap cpColormap, rpColormap;
+ BarChart cpBarChart, rpBarChart;
+ m.cpPlot = &cpPlot;
+ m.rpPlot = &rpPlot;
+ m.splat = &splat;
- // Keep both scatterplots, the splat and line plot scaled equally and
+ auto setCP = std::bind(&Main::setCP, &m, std::placeholders::_1);
+ cpPlot.xyChanged.connect(setCP);
+ cpPlot.xyInteractivelyChanged.connect(setCP);
+
+ // Keep both scatterplots and the splat scaled equally and
// relative to the full plot
MapScaleHandler mapScaleHandler;
- QObject::connect(&mapScaleHandler, &MapScaleHandler::scaleChanged,
- m->cpPlot, &Scatterplot::setScale);
- QObject::connect(&mapScaleHandler, &MapScaleHandler::scaleChanged,
- m->rpPlot, &Scatterplot::setScale);
- QObject::connect(&mapScaleHandler, &MapScaleHandler::scaleChanged,
- m->splat, &VoronoiSplat::setScale);
- QObject::connect(&mapScaleHandler, &MapScaleHandler::scaleChanged,
- m->bundlePlot, &LinePlot::setScale);
- QObject::connect(m->projectionHistory, &ProjectionHistory::currentMapChanged,
- &mapScaleHandler, &MapScaleHandler::scaleToMap);
-
- // Update projection as the cp are modified (either directly in the
- // manipulationHandler object or interactively in cpPlot
+ mapScaleHandler.scaleChanged.connect(
+ std::bind(&Scatterplot::setScale, &cpPlot, std::placeholders::_1, std::placeholders::_2));
+ mapScaleHandler.scaleChanged.connect(
+ std::bind(&Scatterplot::setScale, &rpPlot, std::placeholders::_1, std::placeholders::_2));
+ mapScaleHandler.scaleChanged.connect(
+ std::bind(&VoronoiSplat::setScale, &splat, std::placeholders::_1, std::placeholders::_2));
+ history.currentMapChanged.connect(
+ std::bind(&MapScaleHandler::scaleToMap, &mapScaleHandler, std::placeholders::_1));
+
+ // Update projection as the cp are modified (either directly in
+ // the manipulationHandler object or interactively in cpPlot
ManipulationHandler manipulationHandler(X, cpIndices);
- QObject::connect(m->cpPlot, &Scatterplot::xyInteractivelyChanged,
- &manipulationHandler, &ManipulationHandler::setCP);
-
+ cpPlot.xyInteractivelyChanged.connect(
+ std::bind(&ManipulationHandler::setCP, &manipulationHandler, std::placeholders::_1));
// Update history whenever a new projection is computed...
- QObject::connect(&manipulationHandler, &ManipulationHandler::mapChanged,
- m->projectionHistory, &ProjectionHistory::addMap);
-
+ manipulationHandler.mapChanged.connect(
+ std::bind(&ProjectionHistory::addMap, &history, std::placeholders::_1));
// ... and update visual components whenever the history changes
- QObject::connect(m->projectionHistory, &ProjectionHistory::currentMapChanged,
- m, &Main::updateMap);
- QObject::connect(m->projectionHistory, &ProjectionHistory::currentMapChanged,
- [m](const arma::mat &Y) {
- // ... and bundling
- overviewBundles(m);
- });
+ history.currentMapChanged.connect(
+ std::bind(&Main::updateMap, &m, std::placeholders::_1));
// Linking between selections
+ /*
SelectionHandler cpSelectionHandler(cpIndices.n_elem);
- QObject::connect(m->cpPlot, &Scatterplot::selectionInteractivelyChanged,
+ connect(m->cpPlot, &Scatterplot::selectionInteractivelyChanged,
&cpSelectionHandler, &SelectionHandler::setSelection);
- QObject::connect(m->cpBarChart, &BarChart::selectionInteractivelyChanged,
+ connect(m->cpBarChart, &BarChart::selectionInteractivelyChanged,
&cpSelectionHandler, &SelectionHandler::setSelection);
- QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
+ connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
m->cpPlot, &Scatterplot::setSelection);
- QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
+ connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
m->cpBarChart, &BarChart::setSelection);
- QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
+ connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
[m](const std::vector<bool> &cpSelection) {
// given some CPs, see unexpected RPs *influenced by* them
std::vector<arma::uword> selectedCPIndices;
@@ -267,7 +443,6 @@ int main(int argc, char **argv)
}
if (selectedCPIndices.empty()) {
- overviewBundles(m);
return;
}
@@ -280,7 +455,7 @@ int main(int argc, char **argv)
arma::uvec indicesLargest = arma::sort_index(unreliability.cols(selectedCPs), "descending");
arma::uword numLargest = indicesLargest.n_elem * 0.01f;
indicesLargest = indicesLargest.subvec(0, numLargest-1);
- m->bundlePlot->setValues(unreliability(indicesLargest));
+ // m->bundlePlot->setValues(unreliability(indicesLargest));
const arma::uvec &cpIndices = m->projectionHistory->cpIndices();
arma::uvec CPs = cpIndices(selectedCPs(indicesLargest / unreliability.n_rows));
@@ -297,20 +472,23 @@ int main(int argc, char **argv)
indices[2*i + 0] = CPs(i);
indices[2*i + 1] = RPs(i);
}
- m->bundlePlot->setLines(indices, m->projectionHistory->Y());
+ // m->bundlePlot->setLines(indices, m->projectionHistory->Y());
});
+ */
+ // Same as before, but now for regular points
+ /*
SelectionHandler rpSelectionHandler(X.n_rows - cpIndices.n_elem);
- QObject::connect(m->rpPlot, &Scatterplot::selectionInteractivelyChanged,
- &rpSelectionHandler, &SelectionHandler::setSelection);
- QObject::connect(m->rpBarChart, &BarChart::selectionInteractivelyChanged,
- &rpSelectionHandler, &SelectionHandler::setSelection);
- QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged,
- m->rpPlot, &Scatterplot::setSelection);
- QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged,
- m->rpBarChart, &BarChart::setSelection);
+ rpPlot.selectionInteractivelyChanged.connect(
+ std::bind(&SelectionHandler::setSelection, &rpSelectionHandler));
+ rpBarChart.selectionInteractivelyChanged.connect(
+ std::bind(&SelectionHandler::setSelection, &rpSelectionHandler));
+ rpSelectionHandler.selectionChanged.connect(
+ std::bind(&Scatterplot::setSelection, &rpPlot));
+ rpSelectionHandler.selectionChanged.connect(
+ std::bind(&BarChart::setSelection, &rpBarChart));
// This still needs more tests
- /*QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged,
+ QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged,
[m](const std::vector<bool> &rpSelection) {
// given some RPs, see unexpected CPs *influencing* them
std::vector<arma::uword> selectedRPIndices;
@@ -352,103 +530,163 @@ int main(int argc, char **argv)
indices[2*i + 1] = RPs(i);
}
m->bundlePlot->setLines(indices, m->projectionHistory->Y());
- });*/
+ });
+ */
// Brushing between each bar chart and respective scatterplot
BrushingHandler cpBrushHandler;
- QObject::connect(m->cpPlot, &Scatterplot::itemInteractivelyBrushed,
- &cpBrushHandler, &BrushingHandler::brushItem);
- QObject::connect(m->cpBarChart, &BarChart::itemInteractivelyBrushed,
- &cpBrushHandler, &BrushingHandler::brushItem);
- QObject::connect(&cpBrushHandler, &BrushingHandler::itemBrushed,
- m->cpPlot, &Scatterplot::brushItem);
- QObject::connect(&cpBrushHandler, &BrushingHandler::itemBrushed,
- m->cpBarChart, &BarChart::brushItem);
+ auto cpBrushHandler_brushItem = std::bind(
+ &BrushingHandler::brushItem,
+ &cpBrushHandler,
+ std::placeholders::_1);
+ cpPlot.itemInteractivelyBrushed.connect(cpBrushHandler_brushItem);
+ cpBarChart.itemInteractivelyBrushed.connect(cpBrushHandler_brushItem);
+ cpBrushHandler.itemBrushed.connect(
+ std::bind(&Scatterplot::brushItem, &cpPlot, std::placeholders::_1));
+ cpBrushHandler.itemBrushed.connect(
+ std::bind(&BarChart::brushItem, &cpBarChart, std::placeholders::_1));
BrushingHandler rpBrushHandler;
- QObject::connect(m->rpPlot, &Scatterplot::itemInteractivelyBrushed,
- &rpBrushHandler, &BrushingHandler::brushItem);
- QObject::connect(m->rpBarChart, &BarChart::itemInteractivelyBrushed,
- &rpBrushHandler, &BrushingHandler::brushItem);
- QObject::connect(&rpBrushHandler, &BrushingHandler::itemBrushed,
- m->rpPlot, &Scatterplot::brushItem);
- QObject::connect(&rpBrushHandler, &BrushingHandler::itemBrushed,
- m->rpBarChart, &BarChart::brushItem);
+ auto rpBrushHandler_brushItem = std::bind(
+ &BrushingHandler::brushItem,
+ &rpBrushHandler,
+ std::placeholders::_1);
+ rpPlot.itemInteractivelyBrushed.connect(rpBrushHandler_brushItem);
+ rpBarChart.itemInteractivelyBrushed.connect(rpBrushHandler_brushItem);
+ rpBrushHandler.itemBrushed.connect(
+ std::bind(&Scatterplot::brushItem, &rpPlot, std::placeholders::_1));
+ rpBrushHandler.itemBrushed.connect(
+ std::bind(&BarChart::brushItem, &rpBarChart, std::placeholders::_1));
// Update visual components whenever values change
- QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesChanged,
- [m](const arma::vec &v, bool rescale) {
- ColorScale *ptr = m->colorScaleRPs.get();
- if (!ptr || v.n_elem == 0) {
- return;
- }
-
- if (rescale) {
- ptr->setExtents(v.min(), v.max());
- } else {
- ptr->setExtents(std::min(ptr->min(), (float) v.min()),
- std::max(ptr->max(), (float) v.max()));
- }
-
- m->splat->setColorScale(ptr);
- m->rpColormap->setColorScale(ptr);
- });
- QObject::connect(m->projectionHistory, &ProjectionHistory::cpValuesChanged,
- [m](const arma::vec &v) {
- ColorScale *ptr = m->colorScaleCPs.get();
- if (!ptr || v.n_elem == 0) {
- return;
- }
-
- ptr->setExtents(v.min(), v.max());
- m->cpColormap->setColorScale(ptr);
- });
- QObject::connect(m->projectionHistory, &ProjectionHistory::cpValuesChanged,
- m->cpPlot, &Scatterplot::setColorData);
- QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesChanged,
- m->splat, &VoronoiSplat::setValues);
- QObject::connect(m->projectionHistory, &ProjectionHistory::cpValuesChanged,
- m->cpBarChart, &BarChart::setValues);
- QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesChanged,
- m->rpBarChart, &BarChart::setValues);
+ history.rpValuesChanged.connect([&](const arma::vec &v, bool rescale) {
+ if (!m.colorScaleRPs || v.n_elem == 0) {
+ return;
+ }
+
+ if (rescale) {
+ m.colorScaleRPs->setExtents(v.min(), v.max());
+ } else {
+ m.colorScaleRPs->setExtents(std::min(m.colorScaleRPs->min(), (float) v.min()),
+ std::max(m.colorScaleRPs->max(), (float) v.max()));
+ }
+
+ m.splat->setColorScale(m.colorScaleRPs);
+ m.rpColormap->setColorScale(m.colorScaleRPs);
+ });
+ history.cpValuesChanged.connect([&](const arma::vec &v, bool rescale) {
+ if (!m.colorScaleCPs || v.n_elem == 0) {
+ return;
+ }
+
+ m.colorScaleCPs->setExtents(v.min(), v.max());
+ m.cpColormap->setColorScale(m.colorScaleCPs);
+
+ // Originally, we should have done this:
+ //
+ // history.cpValuesChanged.connect(
+ // std::bind(&Scatterplot::setColorData, &cpPlot, std::placeholders::_1));
+ //
+ // But this has problems with the number of arguments of
+ // setColorData, so we just call it here
+ cpPlot.setColorData(v);
+ });
+ history.rpValuesChanged.connect(
+ std::bind(&VoronoiSplat::setValues, &splat, std::placeholders::_1));
+ history.cpValuesChanged.connect(
+ std::bind(&BarChart::setValues, &cpBarChart, std::placeholders::_1));
+ history.rpValuesChanged.connect(
+ std::bind(&BarChart::setValues, &rpBarChart, std::placeholders::_1));
// ProjectionHistory takes special care of separate CP/RP selections
- QObject::connect(&cpSelectionHandler, &SelectionHandler::selectionChanged,
- m->projectionHistory, &ProjectionHistory::setCPSelection);
- QObject::connect(&rpSelectionHandler, &SelectionHandler::selectionChanged,
- m->projectionHistory, &ProjectionHistory::setRPSelection);
- //QObject::connect(m->projectionHistory, &ProjectionHistory::selectionChanged,
- // m->bundlePlot, LinePlot::selectionChanged);
+ // cpSelectionHandler.selectionChanged.connect(
+ // std::bind(&ProjectionHistory::setCPSelection, &history));
+ // rpSelectionHandler.selectionChanged.connect(
+ // std::bind(&ProjectionHistory::setRPSelection, &history));
+ // history.selectionChanged.connect(
+ // std::bind(&LinePlot::selectionChanged, &bundlePlot));
// Connect projection components to rewinding mechanism
- QObject::connect(plotTC, &TransitionControl::tChanged,
- m->projectionHistory, &ProjectionHistory::setRewind);
- QObject::connect(m->projectionHistory, &ProjectionHistory::mapRewound,
- m, &Main::updateMap);
- QObject::connect(m->projectionHistory, &ProjectionHistory::cpValuesRewound,
- m->cpPlot, &Scatterplot::setColorData);
- QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesRewound,
- m->splat, &VoronoiSplat::setValues);
- QObject::connect(m->projectionHistory, &ProjectionHistory::rpValuesRewound,
- m->rpBarChart, &BarChart::updateValues);
+ // TransitionControl plotTC;
+ // plotTC.tChanged.connect(
+ // std::bind(&ProjectionHistory::setRewind, &history, std::placeholders::_1));
+ history.mapRewound.connect(
+ std::bind(&Main::updateMap, &m, std::placeholders::_1));
+ history.cpValuesRewound.connect(
+ std::bind(&Scatterplot::setColorData, &cpPlot, std::placeholders::_1));
+ history.rpValuesRewound.connect(
+ std::bind(&VoronoiSplat::setValues, &splat, std::placeholders::_1));
+ history.rpValuesRewound.connect(
+ std::bind(&BarChart::updateValues, &rpBarChart, std::placeholders::_1));
// General component set up
- plotTC->setAcceptedMouseButtons(Qt::RightButton);
- m->cpPlot->setDragEnabled(true);
- m->cpPlot->setAutoScale(false);
- m->rpPlot->setAutoScale(false);
- m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
- m->setSelectCPs();
+ // plotTC->setAcceptedMouseButtons(Qt::RightButton);
+ // m->cpPlot->setDragEnabled(true);
+ // m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ m.setSelectCPs();
- m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
- m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ // m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
+ // m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
- m->setCPColorScale(Main::ColorScaleRainbow);
- m->setRPColorScale(Main::ColorScaleRainbow);
+ m.setCPColorScale(Main::ColorScaleRainbow);
+ m.setRPColorScale(Main::ColorScaleRainbow);
// This sets the initial CP configuration, triggering all the necessary
// signals to set up the helper objects and visual components
manipulationHandler.setCP(Ys);
- return app.exec();
+ while (!glfwWindowShouldClose(win)) {
+ struct nk_vec2 scale;
+ glfwGetWindowSize(win, &width, &height);
+ glfwGetFramebufferSize(win, &display_width, &display_height);
+ scale.x = static_cast<float>(display_width) / static_cast<float>(width);
+ scale.y = static_cast<float>(display_height) / static_cast<float>(height);
+
+ // Input
+ glfwPollEvents();
+ {
+ double x, y;
+ nk_input_begin(&ctx);
+ // glfwPollEvents();
+ nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
+ if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
+ glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
+ nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
+ nk_input_key(&ctx, NK_KEY_SHIFT, 1);
+ } else {
+ nk_input_key(&ctx, NK_KEY_COPY, 0);
+ nk_input_key(&ctx, NK_KEY_PASTE, 0);
+ nk_input_key(&ctx, NK_KEY_CUT, 0);
+ nk_input_key(&ctx, NK_KEY_SHIFT, 0);
+ }
+ glfwGetCursorPos(win, &x, &y);
+ int int_x = static_cast<int>(x);
+ int int_y = static_cast<int>(y);
+ nk_input_motion(&ctx, int_x, int_y);
+ nk_input_button(&ctx, NK_BUTTON_LEFT, int_x, int_y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
+ nk_input_button(&ctx, NK_BUTTON_MIDDLE, int_x, int_y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
+ nk_input_button(&ctx, NK_BUTTON_RIGHT, int_x, int_y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
+ nk_input_end(&ctx);
+ }
+
+ glViewport(0, 0, display_width, display_height);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
+ // device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON);
+ glfwSwapBuffers(win);
+ }
+
+ nk_free(&ctx);
+ glfwTerminate();
+ return 0;
}