From fb23c8d47f6dcef429423256d8dddcc0f7184fc4 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Sun, 4 Jun 2023 13:02:14 +0200 Subject: Further advances in nuklear port. Rendering now looks similar to Qt version, needs a few tweaks: * Proper multisampling * Background Missing features: * Barcharts * Interactivity (e.g. brushing/linking in all objects) * History view of interactions --- main.cpp | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 583 insertions(+), 22 deletions(-) (limited to 'main.cpp') diff --git a/main.cpp b/main.cpp index 5f76a74..17c6597 100644 --- a/main.cpp +++ b/main.cpp @@ -8,12 +8,34 @@ #define GLAD_GL_IMPLEMENTATION #include +#undef GLAD_GL_IMPLEMENTATION #define GLFW_INCLUDE_NONE #include +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_SQRT sqrt +#define NK_SIN sinf +#define NK_COS cosf +// #define NK_STRTOD strtod +// #define NK_DTOA dtoa #define NK_IMPLEMENTATION #include "nuklear.h" +// device (NK) implementation details +static const int MAX_VERTEX_MEMORY = 512 * 1024; +static const int MAX_ELEMENT_MEMORY = 128 * 1024; + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" @@ -36,6 +58,275 @@ static const int RNG_SEED = 123; static const int WINDOW_WIDTH = 1200; static const int WINDOW_HEIGHT = 800; +struct media { + struct nk_font *font_14; + struct nk_font *font_18; + struct nk_font *font_20; + struct nk_font *font_22; + + struct nk_image unchecked; + struct nk_image checked; + struct nk_image rocket; + struct nk_image cloud; + struct nk_image pen; + struct nk_image play; + struct nk_image pause; + struct nk_image stop; + struct nk_image prev; + struct nk_image next; + struct nk_image tools; + struct nk_image dir; + struct nk_image copy; + struct nk_image convert; + struct nk_image del; + struct nk_image edit; + struct nk_image images[9]; + struct nk_image menu[6]; +}; +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct device { + struct nk_buffer cmds; + struct nk_draw_null_texture tex_null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static void +ui_header(struct nk_context *ctx, struct media *media, const char *title) +{ + nk_style_set_font(ctx, &media->font_18->handle); + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, title, NK_TEXT_LEFT); +} + +static void +ui_widget(struct nk_context *ctx, struct media *media, float height) +{ + static const float ratio[] = {0.15f, 0.85f}; + nk_style_set_font(ctx, &media->font_22->handle); + nk_layout_row(ctx, NK_DYNAMIC, height, 2, ratio); + nk_spacing(ctx, 1); +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static void +device_shutdown(struct device *dev) +{ + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + struct nk_vec2 scale, enum nk_anti_aliasing AA) +{ + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.tex_null = dev->tex_null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + nk_buffer_clear(&dev->cmds); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + class Main { public: @@ -90,12 +381,27 @@ public: } enum ColorScaleType { - ColorScaleCategorical, + ColorScaleCategorical = 0, ColorScaleContinuous, ColorScaleDivergent, ColorScaleRainbow }; + static constexpr const char *colormapItemNames[] = { + "Categorical", + "Continuous", + "Divergent", + "Rainbow", + nullptr, + }; + + static constexpr const char *metricItemNames[] = { + "Aggregate error", + "Ctrl. Pt. influence", + "Stress", + nullptr, + }; + void setCPColorScale(ColorScaleType colorScaleType) { float min = 0.0f; float max = 1.0f; @@ -106,10 +412,10 @@ public: colorScaleCPs = getColorScale(colorScaleType); colorScaleCPs->setExtents(min, max); - cpPlot->setColorScale(colorScaleCPs); - cpBarChart->setColorScale(colorScaleCPs); + // cpBarChart->setColorScale(colorScaleCPs); cpColormap->setColorScale(colorScaleCPs); // bundlePlot->setColorScale(colorScaleCPs); + cpPlot->update(); } void setRPColorScale(ColorScaleType colorScaleType) { @@ -123,10 +429,10 @@ public: colorScaleRPs = getColorScale(colorScaleType); colorScaleRPs->setExtents(min, max); - rpPlot->setColorScale(colorScaleRPs); - splat->setColorScale(colorScaleRPs); - rpBarChart->setColorScale(colorScaleRPs); + // rpBarChart->setColorScale(colorScaleRPs); rpColormap->setColorScale(colorScaleRPs); + rpPlot->update(); + splat->update(); } // Pointers to visual components whose values are set in the main() function @@ -238,6 +544,31 @@ private: std::string m_indicesSavePath, m_cpSavePath; }; +static void checkGLError(const char *msg) { + GLenum error = glGetError(); + std::cout << msg << " "; + switch (error) { + case GL_NO_ERROR: + std::cout << "GL_NO_ERROR" << std::endl; + break; + case GL_INVALID_ENUM: + std::cout << "GL_INVALID_ENUM" << std::endl; + break; + case GL_INVALID_VALUE: + std::cout << "GL_INVALID_VALUE" << std::endl; + break; + case GL_INVALID_OPERATION: + std::cout << "GL_INVALID_OPERATION" << std::endl; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION" << std::endl; + break; + case GL_OUT_OF_MEMORY: + std::cout << "GL_OUT_OF_MEMORY" << std::endl; + break; + } +} + arma::uvec extractCPs(const arma::mat &X) { int numCPs = (int) (3 * sqrt(X.n_rows)); @@ -247,7 +578,7 @@ arma::uvec extractCPs(const arma::mat &X) return indices.subvec(0, numCPs-1); } -arma::mat standardize(const arma::mat &X) +arma::mat standardise(const arma::mat &X) { arma::mat stdX = X; for (arma::uword j = 0; j < X.n_cols; j++) { @@ -284,9 +615,9 @@ int main(int argc, char *argv[]) int display_width = 0, display_height = 0; // GUI - // struct device device; - // struct nk_font_atlas atlas; - // struct media media; + struct device device; + struct nk_font_atlas atlas; + struct media media; struct nk_context ctx; NK_UNUSED(argc); @@ -303,13 +634,13 @@ int main(int argc, char *argv[]) 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); +#endif glfwWindowHint(GLFW_RED_BITS, 8); glfwWindowHint(GLFW_GREEN_BITS, 8); glfwWindowHint(GLFW_BLUE_BITS, 8); glfwWindowHint(GLFW_ALPHA_BITS, 8); - glfwWindowHint(GLFW_SAMPLES, 8); + glfwWindowHint(GLFW_SAMPLES, 4); win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "pm", NULL, NULL); if (!win) { @@ -342,15 +673,16 @@ int main(int argc, char *argv[]) exit(1); } + glEnable(GL_MULTISAMPLE); glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); Main &m = Main::instance(); - if (!m.loadDataset("wdbc-std.tbl")) { + if (argc != 2 || !m.loadDataset(argv[1])) { std::cerr << "Could not load data." << std::endl; return 1; } - const arma::mat &X = standardize(m.X()); + const arma::mat &X = standardise(m.X()); arma::arma_rng::set_seed(RNG_SEED); arma::uvec cpIndices; @@ -371,7 +703,7 @@ int main(int argc, char *argv[]) // TODO: maybe put this inside m->setCP() method; for now, will be outside // for more flexibility - Ys = arma::normalise(Ys); + // Ys = arma::normalise(Ys); // Sort indices (some operations become easier later) arma::uvec cpSortedIndices = arma::sort_index(cpIndices); @@ -385,13 +717,23 @@ int main(int argc, char *argv[]) ProjectionHistory history(X, cpIndices); // Visual components + Colormap cpColormap, rpColormap; Scatterplot cpPlot, rpPlot; VoronoiSplat splat; - Colormap cpColormap, rpColormap; + cpPlot.setSize(512, 512); + rpPlot.setSize(512, 512); + rpPlot.setGlyphSize(3.0f); + rpPlot.setColormap(rpColormap.texture()); + splat.setSize(512, 512); + splat.setColormap(rpColormap.texture()); BarChart cpBarChart, rpBarChart; m.cpPlot = &cpPlot; m.rpPlot = &rpPlot; m.splat = &splat; + m.cpColormap = &cpColormap; + m.rpColormap = &rpColormap; + m.cpBarChart = &cpBarChart; + m.rpBarChart = &rpBarChart; auto setCP = std::bind(&Main::setCP, &m, std::placeholders::_1); cpPlot.xyChanged.connect(setCP); @@ -571,8 +913,8 @@ int main(int argc, char *argv[]) std::max(m.colorScaleRPs->max(), (float) v.max())); } - m.splat->setColorScale(m.colorScaleRPs); m.rpColormap->setColorScale(m.colorScaleRPs); + // rpPlot.setColorData(v); }); history.cpValuesChanged.connect([&](const arma::vec &v, bool rescale) { if (!m.colorScaleCPs || v.n_elem == 0) { @@ -629,12 +971,40 @@ int main(int argc, char *argv[]) // m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); m.setCPColorScale(Main::ColorScaleRainbow); + enum Main::ColorScaleType cpColorScaleType = Main::ColorScaleRainbow; m.setRPColorScale(Main::ColorScaleRainbow); + enum Main::ColorScaleType rpColorScaleType = 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); + + // GUI init + device_init(&device); + { + const void *image; + int w, h; + struct nk_font_config cfg = nk_font_config(0); + cfg.oversample_h = 3; + cfg.oversample_v = 2; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + media.font_14 = nk_font_atlas_add_from_file(&atlas, "assets/Roboto-Regular.ttf", 14.0f, &cfg); + media.font_18 = nk_font_atlas_add_from_file(&atlas, "assets/Roboto-Regular.ttf", 18.0f, &cfg); + media.font_20 = nk_font_atlas_add_from_file(&atlas, "assets/Roboto-Regular.ttf", 20.0f, &cfg); + media.font_22 = nk_font_atlas_add_from_file(&atlas, "assets/Roboto-Regular.ttf", 22.0f, &cfg); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.tex_null); + } + nk_init_default(&ctx, &media.font_14->handle); + + struct nk_image cpPlot_img = nk_image_id((int) cpPlot.texture()); + struct nk_image rpPlot_img = nk_image_id((int) rpPlot.texture()); + struct nk_image splat_img = nk_image_id((int) splat.texture()); + struct nk_image cpColormap_img = nk_image_id((int) cpColormap.texture()); + struct nk_image rpColormap_img = nk_image_id((int) rpColormap.texture()); while (!glfwWindowShouldClose(win)) { struct nk_vec2 scale; glfwGetWindowSize(win, &width, &height); @@ -673,19 +1043,210 @@ int main(int argc, char *argv[]) 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_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); } + nk_style_set_font(&ctx, &media.font_20->handle); + if (nk_begin(&ctx, "Control points", nk_rect(550, 20, 350, 300), + NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { + ui_header(&ctx, &media, "Glyphs"); + nk_layout_row_dynamic(&ctx, 30, 1); + float glyphSize = cpPlot.glyphSize(); + nk_property_float(&ctx, "Size:", 1.0f, &glyphSize, 20.0f, 1.0f, 0.1f); + if (glyphSize != cpPlot.glyphSize()) { + cpPlot.setGlyphSize(glyphSize); + } + + nk_layout_row_dynamic(&ctx, 30, 1); + float opacity = 1.0f; + nk_property_float(&ctx, "Opacity:", 0.0f, &opacity, 1.0f, 0.1f, 0.01f); + // TODO + + nk_layout_row_dynamic(&ctx, 20, 1); + nk_spacer(&ctx); + + ui_header(&ctx, &media, "Colors"); + nk_layout_row_begin(&ctx, NK_DYNAMIC, 30, 2); + nk_layout_row_push(&ctx, 0.3f); + nk_label(&ctx, "Scale:", NK_TEXT_RIGHT); + nk_layout_row_push(&ctx, 0.7f); + if (nk_combo_begin_label(&ctx, Main::colormapItemNames[cpColorScaleType], + nk_vec2(nk_widget_width(&ctx), 200))) { + nk_layout_row_dynamic(&ctx, 25, 1); + size_t i = 0; + for (const char * const *name = &Main::colormapItemNames[0]; + *name != nullptr; name++) { + if (nk_combo_item_label(&ctx, *name, NK_TEXT_LEFT)) { + enum Main::ColorScaleType type = static_cast(i); + if (type != cpColorScaleType) { + m.setCPColorScale(type); + cpColorScaleType = type; + } + } + i++; + } + nk_combo_end(&ctx); + } + nk_layout_row_end(&ctx); + + nk_layout_row_begin(&ctx, NK_DYNAMIC, 30, 2); + nk_layout_row_push(&ctx, 0.3f); + nk_label(&ctx, "Map to:", NK_TEXT_RIGHT); + nk_layout_row_push(&ctx, 0.7f); + if (nk_combo_begin_label(&ctx, Main::metricItemNames[0], + nk_vec2(nk_widget_width(&ctx), 200))) { + nk_layout_row_dynamic(&ctx, 25, 1); + size_t i = 0; + for (const char * const *name = &Main::metricItemNames[0]; + *name != nullptr; name++) { + if (nk_combo_item_label(&ctx, *name, NK_TEXT_LEFT)) { + // TODO + } + i++; + } + nk_combo_end(&ctx); + } + nk_layout_row_end(&ctx); + } + nk_end(&ctx); + + if (nk_begin(&ctx, "Regular points", nk_rect(550, 340, 350, 400), + NK_WINDOW_BORDER | NK_WINDOW_TITLE)) { + ui_header(&ctx, &media, "Splat"); + nk_layout_row_dynamic(&ctx, 30, 1); + float alpha = splat.alpha(); + nk_property_float(&ctx, "Blur (alpha):", 5.0f, &alpha, 50.0f, 1.0f, 0.1f); + if (alpha != splat.alpha()) { + splat.setAlpha(alpha); + } + + nk_layout_row_dynamic(&ctx, 30, 1); + float beta = splat.beta(); + nk_property_float(&ctx, "Radius (beta):", 5.0f, &beta, 50.0f, 1.0f, 0.1f); + if (beta != splat.beta()) { + splat.setBeta(beta); + } + + nk_layout_row_dynamic(&ctx, 20, 1); + nk_spacer(&ctx); + + ui_header(&ctx, &media, "Glyphs"); + nk_layout_row_dynamic(&ctx, 30, 1); + float glyphSize = rpPlot.glyphSize(); + nk_property_float(&ctx, "Size:", 1.0f, &glyphSize, 20.0f, 1.0f, 0.1f); + if (glyphSize != rpPlot.glyphSize()) { + rpPlot.setGlyphSize(glyphSize); + } + + nk_layout_row_dynamic(&ctx, 30, 1); + float opacity = 1.0f; + nk_property_float(&ctx, "Opacity:", 0.0f, &opacity, 1.0f, 0.1f, 0.01f); + // TODO + + nk_layout_row_dynamic(&ctx, 20, 1); + nk_spacer(&ctx); + + ui_header(&ctx, &media, "Colors"); + nk_layout_row_begin(&ctx, NK_DYNAMIC, 30, 2); + nk_layout_row_push(&ctx, 0.3f); + nk_label(&ctx, "Scale:", NK_TEXT_RIGHT); + nk_layout_row_push(&ctx, 0.7f); + if (nk_combo_begin_label(&ctx, Main::colormapItemNames[rpColorScaleType], + nk_vec2(nk_widget_width(&ctx), 200))) { + nk_layout_row_dynamic(&ctx, 25, 1); + size_t i = 0; + for (const char * const *name = &Main::colormapItemNames[0]; + *name != nullptr; name++) { + if (nk_combo_item_label(&ctx, *name, NK_TEXT_LEFT)) { + enum Main::ColorScaleType type = static_cast(i); + if (type != rpColorScaleType) { + m.setRPColorScale(type); + rpColorScaleType = type; + } + } + i++; + } + nk_combo_end(&ctx); + } + nk_layout_row_end(&ctx); + + nk_layout_row_begin(&ctx, NK_DYNAMIC, 30, 2); + nk_layout_row_push(&ctx, 0.3f); + nk_label(&ctx, "Map to:", NK_TEXT_RIGHT); + nk_layout_row_push(&ctx, 0.7f); + if (nk_combo_begin_label(&ctx, Main::metricItemNames[0], + nk_vec2(nk_widget_width(&ctx), 200))) { + nk_layout_row_dynamic(&ctx, 25, 1); + size_t i = 0; + for (const char * const *name = &Main::metricItemNames[0]; + *name != nullptr; name++) { + if (nk_combo_item_label(&ctx, *name, NK_TEXT_LEFT)) { + // TODO + } + i++; + } + nk_combo_end(&ctx); + } + nk_layout_row_end(&ctx); + } + nk_end(&ctx); + + if (nk_begin(&ctx, "Scatterplot", nk_rect(20, 20, splat.width(), splat.height()), + NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR)) { + struct nk_command_buffer *canvas = nk_window_get_canvas(&ctx); + struct nk_rect region{0.0f, 0.0f, + static_cast(splat.width()), + static_cast(splat.height())}; + + // Render components to their textures + splat.draw(); + rpPlot.draw(); + cpPlot.draw(); + + // Add white background rect and draw textures on top + nk_layout_space_begin(&ctx, NK_STATIC, region.h, 4); + nk_layout_space_push(&ctx, region); + nk_fill_rect(canvas, region, 0.0f, nk_rgba(255, 255, 255, 255)); + nk_layout_space_push(&ctx, region); + nk_image(&ctx, splat_img); + nk_layout_space_push(&ctx, region); + nk_image(&ctx, rpPlot_img); + nk_layout_space_push(&ctx, region); + nk_image(&ctx, cpPlot_img); + nk_layout_space_end(&ctx); + } + nk_end(&ctx); + + if (nk_begin(&ctx, "Colormap", nk_rect(20, 40 + splat.height(), splat.width(), 120), + NK_WINDOW_BORDER | NK_WINDOW_TITLE | NK_WINDOW_NO_SCROLLBAR)) { + // nk_widget(&bounds, &ctx); + ui_header(&ctx, &media, "Control points"); + nk_layout_row_dynamic(&ctx, 10, 1); + nk_image(&ctx, cpColormap_img); + + ui_header(&ctx, &media, "Regular points"); + nk_layout_row_dynamic(&ctx, 10, 1); + nk_image(&ctx, rpColormap_img); + } + nk_end(&ctx); + glViewport(0, 0, display_width, display_height); - glClear(GL_COLOR_BUFFER_BIT); + // glDisable(GL_BLEND); glClearColor(0.3f, 0.3f, 0.3f, 1.0f); - // device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + // nk_clear(&ctx); } + nk_font_atlas_clear(&atlas); nk_free(&ctx); glfwTerminate(); return 0; -- cgit v1.2.3