From 0f34fd437efb936ef29ac91186321aa7251fbfb1 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Tue, 23 May 2023 11:22:33 +0200 Subject: Massive changes in initial port away from Qt. --- main.cpp | 758 +++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 498 insertions(+), 260 deletions(-) (limited to 'main.cpp') diff --git a/main.cpp b/main.cpp index 5f7693d..5f76a74 100644 --- a/main.cpp +++ b/main.cpp @@ -1,38 +1,242 @@ #include +#include #include +#include #include #include #include -#include -#include -#include -#include +#define GLAD_GL_IMPLEMENTATION +#include +#define GLFW_INCLUDE_NONE +#include + +#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 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 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 ®ularPoints = 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 getColorScale(ColorScaleType colorScaleType) { + switch (colorScaleType) { + case ColorScaleCategorical: + return std::shared_ptr(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( + ContinuousColorScale::builtin( + ContinuousColorScale::HeatedObjects, nullptr)); + case ColorScaleDivergent: + return std::shared_ptr( + DivergentColorScale::builtin( + DivergentColorScale::RedGrayBlue, nullptr)); + case ColorScaleRainbow: + // fall-through + default: + return std::shared_ptr( + 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("PM", 1, 0, "Scatterplot"); - qmlRegisterType("PM", 1, 0, "BarChart"); - qmlRegisterType("PM", 1, 0, "VoronoiSplat"); - qmlRegisterType("PM", 1, 0, "LinePlot"); - qmlRegisterType("PM", 1, 0, "Colormap"); - qmlRegisterType("PM", 1, 0, "TransitionControl"); - qmlRegisterSingletonType
("PM", 1, 0, "Main", mainProvider); - QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml")); - - // Initialize pointers to visual components - m->cpPlot = engine.rootObjects()[0]->findChild("cpPlot"); - m->rpPlot = engine.rootObjects()[0]->findChild("rpPlot"); - m->splat = engine.rootObjects()[0]->findChild("splat"); - m->bundlePlot = engine.rootObjects()[0]->findChild("bundlePlot"); - m->cpColormap = engine.rootObjects()[0]->findChild("cpColormap"); - m->rpColormap = engine.rootObjects()[0]->findChild("rpColormap"); - m->cpBarChart = engine.rootObjects()[0]->findChild("cpBarChart"); - m->rpBarChart = engine.rootObjects()[0]->findChild("rpBarChart"); - TransitionControl *plotTC = engine.rootObjects()[0]->findChild("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 &cpSelection) { // given some CPs, see unexpected RPs *influenced by* them std::vector 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 &rpSelection) { // given some RPs, see unexpected CPs *influencing* them std::vector 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(display_width) / static_cast(width); + scale.y = static_cast(display_height) / static_cast(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(x); + int int_y = static_cast(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; } -- cgit v1.2.3