From 809d4bc9d630a5a2b60b5632023f306f30bffc95 Mon Sep 17 00:00:00 2001 From: Kmilo9999 Date: Mon, 1 Aug 2022 16:10:07 -0400 Subject: [PATCH 1/5] changes to compile against vr-imgui 2.0 --- include/UI/UIView.h | 2 +- libs/UIHelpers/embedded_colormaps.h | 2 + .../transfer_function_multichannel_widget.h | 2 +- libs/UIHelpers/transfer_function_widget.cpp | 847 ++++++++---------- libs/UIHelpers/transfer_function_widget.h | 132 +-- src/UI/UIView.cpp | 8 +- 6 files changed, 443 insertions(+), 550 deletions(-) diff --git a/include/UI/UIView.h b/include/UI/UIView.h index ae6249b..d0b81c9 100644 --- a/include/UI/UIView.h +++ b/include/UI/UIView.h @@ -202,7 +202,7 @@ class UIView bool m_show_menu; bool m_renderVolume; std::vector tfn_widget_multi; - std::vector tfn_widget; + std::vector tfn_widget; std::vector m_tfns; std::vector> m_selected_volume_TrFn; diff --git a/libs/UIHelpers/embedded_colormaps.h b/libs/UIHelpers/embedded_colormaps.h index e944c76..70da5f1 100644 --- a/libs/UIHelpers/embedded_colormaps.h +++ b/libs/UIHelpers/embedded_colormaps.h @@ -1,4 +1,5 @@ #pragma once +namespace tfnw { uint8_t paraview_cool_warm[] = { 0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x1,0x8,0x6,0x0,0x0,0x0,0x77,0xca,0x84,0xf4,0x0,0x0,0x1,0x7a,0x49,0x44,0x41,0x54,0x78,0x5e,0x63,0xb4,0xf6,0x39,0xf0,0x9f,0x91,0x89,0x89,0x81,0x91,0x89,0x91,0x81,0x99,0x99,0x99,0x1,0xc4,0x66,0x2,0xd3,0x8,0x3e,0x48,0xe,0x24,0xc6,0x4,0x54,0x83,0x90,0x63,0x62,0x40,0xe8,0x43,0xd6,0xf,0xd2,0x7,0xe4,0x33,0x32,0x42,0xcd,0x64,0x42,0xd0,0x8c,0xc,0x40,0x3b,0x20,0x7c,0x46,0x46,0x90,0x3a,0x98,0x1a,0x20,0xd,0xe5,0x83,0xec,0x60,0x4,0xab,0x43,0x88,0x21,0xf8,0xc,0xc,0x40,0x27,0x30,0x30,0x31,0x33,0x82,0x69,0xa0,0x33,0x19,0xc0,0xe6,0x30,0x31,0x30,0x80,0xd4,0x40,0xe4,0xa0,0x6a,0x98,0x60,0xf4,0x7f,0xb0,0x5a,0xb0,0x19,0x60,0x75,0xff,0x19,0x80,0xda,0x81,0xea,0x81,0x34,0x94,0xf,0x96,0x63,0xfc,0xcf,0xc0,0xc0,0xf0,0xf,0x28,0xf7,0x9f,0x1,0xc2,0xff,0x7,0xa6,0x19,0x19,0xff,0x43,0xc4,0x18,0x80,0xe2,0xc,0x7f,0x11,0x72,0x20,0x3e,0x23,0xc8,0xec,0x7f,0xc,0x4c,0xc,0x10,0x9a,0x11,0x44,0x33,0xfc,0x3,0x9b,0xd,0x67,0x83,0xe5,0x80,0xfa,0xfe,0x3,0xd5,0xfc,0x7,0xd2,0xc,0x50,0xfa,0x3f,0x50,0x1d,0x88,0xfd,0xf,0x2a,0x6,0xa6,0x81,0x62,0xff,0x20,0x6a,0x19,0x19,0x60,0x6c,0xa0,0xdf,0xfe,0xfd,0x61,0x0,0x99,0xc7,0x8,0x94,0x63,0x0,0xe9,0x3,0x9a,0x5,0x67,0x83,0xc4,0xfe,0x41,0xd4,0x32,0x0,0xcd,0x3,0xcb,0x43,0xc5,0x18,0xc0,0xea,0x81,0xfe,0xfa,0xfb,0x7,0x28,0x5,0xa4,0x81,0xea,0xc0,0x62,0xff,0x40,0x6c,0xa0,0x18,0x88,0xfe,0xfb,0x97,0xe1,0x3f,0xd0,0x4c,0x86,0x7f,0x10,0x75,0xff,0xff,0x83,0x68,0xa0,0x18,0x48,0x2d,0x48,0xe,0x24,0xe,0x94,0xff,0xff,0x7,0xa4,0xee,0x3f,0x3,0x88,0x6,0xd9,0xf1,0xf,0xc8,0x7,0x6a,0x4,0x32,0xff,0x81,0xc5,0xfe,0x3,0xd5,0xfd,0xff,0xb,0xd1,0x7,0x92,0x3,0xf3,0x51,0xc4,0xfe,0x41,0xd4,0xfe,0xfd,0xf,0xa1,0x81,0x72,0x20,0x75,0xff,0x80,0x7c,0x98,0xfa,0x7f,0x7f,0x80,0x6a,0xfe,0x82,0xd4,0xfd,0x67,0x0,0xb1,0xff,0x1,0xd9,0xff,0x7e,0xff,0x63,0xf8,0x7,0x52,0xfb,0xfb,0x2f,0x3,0x48,0xed,0x7f,0xa0,0xfa,0xbf,0xbf,0x40,0xea,0xfe,0x33,0xfc,0xfd,0x9,0x14,0xfb,0x3,0xa4,0xbf,0x3,0xf9,0xbf,0x80,0x7a,0x7e,0xfe,0x67,0x0,0x0,0x27,0xf7,0xf2,0xdf,0xa6,0x6f,0xf8,0x80,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -35,3 +36,4 @@ uint8_t ice_fire[] = { uint8_t nic_edge[] = { 0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x1,0x8,0x6,0x0,0x0,0x0,0x77,0xca,0x84,0xf4,0x0,0x0,0x1,0xf7,0x49,0x44,0x41,0x54,0x78,0x5e,0x63,0x34,0x34,0x34,0xfc,0xcf,0xf4,0x9f,0x8d,0x81,0xf9,0x1f,0x3b,0x3,0xcb,0x5f,0x5e,0x6,0x46,0x10,0xfb,0xf,0x90,0x6,0xf2,0x19,0x7f,0xf3,0x31,0x30,0xfc,0x65,0x67,0x60,0xf8,0xc3,0xce,0xf0,0xff,0x7,0x1f,0xc3,0xff,0x9f,0x40,0xfa,0x27,0x37,0x3,0x3,0x23,0x33,0x10,0x73,0x0,0x31,0xb,0x18,0xff,0x67,0xe2,0xc0,0xa0,0xff,0x33,0xb2,0x30,0x80,0x30,0x3,0x17,0x90,0x66,0x65,0x62,0xf8,0xcf,0xe,0xd4,0xc3,0x2,0xa4,0xd9,0x80,0x7c,0x10,0xcd,0xcc,0xc8,0xf0,0x8f,0x15,0xc8,0x66,0x66,0x82,0xd2,0x8c,0xc,0xcc,0xdc,0x8c,0xc,0x8c,0xac,0xc,0xc,0xc,0xac,0x40,0x9a,0x93,0x11,0x48,0x3,0xd9,0x40,0xfa,0x3f,0x50,0xec,0x1f,0x27,0x3,0xc3,0x3f,0x16,0x90,0x1e,0x6,0x86,0x5f,0x40,0x31,0x66,0xd6,0xff,0xc,0x6c,0x9c,0xff,0x19,0x98,0x80,0x72,0x4c,0x40,0x9a,0x11,0xc8,0x7,0x5a,0xc9,0xc0,0xc0,0xf5,0x9f,0xe1,0x3f,0x90,0xfd,0xf,0x28,0xf6,0xf,0x28,0xf7,0x9b,0xf3,0x1f,0x3,0x2b,0x13,0x23,0x10,0x33,0x30,0x70,0x0,0x9d,0xcb,0x2,0x64,0xb3,0x3,0x69,0xa0,0xb5,0xc,0x6c,0x40,0xf3,0x40,0x34,0x8,0xb3,0x2,0xdd,0xc3,0xc4,0x4,0xd4,0xe,0x94,0x67,0x3,0x5a,0xcb,0xd,0x72,0x6,0x3,0x23,0x3,0x88,0xcd,0x9,0xa4,0x41,0xce,0x2,0x3a,0x81,0x81,0x5,0xc8,0x6,0xfa,0x16,0x4c,0x3,0x8d,0x1,0x5a,0x7,0x34,0xff,0xff,0x7f,0x6,0xce,0xff,0x10,0x9a,0x3,0xc8,0x66,0x65,0xf8,0xcf,0xc0,0x2,0xa4,0x39,0x80,0x62,0x10,0xfa,0x3f,0x3,0x33,0x90,0xcf,0xc4,0xf8,0x9d,0x81,0x91,0xf1,0x37,0x3,0x13,0xe3,0xf,0x30,0xcd,0xc8,0xf0,0x87,0x81,0x91,0x9,0x28,0xc6,0xf0,0x1b,0x4a,0x43,0xf8,0xc,0x40,0x3e,0x30,0xd0,0x81,0xa6,0x7c,0x67,0x80,0xb0,0xbf,0x3,0xd9,0xbf,0x81,0x6c,0x18,0xfd,0x1b,0xc8,0xff,0x6,0xe4,0xff,0x2,0xd2,0x5f,0x81,0x1,0xf3,0xf,0xa8,0xc,0xc8,0xfe,0xf7,0x17,0xc8,0x6,0xe2,0x3f,0xbf,0x80,0xf1,0x6,0xa1,0xff,0x83,0xc4,0xfe,0xfc,0x4,0xf3,0xff,0xff,0x3,0x9a,0xff,0x13,0x88,0xff,0x0,0xa3,0xeb,0xfb,0x7f,0x6,0xa0,0x53,0x18,0x18,0xa1,0x34,0xc8,0x4a,0x10,0x1b,0x4c,0x7f,0xfb,0xf,0xb5,0x96,0x91,0xe1,0x3f,0x50,0x2d,0xc3,0x1f,0x20,0xfd,0x3,0x18,0x1f,0xbf,0x91,0xe8,0x6f,0x40,0xf6,0x6f,0x88,0xf3,0xfe,0x7f,0x5,0xb2,0x81,0x56,0xfe,0xf9,0xd,0x8c,0xdf,0xbf,0x40,0x5f,0x1,0x69,0xb0,0x53,0xfe,0x1,0xd9,0xbf,0x80,0xf1,0xb,0x34,0xe3,0xf,0x8c,0x6,0xea,0xfb,0xf7,0xb,0xe4,0x73,0x6,0x86,0x1f,0x20,0x79,0xa0,0x55,0xdf,0xa1,0xf4,0x8f,0xff,0x40,0xf1,0xff,0x20,0x9f,0x3,0xe5,0x40,0x34,0x90,0xf,0x74,0x1e,0xc3,0x2f,0xde,0xff,0xc,0x9f,0xf9,0xfe,0x31,0xfc,0x64,0xf9,0xcf,0xf0,0x13,0x98,0x8c,0x3e,0xb3,0x2,0xd9,0x4c,0x40,0x31,0x66,0x20,0x9f,0xf1,0x3f,0xc3,0x27,0xc6,0x7f,0xc,0x20,0xe7,0xfd,0x64,0xf8,0xcf,0x0,0x0,0xd4,0xca,0xd9,0x94,0x17,0xc3,0x0,0xd6,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; +} diff --git a/libs/UIHelpers/transfer_function_multichannel_widget.h b/libs/UIHelpers/transfer_function_multichannel_widget.h index 79c8356..5978b46 100644 --- a/libs/UIHelpers/transfer_function_multichannel_widget.h +++ b/libs/UIHelpers/transfer_function_multichannel_widget.h @@ -66,7 +66,7 @@ class TransferFunctionMultiChannelWidget { TransferFunctionMultiChannelWidget(); // Add a colormap preset. The image should be a 1D RGBA8 image - void add_colormap(const Colormap &map); + void add_colormap(const tfnw::Colormap &map); // Add the transfer function UI into the currently active window void draw_ui(); diff --git a/libs/UIHelpers/transfer_function_widget.cpp b/libs/UIHelpers/transfer_function_widget.cpp index e26104b..7f0e203 100644 --- a/libs/UIHelpers/transfer_function_widget.cpp +++ b/libs/UIHelpers/transfer_function_widget.cpp @@ -1,583 +1,468 @@ - -#ifdef _WIN32 -#include "GL/glew.h" -#include "GL/wglew.h" -#elif (!defined(__APPLE__)) -#include "GL/glxew.h" -#endif - -// OpenGL Headers #if defined(WIN32) #define NOMINMAX -#include -#include -#elif defined(__APPLE__) -#define GL_GLEXT_PROTOTYPES -#include -#include -#else -#define GL_GLEXT_PROTOTYPES -#include #endif - #include "transfer_function_widget.h" #include #include #include -#include -#include #include "embedded_colormaps.h" -#include "../../include/render/FontHandler.h" #ifndef TFN_WIDGET_NO_STB_IMAGE_IMPL #define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_RESIZE_IMPLEMENTATION #endif #include "stb_image.h" -#include "stb_image_resize.h" +#include "../../include/render/FontHandler.h" +#include +#include + +namespace tfnw { template -T clamp(T x, T min, T max) +inline T clamp(T x, T min, T max) { - if (x < min) - { - return min; - } - if (x > max) - { - return max; - } - return x; + if (x < min) { + return min; + } + if (x > max) { + return max; + } + return x; +} + +inline float srgb_to_linear(const float x) +{ + if (x <= 0.04045f) { + return x / 12.92f; + } else { + return std::pow((x + 0.055f) / 1.055f, 2.4f); + } } -Colormap::Colormap(const std::string &name, const std::vector &img) - : name(name), colormap(img) +Colormap::Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space) + : name(name), colormap(img), color_space(color_space) { } TransferFunctionWidget::TransferFunctionWidget() { - // Load up the embedded colormaps as the default options - load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); - load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); - load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); - load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); - load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); - load_embedded_preset(samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); - load_embedded_preset(samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); - load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); - load_embedded_preset(jet, sizeof(jet), "Jet"); - load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); - load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); - load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); - - // Initialize the colormap alpha channel w/ a linear ramp - update_colormap(); - - for (int i = 0; i < 256; i++) - { - current_histogram.push_back(0); - } - - m_min_max_val[0] = 0.0f; - m_min_max_val[1] = 1.0f; - - m_quantiles[0] = 0.05f; - m_quantiles[1] = 0.95f; + // Load up the embedded colormaps as the default options + load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); + load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); + load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); + load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); + load_embedded_preset( + samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); + load_embedded_preset( + samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); + load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); + load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); + load_embedded_preset(jet, sizeof(jet), "Jet"); + load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); + load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); + load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); + + // Initialize the colormap alpha channel w/ a linear ramp + update_colormap(); + + for (int i = 0; i < 256; i++) + { + current_histogram.push_back(0); + } + + m_min_max_val[0] = 0.0f; + m_min_max_val[1] = 1.0f; + + m_quantiles[0] = 0.05f; + m_quantiles[1] = 0.95f; } void TransferFunctionWidget::add_colormap(const Colormap &map) { - colormaps.push_back(map); + colormaps.push_back(map); + + if (colormaps.back().color_space == SRGB) { + Colormap &cmap = colormaps.back(); + cmap.color_space = LINEAR; + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); + } + } + } } void TransferFunctionWidget::draw_ui() { - update_gpu_image(); - colormap_changed = false; - - const ImGuiIO &io = ImGui::GetIO(); - - ImGui::Text("Transfer Function"); - ImGui::TextWrapped( - "Left click to add a point, right click remove. " - "Left click + drag to move points."); - - if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) - { - for (size_t i = 0; i < colormaps.size(); ++i) - { - if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) - { - selected_colormap = i; - update_colormap(); - } - } - ImGui::EndCombo(); - } + update_gpu_image(); - vec2f canvas_size = ImGui::GetContentRegionAvail(); - // Note: If you're not using OpenGL for rendering your UI, the setup for - // displaying the colormap texture in the UI will need to be updated. - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 80; + const ImGuiIO &io = ImGui::GetIO(); - const float point_radius = 20.f; + ImGui::Text("Transfer Function"); + ImGui::TextWrapped( + "Left click to add a point, right click remove. " + "Left click + drag to move points."); - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); + if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) { + for (size_t i = 0; i < colormaps.size(); ++i) { + if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) { + selected_colormap = i; + update_colormap(); + } + } + ImGui::EndCombo(); + } - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); + vec2f canvas_size = ImGui::GetContentRegionAvail(); + // Note: If you're not using OpenGL for rendering your UI, the setup for + // displaying the colormap texture in the UI will need to be updated. + size_t tmp = colormap_img; + ImGui::Image(reinterpret_cast(tmp), ImVec2(canvas_size.x, 16)); + vec2f canvas_pos = ImGui::GetCursorScreenPos(); + canvas_size.y -= 20; - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); + const float point_radius = 10.f; - ImGui::InvisibleButton("tfn_canvas", canvas_size); - if (ImGui::IsItemHovered() || selected_point != (size_t)-1) - { + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - vec2f mouse_pos = (vec2f(io.MousePos) - view_offset) / view_scale; - mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); - mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); + const vec2f view_scale(canvas_size.x, -canvas_size.y); + const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - if (io.MouseDown[0]) - { - if (selected_point != (size_t)-1) - { - alpha_control_pts[selected_point] = mouse_pos; + draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - // make sure point does not cross + ImGui::InvisibleButton("tfn_canvas", canvas_size); + + static bool clicked_on_item = false; + if (!io.MouseDown[0] && !io.MouseDown[1]) { + clicked_on_item = false; + } + if (ImGui::IsItemHovered() && (io.MouseDown[0] || io.MouseDown[1])) { + clicked_on_item = true; + } + + ImVec2 bbmin = ImGui::GetItemRectMin(); + ImVec2 bbmax = ImGui::GetItemRectMax(); + ImVec2 clipped_mouse_pos = ImVec2(std::min(std::max(io.MousePos.x, bbmin.x), bbmax.x), + std::min(std::max(io.MousePos.y, bbmin.y), bbmax.y)); + + if (clicked_on_item) { + vec2f mouse_pos = (vec2f(clipped_mouse_pos) - view_offset) / view_scale; + mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); + mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); + + if (io.MouseDown[0]) { + if (selected_point != (size_t)-1) { + alpha_control_pts[selected_point] = mouse_pos; + + // Keep the first and last control points at the edges + if (selected_point == 0) { + alpha_control_pts[selected_point].x = 0.f; + } else if (selected_point == alpha_control_pts.size() - 1) { + alpha_control_pts[selected_point].x = 1.f; + } + } else { + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + // No nearby point, we're adding a new one + if (fnd == alpha_control_pts.end()) { + alpha_control_pts.push_back(mouse_pos); + } + } - // Keep the first and last control points at the edges - if (selected_point == 0) - { - alpha_control_pts[selected_point].x = 0.f; - } - else if (selected_point == alpha_control_pts.size() - 1) - { - alpha_control_pts[selected_point].x = 1.f; - } - // make sure the other points do not cross - else - { - alpha_control_pts[selected_point].x = (alpha_control_pts[selected_point].x < alpha_control_pts[selected_point - 1].x) - ? alpha_control_pts[selected_point - 1].x + 0.01 - : alpha_control_pts[selected_point].x; - alpha_control_pts[selected_point].x = (alpha_control_pts[selected_point].x > alpha_control_pts[selected_point + 1].x) - ? alpha_control_pts[selected_point + 1].x - 0.01 - : alpha_control_pts[selected_point].x; - } - } - else - { - // See if we're selecting a point or adding one - if (io.MousePos.x - canvas_pos.x <= point_radius) - { - selected_point = 0; - } - else if (io.MousePos.x - canvas_pos.x >= canvas_size.x - point_radius) - { - selected_point = alpha_control_pts.size() - 1; - } - else - { - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); - // No nearby point, we're adding a new one - if (fnd == alpha_control_pts.end()) - { - alpha_control_pts.push_back(mouse_pos); // Keep alpha control points ordered by x coordinate, update // selected point index to match std::sort(alpha_control_pts.begin(), alpha_control_pts.end(), - [](const vec2f &a, const vec2f &b) - { return a.x < b.x; }); - if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) - { - fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); + [](const vec2f &a, const vec2f &b) { return a.x < b.x; }); + if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) { + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + selected_point = std::distance(alpha_control_pts.begin(), fnd); + } + update_colormap(); + } else if (ImGui::IsMouseClicked(1)) { + selected_point = -1; + // Find and remove the point + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + // We also want to prevent erasing the first and last points + if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && + fnd != alpha_control_pts.end() - 1) { + alpha_control_pts.erase(fnd); } - } - selected_point = std::distance(alpha_control_pts.begin(), fnd); + update_colormap(); + } else { + selected_point = -1; } - } - update_colormap(); - } - else if (ImGui::IsMouseClicked(1)) - { - selected_point = -1; - // Find and remove the point - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); - // We also want to prevent erasing the first and last points - if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && - fnd != alpha_control_pts.end() - 1) - { - alpha_control_pts.erase(fnd); - } - update_colormap(); - } - else - { - selected_point = -1; + } else { + selected_point = -1; } - } - - // Draw the alpha control points, and build the points for the polyline - // which connects them - std::vector polyline_pts; - for (const auto &pt : alpha_control_pts) - { - const vec2f pt_pos = pt * view_scale + view_offset; - polyline_pts.push_back(pt_pos); - draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); - } - - draw_list->AddPolyline(polyline_pts.data(), polyline_pts.size(), 0xFFFFFFFF, false, 2.f); - draw_list->PopClipRect(); - - // Add Label tick marks - int nbTicks = 5; - vec2f tick_pos = ImGui::GetCursorScreenPos(); - tick_pos.y -= ImGui::GetStyle().ItemSpacing.y; - vec2f tick_size = ImGui::GetContentRegionAvail(); - tick_size.y = 5; - draw_list->PushClipRect(tick_pos, ImVec2(tick_pos.x + tick_size.x, tick_pos.y + tick_size.y)); - - for (int i = 0; i < nbTicks; i++) - { - float percentage = float(i) / (nbTicks - 1); - - draw_list->AddLine(ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_size.y), - ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_pos.y + tick_size.y), ImColor(255, 255, 255, 255), 1); - } - draw_list->PopClipRect(); - - // Add Label text - const float ItemSpacing = ImGui::GetStyle().ItemSpacing.x; - for (int i = 0; i < nbTicks; i++) - { - float percentage = float(i) / (nbTicks - 1); - float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; - std::stringstream text; - text << std::fixed << std::setprecision(4) << val; - if (i == 0) - { - } - else if (i == nbTicks - 1) - { - ImGui::SameLine(ImGui::GetWindowWidth() - ItemSpacing - ImGui::CalcTextSize(text.str().c_str()).x); - } - else - { - ImGui::SameLine((ImGui::GetWindowWidth()) * percentage - ImGui::CalcTextSize(text.str().c_str()).x * 0.5); + + // Draw the alpha control points, and build the points for the polyline + // which connects them + std::vector polyline_pts; + for (const auto &pt : alpha_control_pts) { + const vec2f pt_pos = pt * view_scale + view_offset; + polyline_pts.push_back(pt_pos); + draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); } - ImGui::Text(text.str().c_str()); - } + draw_list->AddPolyline( + polyline_pts.data(), (int)polyline_pts.size(), 0xFFFFFFFF, false, 2.f); + draw_list->PopClipRect(); } bool TransferFunctionWidget::changed() const { - return colormap_changed; + return colormap_changed; } std::vector TransferFunctionWidget::get_colormap() { - return current_colormap; + colormap_changed = false; + return current_colormap; } std::vector TransferFunctionWidget::get_colormapf() { - std::vector colormapf(current_colormap.size(), 0.f); - for (size_t i = 0; i < current_colormap.size(); ++i) - { - colormapf[i] = current_colormap[i] / 255.f; - } - return colormapf; + colormap_changed = false; + std::vector colormapf(current_colormap.size(), 0.f); + for (size_t i = 0; i < current_colormap.size(); ++i) { + colormapf[i] = current_colormap[i] / 255.f; + } + return colormapf; } -void TransferFunctionWidget::setHistogram(const std::vector &hist) +void TransferFunctionWidget::draw_legend() { - current_histogram = hist; + GLint viewport[4]; + GLfloat projection[16]; + GLfloat modelview[16]; + + int min[2] = { 50, 100 }; + int max[2] = { 400, 120 }; + + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + glGetFloatv(GL_PROJECTION_MATRIX, &projection[0]); + glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, viewport[2], 0, viewport[3], -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture(GL_TEXTURE_2D, colormap_img); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(min[0], max[1], 0); + glTexCoord2f(1, 1); + glVertex3f(max[0], max[1], 0); + glTexCoord2f(1, 0); + glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(0, 0); + glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); + glEnd(); + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindTexture(GL_TEXTURE_2D, colormap_img); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(1, 1); + glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(1, 0); + glVertex3f(max[0], min[1], 0); + glTexCoord2f(0, 0); + glVertex3f(min[0], min[1], 0); + glEnd(); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + // Add Label tick marks + int nbTicks = 4; + float diff = (max[0] - min[0]) / nbTicks; + for (int i = 0; i <= nbTicks; i++) + { + float pos_x = min[0] + i * diff; + glBegin(GL_LINES); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glVertex3f(pos_x, min[1], 0); + glVertex3f(pos_x, min[1] - 5, 0); + glEnd(); + + float percentage = float(i) / (nbTicks); + float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; + std::stringstream text; + text << std::fixed << std::setprecision(4) << val; + + FontHandler::getInstance()->renderTextBox(text.str(), pos_x - 100, min[1] - 15, 0, 200.0, 10, TextAlignment::CENTER); + } + + glEnable(GL_LIGHTING); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projection); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(modelview); } -std::vector &TransferFunctionWidget::getHistogram() +void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) { - return current_histogram; + bool show_legend = true; + ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); + ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); + ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); + ImGuiIO& io = ImGui::GetIO(); + ImTextureID my_tex_id = io.Fonts->TexID; + float texture_width = (float)io.Fonts->TexWidth; + float texture_height = (float)io.Fonts->TexHeight; + std::string max_str = std::to_string(m_min_max_val[1]); + float offset = 0.008 * (texture_width * max_str.size()); + ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); + ImGui::Text("%.0f", m_min_max_val[0]); + ImGui::SameLine((legend_width / 2)); + ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); + ImGui::SameLine(ImGui::GetWindowWidth() - offset); + ImGui::Text(" %.0f", m_min_max_val[1]); + ImGui::End(); } -void TransferFunctionWidget::setMinMax(const float min, const float max) -{ - m_min_max_val[0] = min; - m_min_max_val[1] = max; -} -void TransferFunctionWidget::setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha) +void TransferFunctionWidget::get_colormapf(std::vector &color, + std::vector &opacity) { - if (hist1.size() != hist2.size()) - return; - - current_histogram.clear(); - for (int i = 0; i < hist1.size(); i++) - current_histogram.push_back(hist1[i] * alpha + hist2[i] * (1.0f - alpha)); + colormap_changed = false; + color.resize((current_colormap.size() / 4) * 3); + opacity.resize(current_colormap.size() / 4); + for (size_t i = 0; i < current_colormap.size() / 4; ++i) { + color[i * 3] = current_colormap[i * 4] / 255.f; + color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; + color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; + opacity[i] = current_colormap[i * 4 + 3] / 255.f; + } } -void TransferFunctionWidget::get_colormapf(std::vector &color, std::vector &opacity) +void TransferFunctionWidget::update_gpu_image() { - color.resize((current_colormap.size() / 4) * 3); - opacity.resize(current_colormap.size() / 4); - for (size_t i = 0; i < current_colormap.size() / 4; ++i) - { - color[i * 3] = current_colormap[i * 4] / 255.f; - color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; - color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; - opacity[i] = current_colormap[i * 4 + 3] / 255.f; - } + GLint prev_tex_2d = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); + + if (colormap_img == (GLuint)-1) { + glGenTextures(1, &colormap_img); + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + if (gpu_image_stale) { + gpu_image_stale = false; + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB8, + (GLsizei)(current_colormap.size() / 4), + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + current_colormap.data()); + } + glBindTexture(GL_TEXTURE_2D, prev_tex_2d); } -void TransferFunctionWidget::draw_legend() +void TransferFunctionWidget::update_colormap() { - GLint viewport[4]; - GLfloat projection[16]; - GLfloat modelview[16]; - - int min[2] = {50, 100}; - int max[2] = {400, 120}; - - glGetIntegerv(GL_VIEWPORT, &viewport[0]); - glGetFloatv(GL_PROJECTION_MATRIX, &projection[0]); - glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, viewport[2], 0, viewport[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], max[1], 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], max[1], 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glEnd(); - glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, 0); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], min[1], 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], min[1], 0); - glEnd(); - glDisable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, 0); - - // Add Label tick marks - int nbTicks = 4; - float diff = (max[0] - min[0]) / nbTicks; - for (int i = 0; i <= nbTicks; i++) - { - float pos_x = min[0] + i * diff; - glBegin(GL_LINES); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glVertex3f(pos_x, min[1], 0); - glVertex3f(pos_x, min[1] - 5, 0); - glEnd(); - - float percentage = float(i) / (nbTicks); - float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; - std::stringstream text; - text << std::fixed << std::setprecision(4) << val; - - FontHandler::getInstance()->renderTextBox(text.str(), pos_x - 100, min[1] - 15, 0, 200.0, 10, TextAlignment::CENTER); - } - - glEnable(GL_LIGHTING); - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(projection); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(modelview); + colormap_changed = true; + gpu_image_stale = true; + current_colormap = colormaps[selected_colormap].colormap; + // We only change opacities for now, so go through and update the opacity + // by blending between the neighboring control points + auto a_it = alpha_control_pts.begin(); + const size_t npixels = current_colormap.size() / 4; + for (size_t i = 0; i < npixels; ++i) { + float x = static_cast(i) / npixels; + auto high = a_it + 1; + if (x > high->x) { + ++a_it; + ++high; + } + float t = (x - a_it->x) / (high->x - a_it->x); + float alpha = (1.f - t) * a_it->y + t * high->y; + current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); + } } -void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) +void TransferFunctionWidget::set_quantiles(float min, float max) { - bool show_legend = true; - ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); - ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); - ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); - ImGuiIO &io = ImGui::GetIO(); - ImTextureID my_tex_id = io.Fonts->TexID; - float texture_width = (float)io.Fonts->TexWidth; - float texture_height = (float)io.Fonts->TexHeight; - std::string max_str = std::to_string(m_min_max_val[1]); - float offset = 0.008 * (texture_width * max_str.size()); - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); - ImGui::Text("%.0f", m_min_max_val[0]); - ImGui::SameLine((legend_width / 2)); - ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); - ImGui::SameLine(ImGui::GetWindowWidth() - offset); - ImGui::Text(" %.0f", m_min_max_val[1]); - ImGui::End(); + m_quantiles[0] = min; + m_quantiles[1] = max; } -void TransferFunctionWidget::draw_histogram() +void TransferFunctionWidget::get_quantiles(float& min, float& max) { - - const ImGuiIO &io = ImGui::GetIO(); - - ImGui::Text("Histogram"); - - vec2f canvas_size = ImGui::GetContentRegionAvail(); - // Note: If you're not using OpenGL for rendering your UI, the setup for - // displaying the colormap texture in the UI will need to be updated. - // TODO(#45) ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 250; - - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - - ImGui::InvisibleButton("hstg_canvas", canvas_size); - - // Draw the alpha control points, and build the points for the polyline - // which connects them - - // Code to Draw histogram in the UI - for (int i = 0; i < current_histogram.size(); i++) - { - vec2f lp = vec2f(((float)i) / current_histogram.size(), 0.0f); - vec2f hp = vec2f(((float)i + 1.0f) / current_histogram.size(), current_histogram[i]); - draw_list->AddRectFilled(lp * view_scale + view_offset, hp * view_scale + view_offset, 0x77777777); - } - - draw_list->PopClipRect(); + min = m_quantiles[0]; + max = m_quantiles[1]; } -void TransferFunctionWidget::update_gpu_image() -{ - GLint prev_tex_2d = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); - - if (colormap_img == (GLuint)-1) - { - glGenTextures(1, &colormap_img); - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - if (colormap_changed) - { - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - current_colormap.size() / 4, - 1, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - current_colormap.data()); - } - if (prev_tex_2d != 0) - { - glBindTexture(GL_TEXTURE_2D, prev_tex_2d); - } -} -void TransferFunctionWidget::update_colormap() +void TransferFunctionWidget::setHistogram(const std::vector& hist) { - colormap_changed = true; - current_colormap = colormaps[selected_colormap].colormap; - // We only change opacities for now, so go through and update the opacity - // by blending between the neighboring control points - auto a_it = alpha_control_pts.begin(); - const size_t npixels = current_colormap.size() / 4; - - for (size_t i = 0; i < npixels; ++i) - { - float x = static_cast(i) / npixels; - auto high = a_it + 1; - if (x > high->x) - { - ++a_it; - ++high; - } - float t = (x - a_it->x) / (high->x - a_it->x); - float alpha = (1.f - t) * a_it->y + t * high->y; - current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); - } + current_histogram = hist; } -void TransferFunctionWidget::set_quantiles(float min, float max) +std::vector& TransferFunctionWidget::getHistogram() { - m_quantiles[0] = min; - m_quantiles[1] = max; + return current_histogram; } -void TransferFunctionWidget::get_quantiles(float &min, float &max) +void TransferFunctionWidget::setMinMax(const float min, const float max) { - min = m_quantiles[0]; - max = m_quantiles[1]; + m_min_max_val[0] = min; + m_min_max_val[1] = max; } void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name) { - int w, h, n; - - uint8_t* img_data = stbi_load_from_memory(buf, size, &w, &h, &n, 4); - - - int out_w = 256; - uint8_t* output_pixels = (unsigned char*)malloc(out_w * h * n); - - stbir_resize_uint8(img_data, w, h, 0, output_pixels, out_w, h, 0, n); - auto img = std::vector(output_pixels, output_pixels + out_w * 1 * 4); - - stbi_image_free(img_data); - stbi_image_free(output_pixels); + int w, h, n; + uint8_t *img_data = stbi_load_from_memory(buf, (int)size, &w, &h, &n, 4); + auto img = std::vector(img_data, img_data + w * 1 * 4); + stbi_image_free(img_data); + colormaps.emplace_back(name, img, SRGB); + Colormap &cmap = colormaps.back(); + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); + } + } +} - colormaps.emplace_back(name, img); } diff --git a/libs/UIHelpers/transfer_function_widget.h b/libs/UIHelpers/transfer_function_widget.h index 7a15b1c..7febed9 100644 --- a/libs/UIHelpers/transfer_function_widget.h +++ b/libs/UIHelpers/transfer_function_widget.h @@ -1,6 +1,4 @@ -#ifndef TransferFunctionWidget_H_ -#define TransferFunctionWidget_H_ - +#pragma once #ifdef _WIN32 #include "GL/glew.h" #include "GL/wglew.h" @@ -15,105 +13,113 @@ #include #elif defined(__APPLE__) #define GL_GLEXT_PROTOTYPES -#include -#include +#include +#include #else #define GL_GLEXT_PROTOTYPES #include #endif - #include #include #include -#include "imgui/imgui.h" +#include "imgui.h" #include "Vec2.h" -struct Colormap -{ - std::string name; - // An RGBA8 1D image - std::vector colormap; +namespace tfnw { - Colormap(const std::string &name, const std::vector &img); -}; +enum ColorSpace { LINEAR, SRGB }; -class TransferFunctionWidget -{ +struct Colormap { + std::string name; + // An RGBA8 1D image + std::vector colormap; + ColorSpace color_space; - std::vector colormaps; + Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space); +}; - std::vector current_colormap; +class TransferFunctionWidget { + - size_t selected_point = -1; + std::vector colormaps; + + std::vector current_colormap; - bool colormap_changed = true; - GLuint colormap_img = -1; + + size_t selected_point = -1; - std::vector current_histogram; - float m_min_max_val[2]; + bool clicked_on_item = false; + bool gpu_image_stale = true; + bool colormap_changed = true; + GLuint colormap_img = -1; public: - size_t selected_colormap = 0; - std::vector alpha_control_pts = {vec2f(0.f), vec2f(1.f)}; + TransferFunctionWidget(); + + std::vector alpha_control_pts = { vec2f(0.f), vec2f(1.f) }; - TransferFunctionWidget(); + size_t selected_colormap = 0; - // Add a colormap preset. The image should be a 1D RGBA8 image - void add_colormap(const Colormap &map); + // Add a colormap preset. The image should be a 1D RGBA8 image, if the image + // is provided in sRGBA colorspace it will be linearized + void add_colormap(const Colormap &map); - // Add the transfer function UI into the currently active window - void draw_ui(); + // Add the transfer function UI into the currently active window + void draw_ui(); - // Returns true if the colormap was updated since the last - // call to draw_ui - bool changed() const; + // Returns true if the colormap was updated since the last + // call to draw_ui + bool changed() const; - // Get back the RGBA8 color data for the transfer function - std::vector get_colormap(); + // Get back the RGBA8 color data for the transfer function + std::vector get_colormap(); - // Get back the RGBA32F color data for the transfer function - std::vector get_colormapf(); + // Get back the RGBA32F color data for the transfer function + std::vector get_colormapf(); - void setHistogram(const std::vector &hist); + // Get back the RGBA32F color data for the transfer function + // as separate color and opacity vectors + void get_colormapf(std::vector &color, std::vector &opacity); - std::vector &getHistogram(); - void setMinMax(const float min, const float max); + GLint get_colormap_gpu() + { + return colormap_img; + } - void setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha); + void set_colormap_gpu(GLint colormap) + { + colormap_img = colormap; + } - // Get back the RGBA32F color data for the transfer function - // as separate color and opacity vectors - void get_colormapf(std::vector &color, std::vector &opacity); + void draw_legend(); - GLint get_colormap_gpu() - { - return colormap_img; - } + void draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); - void set_colormap_gpu(GLint colormap) - { - colormap_img = colormap; - } + void update_colormap(); - void draw_legend(); + void set_quantiles(float min, float max); - void draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); + void get_quantiles(float& min, float& max); - void draw_histogram(); + std::vector current_histogram; - void update_colormap(); + float m_min_max_val[2]; - void set_quantiles(float min, float max); + void setHistogram(const std::vector& hist); - void get_quantiles(float &min, float &max); + std::vector& getHistogram(); -private: - void update_gpu_image(); + void setMinMax(const float min, const float max); - void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); +private: + void update_gpu_image(); - float m_quantiles[2]; + void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); + + float m_quantiles[2]; }; +} -#endif \ No newline at end of file diff --git a/src/UI/UIView.cpp b/src/UI/UIView.cpp index 2a1a9dc..ceb0f47 100644 --- a/src/UI/UIView.cpp +++ b/src/UI/UIView.cpp @@ -126,7 +126,7 @@ void UIView::draw_ui_callback() { if (ImGui::SmallButton("Add Function")) { - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); int index = m_selected_volume_TrFn.size(); m_selected_volume_TrFn.push_back(std::vector(numVolumes)); @@ -151,7 +151,7 @@ void UIView::draw_ui_callback() tfn_widget.clear(); tfn_widget_multi.clear(); m_tfns.clear(); - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); add_transfer_function(); m_trnfnc_table_selection = 0; @@ -174,7 +174,7 @@ void UIView::draw_ui_callback() tfn_widget.clear(); tfn_widget_multi.clear(); m_tfns.clear(); - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); MyTransFerFunctions trfntc; char label[32]; @@ -1813,7 +1813,7 @@ void UIView::load_ocean_color_maps() std::string name = color_map_name.substr(0, color_map_name.find_first_of(".")); - Colormap color_map(name, img); + tfnw::Colormap color_map(name, img, tfnw::SRGB); tfn_widget[0].add_colormap(color_map); } } From bca525688a8836bc02e09d368279fa34c5f74a1d Mon Sep 17 00:00:00 2001 From: Kmilo9999 Date: Mon, 1 Aug 2022 16:10:07 -0400 Subject: [PATCH 2/5] changes to compile against vr-imgui 2.0 --- include/UI/UIView.h | 2 +- libs/UIHelpers/embedded_colormaps.h | 2 + .../transfer_function_multichannel_widget.h | 2 +- libs/UIHelpers/transfer_function_widget.cpp | 847 ++++++++---------- libs/UIHelpers/transfer_function_widget.h | 132 +-- src/UI/UIView.cpp | 8 +- 6 files changed, 443 insertions(+), 550 deletions(-) diff --git a/include/UI/UIView.h b/include/UI/UIView.h index ae6249b..d0b81c9 100644 --- a/include/UI/UIView.h +++ b/include/UI/UIView.h @@ -202,7 +202,7 @@ class UIView bool m_show_menu; bool m_renderVolume; std::vector tfn_widget_multi; - std::vector tfn_widget; + std::vector tfn_widget; std::vector m_tfns; std::vector> m_selected_volume_TrFn; diff --git a/libs/UIHelpers/embedded_colormaps.h b/libs/UIHelpers/embedded_colormaps.h index e944c76..70da5f1 100644 --- a/libs/UIHelpers/embedded_colormaps.h +++ b/libs/UIHelpers/embedded_colormaps.h @@ -1,4 +1,5 @@ #pragma once +namespace tfnw { uint8_t paraview_cool_warm[] = { 0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x1,0x8,0x6,0x0,0x0,0x0,0x77,0xca,0x84,0xf4,0x0,0x0,0x1,0x7a,0x49,0x44,0x41,0x54,0x78,0x5e,0x63,0xb4,0xf6,0x39,0xf0,0x9f,0x91,0x89,0x89,0x81,0x91,0x89,0x91,0x81,0x99,0x99,0x99,0x1,0xc4,0x66,0x2,0xd3,0x8,0x3e,0x48,0xe,0x24,0xc6,0x4,0x54,0x83,0x90,0x63,0x62,0x40,0xe8,0x43,0xd6,0xf,0xd2,0x7,0xe4,0x33,0x32,0x42,0xcd,0x64,0x42,0xd0,0x8c,0xc,0x40,0x3b,0x20,0x7c,0x46,0x46,0x90,0x3a,0x98,0x1a,0x20,0xd,0xe5,0x83,0xec,0x60,0x4,0xab,0x43,0x88,0x21,0xf8,0xc,0xc,0x40,0x27,0x30,0x30,0x31,0x33,0x82,0x69,0xa0,0x33,0x19,0xc0,0xe6,0x30,0x31,0x30,0x80,0xd4,0x40,0xe4,0xa0,0x6a,0x98,0x60,0xf4,0x7f,0xb0,0x5a,0xb0,0x19,0x60,0x75,0xff,0x19,0x80,0xda,0x81,0xea,0x81,0x34,0x94,0xf,0x96,0x63,0xfc,0xcf,0xc0,0xc0,0xf0,0xf,0x28,0xf7,0x9f,0x1,0xc2,0xff,0x7,0xa6,0x19,0x19,0xff,0x43,0xc4,0x18,0x80,0xe2,0xc,0x7f,0x11,0x72,0x20,0x3e,0x23,0xc8,0xec,0x7f,0xc,0x4c,0xc,0x10,0x9a,0x11,0x44,0x33,0xfc,0x3,0x9b,0xd,0x67,0x83,0xe5,0x80,0xfa,0xfe,0x3,0xd5,0xfc,0x7,0xd2,0xc,0x50,0xfa,0x3f,0x50,0x1d,0x88,0xfd,0xf,0x2a,0x6,0xa6,0x81,0x62,0xff,0x20,0x6a,0x19,0x19,0x60,0x6c,0xa0,0xdf,0xfe,0xfd,0x61,0x0,0x99,0xc7,0x8,0x94,0x63,0x0,0xe9,0x3,0x9a,0x5,0x67,0x83,0xc4,0xfe,0x41,0xd4,0x32,0x0,0xcd,0x3,0xcb,0x43,0xc5,0x18,0xc0,0xea,0x81,0xfe,0xfa,0xfb,0x7,0x28,0x5,0xa4,0x81,0xea,0xc0,0x62,0xff,0x40,0x6c,0xa0,0x18,0x88,0xfe,0xfb,0x97,0xe1,0x3f,0xd0,0x4c,0x86,0x7f,0x10,0x75,0xff,0xff,0x83,0x68,0xa0,0x18,0x48,0x2d,0x48,0xe,0x24,0xe,0x94,0xff,0xff,0x7,0xa4,0xee,0x3f,0x3,0x88,0x6,0xd9,0xf1,0xf,0xc8,0x7,0x6a,0x4,0x32,0xff,0x81,0xc5,0xfe,0x3,0xd5,0xfd,0xff,0xb,0xd1,0x7,0x92,0x3,0xf3,0x51,0xc4,0xfe,0x41,0xd4,0xfe,0xfd,0xf,0xa1,0x81,0x72,0x20,0x75,0xff,0x80,0x7c,0x98,0xfa,0x7f,0x7f,0x80,0x6a,0xfe,0x82,0xd4,0xfd,0x67,0x0,0xb1,0xff,0x1,0xd9,0xff,0x7e,0xff,0x63,0xf8,0x7,0x52,0xfb,0xfb,0x2f,0x3,0x48,0xed,0x7f,0xa0,0xfa,0xbf,0xbf,0x40,0xea,0xfe,0x33,0xfc,0xfd,0x9,0x14,0xfb,0x3,0xa4,0xbf,0x3,0xf9,0xbf,0x80,0x7a,0x7e,0xfe,0x67,0x0,0x0,0x27,0xf7,0xf2,0xdf,0xa6,0x6f,0xf8,0x80,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; @@ -35,3 +36,4 @@ uint8_t ice_fire[] = { uint8_t nic_edge[] = { 0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0xb4,0x0,0x0,0x0,0x1,0x8,0x6,0x0,0x0,0x0,0x77,0xca,0x84,0xf4,0x0,0x0,0x1,0xf7,0x49,0x44,0x41,0x54,0x78,0x5e,0x63,0x34,0x34,0x34,0xfc,0xcf,0xf4,0x9f,0x8d,0x81,0xf9,0x1f,0x3b,0x3,0xcb,0x5f,0x5e,0x6,0x46,0x10,0xfb,0xf,0x90,0x6,0xf2,0x19,0x7f,0xf3,0x31,0x30,0xfc,0x65,0x67,0x60,0xf8,0xc3,0xce,0xf0,0xff,0x7,0x1f,0xc3,0xff,0x9f,0x40,0xfa,0x27,0x37,0x3,0x3,0x23,0x33,0x10,0x73,0x0,0x31,0xb,0x18,0xff,0x67,0xe2,0xc0,0xa0,0xff,0x33,0xb2,0x30,0x80,0x30,0x3,0x17,0x90,0x66,0x65,0x62,0xf8,0xcf,0xe,0xd4,0xc3,0x2,0xa4,0xd9,0x80,0x7c,0x10,0xcd,0xcc,0xc8,0xf0,0x8f,0x15,0xc8,0x66,0x66,0x82,0xd2,0x8c,0xc,0xcc,0xdc,0x8c,0xc,0x8c,0xac,0xc,0xc,0xc,0xac,0x40,0x9a,0x93,0x11,0x48,0x3,0xd9,0x40,0xfa,0x3f,0x50,0xec,0x1f,0x27,0x3,0xc3,0x3f,0x16,0x90,0x1e,0x6,0x86,0x5f,0x40,0x31,0x66,0xd6,0xff,0xc,0x6c,0x9c,0xff,0x19,0x98,0x80,0x72,0x4c,0x40,0x9a,0x11,0xc8,0x7,0x5a,0xc9,0xc0,0xc0,0xf5,0x9f,0xe1,0x3f,0x90,0xfd,0xf,0x28,0xf6,0xf,0x28,0xf7,0x9b,0xf3,0x1f,0x3,0x2b,0x13,0x23,0x10,0x33,0x30,0x70,0x0,0x9d,0xcb,0x2,0x64,0xb3,0x3,0x69,0xa0,0xb5,0xc,0x6c,0x40,0xf3,0x40,0x34,0x8,0xb3,0x2,0xdd,0xc3,0xc4,0x4,0xd4,0xe,0x94,0x67,0x3,0x5a,0xcb,0xd,0x72,0x6,0x3,0x23,0x3,0x88,0xcd,0x9,0xa4,0x41,0xce,0x2,0x3a,0x81,0x81,0x5,0xc8,0x6,0xfa,0x16,0x4c,0x3,0x8d,0x1,0x5a,0x7,0x34,0xff,0xff,0x7f,0x6,0xce,0xff,0x10,0x9a,0x3,0xc8,0x66,0x65,0xf8,0xcf,0xc0,0x2,0xa4,0x39,0x80,0x62,0x10,0xfa,0x3f,0x3,0x33,0x90,0xcf,0xc4,0xf8,0x9d,0x81,0x91,0xf1,0x37,0x3,0x13,0xe3,0xf,0x30,0xcd,0xc8,0xf0,0x87,0x81,0x91,0x9,0x28,0xc6,0xf0,0x1b,0x4a,0x43,0xf8,0xc,0x40,0x3e,0x30,0xd0,0x81,0xa6,0x7c,0x67,0x80,0xb0,0xbf,0x3,0xd9,0xbf,0x81,0x6c,0x18,0xfd,0x1b,0xc8,0xff,0x6,0xe4,0xff,0x2,0xd2,0x5f,0x81,0x1,0xf3,0xf,0xa8,0xc,0xc8,0xfe,0xf7,0x17,0xc8,0x6,0xe2,0x3f,0xbf,0x80,0xf1,0x6,0xa1,0xff,0x83,0xc4,0xfe,0xfc,0x4,0xf3,0xff,0xff,0x3,0x9a,0xff,0x13,0x88,0xff,0x0,0xa3,0xeb,0xfb,0x7f,0x6,0xa0,0x53,0x18,0x18,0xa1,0x34,0xc8,0x4a,0x10,0x1b,0x4c,0x7f,0xfb,0xf,0xb5,0x96,0x91,0xe1,0x3f,0x50,0x2d,0xc3,0x1f,0x20,0xfd,0x3,0x18,0x1f,0xbf,0x91,0xe8,0x6f,0x40,0xf6,0x6f,0x88,0xf3,0xfe,0x7f,0x5,0xb2,0x81,0x56,0xfe,0xf9,0xd,0x8c,0xdf,0xbf,0x40,0x5f,0x1,0x69,0xb0,0x53,0xfe,0x1,0xd9,0xbf,0x80,0xf1,0xb,0x34,0xe3,0xf,0x8c,0x6,0xea,0xfb,0xf7,0xb,0xe4,0x73,0x6,0x86,0x1f,0x20,0x79,0xa0,0x55,0xdf,0xa1,0xf4,0x8f,0xff,0x40,0xf1,0xff,0x20,0x9f,0x3,0xe5,0x40,0x34,0x90,0xf,0x74,0x1e,0xc3,0x2f,0xde,0xff,0xc,0x9f,0xf9,0xfe,0x31,0xfc,0x64,0xf9,0xcf,0xf0,0x13,0x98,0x8c,0x3e,0xb3,0x2,0xd9,0x4c,0x40,0x31,0x66,0x20,0x9f,0xf1,0x3f,0xc3,0x27,0xc6,0x7f,0xc,0x20,0xe7,0xfd,0x64,0xf8,0xcf,0x0,0x0,0xd4,0xca,0xd9,0x94,0x17,0xc3,0x0,0xd6,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; +} diff --git a/libs/UIHelpers/transfer_function_multichannel_widget.h b/libs/UIHelpers/transfer_function_multichannel_widget.h index 79c8356..5978b46 100644 --- a/libs/UIHelpers/transfer_function_multichannel_widget.h +++ b/libs/UIHelpers/transfer_function_multichannel_widget.h @@ -66,7 +66,7 @@ class TransferFunctionMultiChannelWidget { TransferFunctionMultiChannelWidget(); // Add a colormap preset. The image should be a 1D RGBA8 image - void add_colormap(const Colormap &map); + void add_colormap(const tfnw::Colormap &map); // Add the transfer function UI into the currently active window void draw_ui(); diff --git a/libs/UIHelpers/transfer_function_widget.cpp b/libs/UIHelpers/transfer_function_widget.cpp index e26104b..7f0e203 100644 --- a/libs/UIHelpers/transfer_function_widget.cpp +++ b/libs/UIHelpers/transfer_function_widget.cpp @@ -1,583 +1,468 @@ - -#ifdef _WIN32 -#include "GL/glew.h" -#include "GL/wglew.h" -#elif (!defined(__APPLE__)) -#include "GL/glxew.h" -#endif - -// OpenGL Headers #if defined(WIN32) #define NOMINMAX -#include -#include -#elif defined(__APPLE__) -#define GL_GLEXT_PROTOTYPES -#include -#include -#else -#define GL_GLEXT_PROTOTYPES -#include #endif - #include "transfer_function_widget.h" #include #include #include -#include -#include #include "embedded_colormaps.h" -#include "../../include/render/FontHandler.h" #ifndef TFN_WIDGET_NO_STB_IMAGE_IMPL #define STB_IMAGE_IMPLEMENTATION -#define STB_IMAGE_RESIZE_IMPLEMENTATION #endif #include "stb_image.h" -#include "stb_image_resize.h" +#include "../../include/render/FontHandler.h" +#include +#include + +namespace tfnw { template -T clamp(T x, T min, T max) +inline T clamp(T x, T min, T max) { - if (x < min) - { - return min; - } - if (x > max) - { - return max; - } - return x; + if (x < min) { + return min; + } + if (x > max) { + return max; + } + return x; +} + +inline float srgb_to_linear(const float x) +{ + if (x <= 0.04045f) { + return x / 12.92f; + } else { + return std::pow((x + 0.055f) / 1.055f, 2.4f); + } } -Colormap::Colormap(const std::string &name, const std::vector &img) - : name(name), colormap(img) +Colormap::Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space) + : name(name), colormap(img), color_space(color_space) { } TransferFunctionWidget::TransferFunctionWidget() { - // Load up the embedded colormaps as the default options - load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); - load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); - load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); - load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); - load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); - load_embedded_preset(samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); - load_embedded_preset(samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); - load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); - load_embedded_preset(jet, sizeof(jet), "Jet"); - load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); - load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); - load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); - - // Initialize the colormap alpha channel w/ a linear ramp - update_colormap(); - - for (int i = 0; i < 256; i++) - { - current_histogram.push_back(0); - } - - m_min_max_val[0] = 0.0f; - m_min_max_val[1] = 1.0f; - - m_quantiles[0] = 0.05f; - m_quantiles[1] = 0.95f; + // Load up the embedded colormaps as the default options + load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); + load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); + load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); + load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); + load_embedded_preset( + samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); + load_embedded_preset( + samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); + load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); + load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); + load_embedded_preset(jet, sizeof(jet), "Jet"); + load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); + load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); + load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); + + // Initialize the colormap alpha channel w/ a linear ramp + update_colormap(); + + for (int i = 0; i < 256; i++) + { + current_histogram.push_back(0); + } + + m_min_max_val[0] = 0.0f; + m_min_max_val[1] = 1.0f; + + m_quantiles[0] = 0.05f; + m_quantiles[1] = 0.95f; } void TransferFunctionWidget::add_colormap(const Colormap &map) { - colormaps.push_back(map); + colormaps.push_back(map); + + if (colormaps.back().color_space == SRGB) { + Colormap &cmap = colormaps.back(); + cmap.color_space = LINEAR; + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); + } + } + } } void TransferFunctionWidget::draw_ui() { - update_gpu_image(); - colormap_changed = false; - - const ImGuiIO &io = ImGui::GetIO(); - - ImGui::Text("Transfer Function"); - ImGui::TextWrapped( - "Left click to add a point, right click remove. " - "Left click + drag to move points."); - - if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) - { - for (size_t i = 0; i < colormaps.size(); ++i) - { - if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) - { - selected_colormap = i; - update_colormap(); - } - } - ImGui::EndCombo(); - } + update_gpu_image(); - vec2f canvas_size = ImGui::GetContentRegionAvail(); - // Note: If you're not using OpenGL for rendering your UI, the setup for - // displaying the colormap texture in the UI will need to be updated. - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 80; + const ImGuiIO &io = ImGui::GetIO(); - const float point_radius = 20.f; + ImGui::Text("Transfer Function"); + ImGui::TextWrapped( + "Left click to add a point, right click remove. " + "Left click + drag to move points."); - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); + if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) { + for (size_t i = 0; i < colormaps.size(); ++i) { + if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) { + selected_colormap = i; + update_colormap(); + } + } + ImGui::EndCombo(); + } - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); + vec2f canvas_size = ImGui::GetContentRegionAvail(); + // Note: If you're not using OpenGL for rendering your UI, the setup for + // displaying the colormap texture in the UI will need to be updated. + size_t tmp = colormap_img; + ImGui::Image(reinterpret_cast(tmp), ImVec2(canvas_size.x, 16)); + vec2f canvas_pos = ImGui::GetCursorScreenPos(); + canvas_size.y -= 20; - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); + const float point_radius = 10.f; - ImGui::InvisibleButton("tfn_canvas", canvas_size); - if (ImGui::IsItemHovered() || selected_point != (size_t)-1) - { + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - vec2f mouse_pos = (vec2f(io.MousePos) - view_offset) / view_scale; - mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); - mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); + const vec2f view_scale(canvas_size.x, -canvas_size.y); + const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - if (io.MouseDown[0]) - { - if (selected_point != (size_t)-1) - { - alpha_control_pts[selected_point] = mouse_pos; + draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - // make sure point does not cross + ImGui::InvisibleButton("tfn_canvas", canvas_size); + + static bool clicked_on_item = false; + if (!io.MouseDown[0] && !io.MouseDown[1]) { + clicked_on_item = false; + } + if (ImGui::IsItemHovered() && (io.MouseDown[0] || io.MouseDown[1])) { + clicked_on_item = true; + } + + ImVec2 bbmin = ImGui::GetItemRectMin(); + ImVec2 bbmax = ImGui::GetItemRectMax(); + ImVec2 clipped_mouse_pos = ImVec2(std::min(std::max(io.MousePos.x, bbmin.x), bbmax.x), + std::min(std::max(io.MousePos.y, bbmin.y), bbmax.y)); + + if (clicked_on_item) { + vec2f mouse_pos = (vec2f(clipped_mouse_pos) - view_offset) / view_scale; + mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); + mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); + + if (io.MouseDown[0]) { + if (selected_point != (size_t)-1) { + alpha_control_pts[selected_point] = mouse_pos; + + // Keep the first and last control points at the edges + if (selected_point == 0) { + alpha_control_pts[selected_point].x = 0.f; + } else if (selected_point == alpha_control_pts.size() - 1) { + alpha_control_pts[selected_point].x = 1.f; + } + } else { + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + // No nearby point, we're adding a new one + if (fnd == alpha_control_pts.end()) { + alpha_control_pts.push_back(mouse_pos); + } + } - // Keep the first and last control points at the edges - if (selected_point == 0) - { - alpha_control_pts[selected_point].x = 0.f; - } - else if (selected_point == alpha_control_pts.size() - 1) - { - alpha_control_pts[selected_point].x = 1.f; - } - // make sure the other points do not cross - else - { - alpha_control_pts[selected_point].x = (alpha_control_pts[selected_point].x < alpha_control_pts[selected_point - 1].x) - ? alpha_control_pts[selected_point - 1].x + 0.01 - : alpha_control_pts[selected_point].x; - alpha_control_pts[selected_point].x = (alpha_control_pts[selected_point].x > alpha_control_pts[selected_point + 1].x) - ? alpha_control_pts[selected_point + 1].x - 0.01 - : alpha_control_pts[selected_point].x; - } - } - else - { - // See if we're selecting a point or adding one - if (io.MousePos.x - canvas_pos.x <= point_radius) - { - selected_point = 0; - } - else if (io.MousePos.x - canvas_pos.x >= canvas_size.x - point_radius) - { - selected_point = alpha_control_pts.size() - 1; - } - else - { - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); - // No nearby point, we're adding a new one - if (fnd == alpha_control_pts.end()) - { - alpha_control_pts.push_back(mouse_pos); // Keep alpha control points ordered by x coordinate, update // selected point index to match std::sort(alpha_control_pts.begin(), alpha_control_pts.end(), - [](const vec2f &a, const vec2f &b) - { return a.x < b.x; }); - if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) - { - fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); + [](const vec2f &a, const vec2f &b) { return a.x < b.x; }); + if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) { + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + selected_point = std::distance(alpha_control_pts.begin(), fnd); + } + update_colormap(); + } else if (ImGui::IsMouseClicked(1)) { + selected_point = -1; + // Find and remove the point + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + // We also want to prevent erasing the first and last points + if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && + fnd != alpha_control_pts.end() - 1) { + alpha_control_pts.erase(fnd); } - } - selected_point = std::distance(alpha_control_pts.begin(), fnd); + update_colormap(); + } else { + selected_point = -1; } - } - update_colormap(); - } - else if (ImGui::IsMouseClicked(1)) - { - selected_point = -1; - // Find and remove the point - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(io.MousePos)).length(); - return dist <= point_radius; }); - // We also want to prevent erasing the first and last points - if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && - fnd != alpha_control_pts.end() - 1) - { - alpha_control_pts.erase(fnd); - } - update_colormap(); - } - else - { - selected_point = -1; + } else { + selected_point = -1; } - } - - // Draw the alpha control points, and build the points for the polyline - // which connects them - std::vector polyline_pts; - for (const auto &pt : alpha_control_pts) - { - const vec2f pt_pos = pt * view_scale + view_offset; - polyline_pts.push_back(pt_pos); - draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); - } - - draw_list->AddPolyline(polyline_pts.data(), polyline_pts.size(), 0xFFFFFFFF, false, 2.f); - draw_list->PopClipRect(); - - // Add Label tick marks - int nbTicks = 5; - vec2f tick_pos = ImGui::GetCursorScreenPos(); - tick_pos.y -= ImGui::GetStyle().ItemSpacing.y; - vec2f tick_size = ImGui::GetContentRegionAvail(); - tick_size.y = 5; - draw_list->PushClipRect(tick_pos, ImVec2(tick_pos.x + tick_size.x, tick_pos.y + tick_size.y)); - - for (int i = 0; i < nbTicks; i++) - { - float percentage = float(i) / (nbTicks - 1); - - draw_list->AddLine(ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_size.y), - ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_pos.y + tick_size.y), ImColor(255, 255, 255, 255), 1); - } - draw_list->PopClipRect(); - - // Add Label text - const float ItemSpacing = ImGui::GetStyle().ItemSpacing.x; - for (int i = 0; i < nbTicks; i++) - { - float percentage = float(i) / (nbTicks - 1); - float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; - std::stringstream text; - text << std::fixed << std::setprecision(4) << val; - if (i == 0) - { - } - else if (i == nbTicks - 1) - { - ImGui::SameLine(ImGui::GetWindowWidth() - ItemSpacing - ImGui::CalcTextSize(text.str().c_str()).x); - } - else - { - ImGui::SameLine((ImGui::GetWindowWidth()) * percentage - ImGui::CalcTextSize(text.str().c_str()).x * 0.5); + + // Draw the alpha control points, and build the points for the polyline + // which connects them + std::vector polyline_pts; + for (const auto &pt : alpha_control_pts) { + const vec2f pt_pos = pt * view_scale + view_offset; + polyline_pts.push_back(pt_pos); + draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); } - ImGui::Text(text.str().c_str()); - } + draw_list->AddPolyline( + polyline_pts.data(), (int)polyline_pts.size(), 0xFFFFFFFF, false, 2.f); + draw_list->PopClipRect(); } bool TransferFunctionWidget::changed() const { - return colormap_changed; + return colormap_changed; } std::vector TransferFunctionWidget::get_colormap() { - return current_colormap; + colormap_changed = false; + return current_colormap; } std::vector TransferFunctionWidget::get_colormapf() { - std::vector colormapf(current_colormap.size(), 0.f); - for (size_t i = 0; i < current_colormap.size(); ++i) - { - colormapf[i] = current_colormap[i] / 255.f; - } - return colormapf; + colormap_changed = false; + std::vector colormapf(current_colormap.size(), 0.f); + for (size_t i = 0; i < current_colormap.size(); ++i) { + colormapf[i] = current_colormap[i] / 255.f; + } + return colormapf; } -void TransferFunctionWidget::setHistogram(const std::vector &hist) +void TransferFunctionWidget::draw_legend() { - current_histogram = hist; + GLint viewport[4]; + GLfloat projection[16]; + GLfloat modelview[16]; + + int min[2] = { 50, 100 }; + int max[2] = { 400, 120 }; + + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + glGetFloatv(GL_PROJECTION_MATRIX, &projection[0]); + glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, viewport[2], 0, viewport[3], -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture(GL_TEXTURE_2D, colormap_img); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(min[0], max[1], 0); + glTexCoord2f(1, 1); + glVertex3f(max[0], max[1], 0); + glTexCoord2f(1, 0); + glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(0, 0); + glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); + glEnd(); + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindTexture(GL_TEXTURE_2D, colormap_img); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(1, 1); + glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); + glTexCoord2f(1, 0); + glVertex3f(max[0], min[1], 0); + glTexCoord2f(0, 0); + glVertex3f(min[0], min[1], 0); + glEnd(); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + // Add Label tick marks + int nbTicks = 4; + float diff = (max[0] - min[0]) / nbTicks; + for (int i = 0; i <= nbTicks; i++) + { + float pos_x = min[0] + i * diff; + glBegin(GL_LINES); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glVertex3f(pos_x, min[1], 0); + glVertex3f(pos_x, min[1] - 5, 0); + glEnd(); + + float percentage = float(i) / (nbTicks); + float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; + std::stringstream text; + text << std::fixed << std::setprecision(4) << val; + + FontHandler::getInstance()->renderTextBox(text.str(), pos_x - 100, min[1] - 15, 0, 200.0, 10, TextAlignment::CENTER); + } + + glEnable(GL_LIGHTING); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(projection); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(modelview); } -std::vector &TransferFunctionWidget::getHistogram() +void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) { - return current_histogram; + bool show_legend = true; + ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); + ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); + ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); + ImGuiIO& io = ImGui::GetIO(); + ImTextureID my_tex_id = io.Fonts->TexID; + float texture_width = (float)io.Fonts->TexWidth; + float texture_height = (float)io.Fonts->TexHeight; + std::string max_str = std::to_string(m_min_max_val[1]); + float offset = 0.008 * (texture_width * max_str.size()); + ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); + ImGui::Text("%.0f", m_min_max_val[0]); + ImGui::SameLine((legend_width / 2)); + ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); + ImGui::SameLine(ImGui::GetWindowWidth() - offset); + ImGui::Text(" %.0f", m_min_max_val[1]); + ImGui::End(); } -void TransferFunctionWidget::setMinMax(const float min, const float max) -{ - m_min_max_val[0] = min; - m_min_max_val[1] = max; -} -void TransferFunctionWidget::setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha) +void TransferFunctionWidget::get_colormapf(std::vector &color, + std::vector &opacity) { - if (hist1.size() != hist2.size()) - return; - - current_histogram.clear(); - for (int i = 0; i < hist1.size(); i++) - current_histogram.push_back(hist1[i] * alpha + hist2[i] * (1.0f - alpha)); + colormap_changed = false; + color.resize((current_colormap.size() / 4) * 3); + opacity.resize(current_colormap.size() / 4); + for (size_t i = 0; i < current_colormap.size() / 4; ++i) { + color[i * 3] = current_colormap[i * 4] / 255.f; + color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; + color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; + opacity[i] = current_colormap[i * 4 + 3] / 255.f; + } } -void TransferFunctionWidget::get_colormapf(std::vector &color, std::vector &opacity) +void TransferFunctionWidget::update_gpu_image() { - color.resize((current_colormap.size() / 4) * 3); - opacity.resize(current_colormap.size() / 4); - for (size_t i = 0; i < current_colormap.size() / 4; ++i) - { - color[i * 3] = current_colormap[i * 4] / 255.f; - color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; - color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; - opacity[i] = current_colormap[i * 4 + 3] / 255.f; - } + GLint prev_tex_2d = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); + + if (colormap_img == (GLuint)-1) { + glGenTextures(1, &colormap_img); + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + if (gpu_image_stale) { + gpu_image_stale = false; + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB8, + (GLsizei)(current_colormap.size() / 4), + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + current_colormap.data()); + } + glBindTexture(GL_TEXTURE_2D, prev_tex_2d); } -void TransferFunctionWidget::draw_legend() +void TransferFunctionWidget::update_colormap() { - GLint viewport[4]; - GLfloat projection[16]; - GLfloat modelview[16]; - - int min[2] = {50, 100}; - int max[2] = {400, 120}; - - glGetIntegerv(GL_VIEWPORT, &viewport[0]); - glGetFloatv(GL_PROJECTION_MATRIX, &projection[0]); - glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, viewport[2], 0, viewport[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], max[1], 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], max[1], 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glEnd(); - glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, 0); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], min[1], 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], min[1], 0); - glEnd(); - glDisable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, 0); - - // Add Label tick marks - int nbTicks = 4; - float diff = (max[0] - min[0]) / nbTicks; - for (int i = 0; i <= nbTicks; i++) - { - float pos_x = min[0] + i * diff; - glBegin(GL_LINES); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glVertex3f(pos_x, min[1], 0); - glVertex3f(pos_x, min[1] - 5, 0); - glEnd(); - - float percentage = float(i) / (nbTicks); - float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; - std::stringstream text; - text << std::fixed << std::setprecision(4) << val; - - FontHandler::getInstance()->renderTextBox(text.str(), pos_x - 100, min[1] - 15, 0, 200.0, 10, TextAlignment::CENTER); - } - - glEnable(GL_LIGHTING); - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(projection); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(modelview); + colormap_changed = true; + gpu_image_stale = true; + current_colormap = colormaps[selected_colormap].colormap; + // We only change opacities for now, so go through and update the opacity + // by blending between the neighboring control points + auto a_it = alpha_control_pts.begin(); + const size_t npixels = current_colormap.size() / 4; + for (size_t i = 0; i < npixels; ++i) { + float x = static_cast(i) / npixels; + auto high = a_it + 1; + if (x > high->x) { + ++a_it; + ++high; + } + float t = (x - a_it->x) / (high->x - a_it->x); + float alpha = (1.f - t) * a_it->y + t * high->y; + current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); + } } -void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) +void TransferFunctionWidget::set_quantiles(float min, float max) { - bool show_legend = true; - ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); - ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); - ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); - ImGuiIO &io = ImGui::GetIO(); - ImTextureID my_tex_id = io.Fonts->TexID; - float texture_width = (float)io.Fonts->TexWidth; - float texture_height = (float)io.Fonts->TexHeight; - std::string max_str = std::to_string(m_min_max_val[1]); - float offset = 0.008 * (texture_width * max_str.size()); - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); - ImGui::Text("%.0f", m_min_max_val[0]); - ImGui::SameLine((legend_width / 2)); - ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); - ImGui::SameLine(ImGui::GetWindowWidth() - offset); - ImGui::Text(" %.0f", m_min_max_val[1]); - ImGui::End(); + m_quantiles[0] = min; + m_quantiles[1] = max; } -void TransferFunctionWidget::draw_histogram() +void TransferFunctionWidget::get_quantiles(float& min, float& max) { - - const ImGuiIO &io = ImGui::GetIO(); - - ImGui::Text("Histogram"); - - vec2f canvas_size = ImGui::GetContentRegionAvail(); - // Note: If you're not using OpenGL for rendering your UI, the setup for - // displaying the colormap texture in the UI will need to be updated. - // TODO(#45) ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 250; - - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - - ImGui::InvisibleButton("hstg_canvas", canvas_size); - - // Draw the alpha control points, and build the points for the polyline - // which connects them - - // Code to Draw histogram in the UI - for (int i = 0; i < current_histogram.size(); i++) - { - vec2f lp = vec2f(((float)i) / current_histogram.size(), 0.0f); - vec2f hp = vec2f(((float)i + 1.0f) / current_histogram.size(), current_histogram[i]); - draw_list->AddRectFilled(lp * view_scale + view_offset, hp * view_scale + view_offset, 0x77777777); - } - - draw_list->PopClipRect(); + min = m_quantiles[0]; + max = m_quantiles[1]; } -void TransferFunctionWidget::update_gpu_image() -{ - GLint prev_tex_2d = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); - - if (colormap_img == (GLuint)-1) - { - glGenTextures(1, &colormap_img); - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - if (colormap_changed) - { - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - current_colormap.size() / 4, - 1, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - current_colormap.data()); - } - if (prev_tex_2d != 0) - { - glBindTexture(GL_TEXTURE_2D, prev_tex_2d); - } -} -void TransferFunctionWidget::update_colormap() +void TransferFunctionWidget::setHistogram(const std::vector& hist) { - colormap_changed = true; - current_colormap = colormaps[selected_colormap].colormap; - // We only change opacities for now, so go through and update the opacity - // by blending between the neighboring control points - auto a_it = alpha_control_pts.begin(); - const size_t npixels = current_colormap.size() / 4; - - for (size_t i = 0; i < npixels; ++i) - { - float x = static_cast(i) / npixels; - auto high = a_it + 1; - if (x > high->x) - { - ++a_it; - ++high; - } - float t = (x - a_it->x) / (high->x - a_it->x); - float alpha = (1.f - t) * a_it->y + t * high->y; - current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); - } + current_histogram = hist; } -void TransferFunctionWidget::set_quantiles(float min, float max) +std::vector& TransferFunctionWidget::getHistogram() { - m_quantiles[0] = min; - m_quantiles[1] = max; + return current_histogram; } -void TransferFunctionWidget::get_quantiles(float &min, float &max) +void TransferFunctionWidget::setMinMax(const float min, const float max) { - min = m_quantiles[0]; - max = m_quantiles[1]; + m_min_max_val[0] = min; + m_min_max_val[1] = max; } void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name) { - int w, h, n; - - uint8_t* img_data = stbi_load_from_memory(buf, size, &w, &h, &n, 4); - - - int out_w = 256; - uint8_t* output_pixels = (unsigned char*)malloc(out_w * h * n); - - stbir_resize_uint8(img_data, w, h, 0, output_pixels, out_w, h, 0, n); - auto img = std::vector(output_pixels, output_pixels + out_w * 1 * 4); - - stbi_image_free(img_data); - stbi_image_free(output_pixels); + int w, h, n; + uint8_t *img_data = stbi_load_from_memory(buf, (int)size, &w, &h, &n, 4); + auto img = std::vector(img_data, img_data + w * 1 * 4); + stbi_image_free(img_data); + colormaps.emplace_back(name, img, SRGB); + Colormap &cmap = colormaps.back(); + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); + } + } +} - colormaps.emplace_back(name, img); } diff --git a/libs/UIHelpers/transfer_function_widget.h b/libs/UIHelpers/transfer_function_widget.h index 7a15b1c..7febed9 100644 --- a/libs/UIHelpers/transfer_function_widget.h +++ b/libs/UIHelpers/transfer_function_widget.h @@ -1,6 +1,4 @@ -#ifndef TransferFunctionWidget_H_ -#define TransferFunctionWidget_H_ - +#pragma once #ifdef _WIN32 #include "GL/glew.h" #include "GL/wglew.h" @@ -15,105 +13,113 @@ #include #elif defined(__APPLE__) #define GL_GLEXT_PROTOTYPES -#include -#include +#include +#include #else #define GL_GLEXT_PROTOTYPES #include #endif - #include #include #include -#include "imgui/imgui.h" +#include "imgui.h" #include "Vec2.h" -struct Colormap -{ - std::string name; - // An RGBA8 1D image - std::vector colormap; +namespace tfnw { - Colormap(const std::string &name, const std::vector &img); -}; +enum ColorSpace { LINEAR, SRGB }; -class TransferFunctionWidget -{ +struct Colormap { + std::string name; + // An RGBA8 1D image + std::vector colormap; + ColorSpace color_space; - std::vector colormaps; + Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space); +}; - std::vector current_colormap; +class TransferFunctionWidget { + - size_t selected_point = -1; + std::vector colormaps; + + std::vector current_colormap; - bool colormap_changed = true; - GLuint colormap_img = -1; + + size_t selected_point = -1; - std::vector current_histogram; - float m_min_max_val[2]; + bool clicked_on_item = false; + bool gpu_image_stale = true; + bool colormap_changed = true; + GLuint colormap_img = -1; public: - size_t selected_colormap = 0; - std::vector alpha_control_pts = {vec2f(0.f), vec2f(1.f)}; + TransferFunctionWidget(); + + std::vector alpha_control_pts = { vec2f(0.f), vec2f(1.f) }; - TransferFunctionWidget(); + size_t selected_colormap = 0; - // Add a colormap preset. The image should be a 1D RGBA8 image - void add_colormap(const Colormap &map); + // Add a colormap preset. The image should be a 1D RGBA8 image, if the image + // is provided in sRGBA colorspace it will be linearized + void add_colormap(const Colormap &map); - // Add the transfer function UI into the currently active window - void draw_ui(); + // Add the transfer function UI into the currently active window + void draw_ui(); - // Returns true if the colormap was updated since the last - // call to draw_ui - bool changed() const; + // Returns true if the colormap was updated since the last + // call to draw_ui + bool changed() const; - // Get back the RGBA8 color data for the transfer function - std::vector get_colormap(); + // Get back the RGBA8 color data for the transfer function + std::vector get_colormap(); - // Get back the RGBA32F color data for the transfer function - std::vector get_colormapf(); + // Get back the RGBA32F color data for the transfer function + std::vector get_colormapf(); - void setHistogram(const std::vector &hist); + // Get back the RGBA32F color data for the transfer function + // as separate color and opacity vectors + void get_colormapf(std::vector &color, std::vector &opacity); - std::vector &getHistogram(); - void setMinMax(const float min, const float max); + GLint get_colormap_gpu() + { + return colormap_img; + } - void setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha); + void set_colormap_gpu(GLint colormap) + { + colormap_img = colormap; + } - // Get back the RGBA32F color data for the transfer function - // as separate color and opacity vectors - void get_colormapf(std::vector &color, std::vector &opacity); + void draw_legend(); - GLint get_colormap_gpu() - { - return colormap_img; - } + void draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); - void set_colormap_gpu(GLint colormap) - { - colormap_img = colormap; - } + void update_colormap(); - void draw_legend(); + void set_quantiles(float min, float max); - void draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); + void get_quantiles(float& min, float& max); - void draw_histogram(); + std::vector current_histogram; - void update_colormap(); + float m_min_max_val[2]; - void set_quantiles(float min, float max); + void setHistogram(const std::vector& hist); - void get_quantiles(float &min, float &max); + std::vector& getHistogram(); -private: - void update_gpu_image(); + void setMinMax(const float min, const float max); - void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); +private: + void update_gpu_image(); - float m_quantiles[2]; + void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); + + float m_quantiles[2]; }; +} -#endif \ No newline at end of file diff --git a/src/UI/UIView.cpp b/src/UI/UIView.cpp index 2a1a9dc..ceb0f47 100644 --- a/src/UI/UIView.cpp +++ b/src/UI/UIView.cpp @@ -126,7 +126,7 @@ void UIView::draw_ui_callback() { if (ImGui::SmallButton("Add Function")) { - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); int index = m_selected_volume_TrFn.size(); m_selected_volume_TrFn.push_back(std::vector(numVolumes)); @@ -151,7 +151,7 @@ void UIView::draw_ui_callback() tfn_widget.clear(); tfn_widget_multi.clear(); m_tfns.clear(); - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); add_transfer_function(); m_trnfnc_table_selection = 0; @@ -174,7 +174,7 @@ void UIView::draw_ui_callback() tfn_widget.clear(); tfn_widget_multi.clear(); m_tfns.clear(); - tfn_widget.push_back(TransferFunctionWidget()); + tfn_widget.push_back(tfnw::TransferFunctionWidget()); tfn_widget_multi.push_back(TransferFunctionMultiChannelWidget()); MyTransFerFunctions trfntc; char label[32]; @@ -1813,7 +1813,7 @@ void UIView::load_ocean_color_maps() std::string name = color_map_name.substr(0, color_map_name.find_first_of(".")); - Colormap color_map(name, img); + tfnw::Colormap color_map(name, img, tfnw::SRGB); tfn_widget[0].add_colormap(color_map); } } From 1c6a299b300be03bf6e6ad22e1f51c7a285e0b3f Mon Sep 17 00:00:00 2001 From: Kmilo9999 Date: Wed, 10 Aug 2022 09:45:32 -0400 Subject: [PATCH 3/5] changed enum to enum class. --- libs/UIHelpers/transfer_function_widget.cpp | 7 ++++--- libs/UIHelpers/transfer_function_widget.h | 2 +- src/UI/UIView.cpp | 2 +- src/vrapp/VRVolumeApp.cpp | 6 ++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libs/UIHelpers/transfer_function_widget.cpp b/libs/UIHelpers/transfer_function_widget.cpp index 7f0e203..d68a92f 100644 --- a/libs/UIHelpers/transfer_function_widget.cpp +++ b/libs/UIHelpers/transfer_function_widget.cpp @@ -9,6 +9,7 @@ #ifndef TFN_WIDGET_NO_STB_IMAGE_IMPL #define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_IMPLEMENTATION #endif #include "stb_image.h" @@ -83,9 +84,9 @@ void TransferFunctionWidget::add_colormap(const Colormap &map) { colormaps.push_back(map); - if (colormaps.back().color_space == SRGB) { + if (colormaps.back().color_space == ColorSpace::SRGB) { Colormap &cmap = colormaps.back(); - cmap.color_space = LINEAR; + cmap.color_space = ColorSpace::LINEAR; for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { for (size_t j = 0; j < 3; ++j) { const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); @@ -455,7 +456,7 @@ void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, uint8_t *img_data = stbi_load_from_memory(buf, (int)size, &w, &h, &n, 4); auto img = std::vector(img_data, img_data + w * 1 * 4); stbi_image_free(img_data); - colormaps.emplace_back(name, img, SRGB); + colormaps.emplace_back(name, img, ColorSpace::SRGB); Colormap &cmap = colormaps.back(); for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { for (size_t j = 0; j < 3; ++j) { diff --git a/libs/UIHelpers/transfer_function_widget.h b/libs/UIHelpers/transfer_function_widget.h index 7febed9..034dc25 100644 --- a/libs/UIHelpers/transfer_function_widget.h +++ b/libs/UIHelpers/transfer_function_widget.h @@ -27,7 +27,7 @@ namespace tfnw { -enum ColorSpace { LINEAR, SRGB }; +enum class ColorSpace { LINEAR, SRGB }; struct Colormap { std::string name; diff --git a/src/UI/UIView.cpp b/src/UI/UIView.cpp index ceb0f47..bf71d7a 100644 --- a/src/UI/UIView.cpp +++ b/src/UI/UIView.cpp @@ -1813,7 +1813,7 @@ void UIView::load_ocean_color_maps() std::string name = color_map_name.substr(0, color_map_name.find_first_of(".")); - tfnw::Colormap color_map(name, img, tfnw::SRGB); + tfnw::Colormap color_map(name, img, tfnw::ColorSpace::SRGB); tfn_widget[0].add_colormap(color_map); } } diff --git a/src/vrapp/VRVolumeApp.cpp b/src/vrapp/VRVolumeApp.cpp index c2e6857..c94c10f 100644 --- a/src/vrapp/VRVolumeApp.cpp +++ b/src/vrapp/VRVolumeApp.cpp @@ -760,18 +760,16 @@ void VRVolumeApp::animated_render(int tfn, int vol) if (m_ui_view->is_render_volume_enabled()) { - GLint colorMap = m_ui_view->get_transfer_function_colormap(tfn); - GLint colorMapMult = m_ui_view->get_multitransfer_function_colormap(tfn); GLint lut = -1; if (use_tranferFunction) { if (m_use_multi_transfer) { - lut = colorMapMult; + lut = m_ui_view->get_multitransfer_function_colormap(tfn); } else { - lut = colorMap; + lut = m_ui_view->get_transfer_function_colormap(tfn); } } From 1172b628c831b65bfcc7f5c0ae474ec15a07eab3 Mon Sep 17 00:00:00 2001 From: Kmilo9999 Date: Wed, 10 Aug 2022 17:30:06 -0400 Subject: [PATCH 4/5] updated code to support new version of vr-imgui --- include/UI/UIView.h | 2 - include/common/common.h | 1 + libs/UIHelpers/transfer_function_widget.cpp | 868 ++++++++++---------- libs/UIHelpers/transfer_function_widget.h | 54 +- src/UI/UIView.cpp | 10 +- src/vrapp/VRVolumeApp.cpp | 5 - superbuild/vr-imgui/CMakeLists.txt | 2 +- 7 files changed, 480 insertions(+), 462 deletions(-) diff --git a/include/UI/UIView.h b/include/UI/UIView.h index d0b81c9..721317a 100644 --- a/include/UI/UIView.h +++ b/include/UI/UIView.h @@ -139,8 +139,6 @@ class UIView void set_volume_time_info(time_t time); - void draw_transfer_function_legend(); - void set_transfer_function_min_max(float min, float max); bool get_show_movie_saved_pop_up() const { return m_show_movie_saved_pop_up; } diff --git a/include/common/common.h b/include/common/common.h index fb5f80c..3e4e8ba 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -3,6 +3,7 @@ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS +#define NOMINMAX #include #define OS_SLASH "\\" #include "../../external/msvc/dirent.h" diff --git a/libs/UIHelpers/transfer_function_widget.cpp b/libs/UIHelpers/transfer_function_widget.cpp index 90403c1..edacf72 100644 --- a/libs/UIHelpers/transfer_function_widget.cpp +++ b/libs/UIHelpers/transfer_function_widget.cpp @@ -1,509 +1,539 @@ -#if defined(WIN32) -#define NOMINMAX -#endif #include "transfer_function_widget.h" + #include #include #include +#include +#include #include "embedded_colormaps.h" #ifndef TFN_WIDGET_NO_STB_IMAGE_IMPL #define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_IMPLEMENTATION #endif #include "stb_image.h" -#include "../../include/render/FontHandler.h" -#include -#include +#include "stb_image_resize.h" + +static const float alpha_points_min_distance = 0.006f; namespace tfnw { - - template - inline T clamp(T x, T min, T max) + template + inline T clamp(T x, T min, T max) + { + if (x < min) { - if (x < min) - { - return min; - } - if (x > max) - { - return max; - } - return x; + return min; } - - inline float srgb_to_linear(const float x) + if (x > max) { - if (x <= 0.04045f) - { - return x / 12.92f; - } - else - { - return std::pow((x + 0.055f) / 1.055f, 2.4f); - } + return max; } + return x; + } - Colormap::Colormap(const std::string &name, - const std::vector &img, - const ColorSpace color_space) - : name(name), colormap(img), color_space(color_space) + inline float srgb_to_linear(const float x) + { + if (x <= 0.04045f) { + return x / 12.92f; } - - TransferFunctionWidget::TransferFunctionWidget() + else { - // Load up the embedded colormaps as the default options - load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); - load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); - load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); - load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); - load_embedded_preset( - samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); - load_embedded_preset( - samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); - load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); - load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); - load_embedded_preset(jet, sizeof(jet), "Jet"); - load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); - load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); - load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); - - // Initialize the colormap alpha channel w/ a linear ramp - update_colormap(); + return std::pow((x + 0.055f) / 1.055f, 2.4f); + } + } + + Colormap::Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space) + : name(name), colormap(img), color_space(color_space) + { + } + + TransferFunctionWidget::TransferFunctionWidget() + { + // Load up the embedded colormaps as the default options + load_embedded_preset(cool_warm_extended, sizeof(cool_warm_extended), "Cool Warm Extended"); + load_embedded_preset(paraview_cool_warm, sizeof(paraview_cool_warm), "ParaView Cool Warm"); + load_embedded_preset(rainbow, sizeof(rainbow), "Rainbow"); + load_embedded_preset(matplotlib_plasma, sizeof(matplotlib_plasma), "Matplotlib Plasma"); + load_embedded_preset(matplotlib_virdis, sizeof(matplotlib_virdis), "Matplotlib Virdis"); + load_embedded_preset(samsel_linear_green, sizeof(samsel_linear_green), "Samsel Linear Green"); + load_embedded_preset(samsel_linear_ygb_1211g, sizeof(samsel_linear_ygb_1211g), "Samsel Linear YGB 1211G"); + load_embedded_preset(blackbody, sizeof(blackbody), "Black Body"); + load_embedded_preset(jet, sizeof(jet), "Jet"); + load_embedded_preset(blue_gold, sizeof(blue_gold), "Blue Gold"); + load_embedded_preset(ice_fire, sizeof(ice_fire), "Ice Fire"); + load_embedded_preset(nic_edge, sizeof(nic_edge), "nic Edge"); + + // Initialize the colormap alpha channel w/ a linear ramp + update_colormap(); + + for (int i = 0; i < 256; i++) + { + current_histogram.push_back(0); + } - for (int i = 0; i < 256; i++) - { - current_histogram.push_back(0); - } + m_min_max_val[0] = 0.0f; + m_min_max_val[1] = 1.0f; - m_min_max_val[0] = 0.0f; - m_min_max_val[1] = 1.0f; + m_quantiles[0] = 0.05f; + m_quantiles[1] = 0.95f; + } - m_quantiles[0] = 0.05f; - m_quantiles[1] = 0.95f; - } - - void TransferFunctionWidget::add_colormap(const Colormap &map) + void TransferFunctionWidget::add_colormap(const Colormap &map) + { + colormaps.push_back(map); + if (colormaps.back().color_space == SRGB) { - colormaps.push_back(map); - - if (colormaps.back().color_space == ColorSpace::SRGB) + Colormap &cmap = colormaps.back(); + cmap.color_space = LINEAR; + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) + { + for (size_t j = 0; j < 3; ++j) { - Colormap &cmap = colormaps.back(); - cmap.color_space = ColorSpace::LINEAR; - for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) - { - for (size_t j = 0; j < 3; ++j) - { - const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); - cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); - } - } + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); } + } } + } - void TransferFunctionWidget::draw_ui() - { - update_gpu_image(); + void TransferFunctionWidget::draw_ui() + { + update_gpu_image(); - const ImGuiIO &io = ImGui::GetIO(); + const ImGuiIO &io = ImGui::GetIO(); - ImGui::Text("Transfer Function"); - ImGui::TextWrapped( - "Left click to add a point, right click remove. " - "Left click + drag to move points."); + ImGui::Text("Transfer Function"); + ImGui::TextWrapped( + "Left click to add a point, right click remove. " + "Left click + drag to move points."); - if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) + if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) + { + for (size_t i = 0; i < colormaps.size(); ++i) + { + if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) { - for (size_t i = 0; i < colormaps.size(); ++i) - { - if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) - { - selected_colormap = i; - update_colormap(); - } - } - ImGui::EndCombo(); + selected_colormap = i; + update_colormap(); } + } + ImGui::EndCombo(); + } - vec2f canvas_size = ImGui::GetContentRegionAvail(); - // Note: If you're not using OpenGL for rendering your UI, the setup for - // displaying the colormap texture in the UI will need to be updated. - size_t tmp = colormap_img; - ImGui::Image(reinterpret_cast(tmp), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 20; + vec2f canvas_size = ImGui::GetContentRegionAvail(); + // Note: If you're not using OpenGL for rendering your UI, the setup for + // displaying the colormap texture in the UI will need to be updated. + size_t tmp = colormap_img; + ImGui::Image(reinterpret_cast(tmp), ImVec2(canvas_size.x, 16)); + vec2f canvas_pos = ImGui::GetCursorScreenPos(); + canvas_size.y -= 20; - const float point_radius = 10.f; + const float point_radius = 10.f; - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); + const vec2f view_scale(canvas_size.x, -canvas_size.y); + const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); + draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - ImGui::InvisibleButton("tfn_canvas", canvas_size); + ImGui::InvisibleButton("tfn_canvas", canvas_size); - static bool clicked_on_item = false; - if (!io.MouseDown[0] && !io.MouseDown[1]) - { - clicked_on_item = false; - } - if (ImGui::IsItemHovered() && (io.MouseDown[0] || io.MouseDown[1])) - { - clicked_on_item = true; - } + static bool clicked_on_item = false; + if ((!io.MouseDown[0]) && (!io.MouseDown[1])) + { + clicked_on_item = false; + } + if (ImGui::IsItemHovered() || selected_point != (size_t)-1 && (io.MouseDown[0] || io.MouseDown[1])) + { + clicked_on_item = true; + } - ImVec2 bbmin = ImGui::GetItemRectMin(); - ImVec2 bbmax = ImGui::GetItemRectMax(); - ImVec2 clipped_mouse_pos = ImVec2(std::min(std::max(io.MousePos.x, bbmin.x), bbmax.x), - std::min(std::max(io.MousePos.y, bbmin.y), bbmax.y)); + ImVec2 bbmin = ImGui::GetItemRectMin(); + ImVec2 bbmax = ImGui::GetItemRectMax(); + ImVec2 clipped_mouse_pos = ImVec2( + (std::min)((std::max)(io.MousePos.x, bbmin.x), bbmax.x), + (std::min)((std::max)(io.MousePos.y, bbmin.y), bbmax.y)); - if (clicked_on_item) - { - vec2f mouse_pos = (vec2f(clipped_mouse_pos) - view_offset) / view_scale; - mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); - mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); + if (clicked_on_item) + { + vec2f mouse_pos = (vec2f(clipped_mouse_pos) - view_offset) / view_scale; + mouse_pos.x = clamp(mouse_pos.x, 0.f, 1.f); + mouse_pos.y = clamp(mouse_pos.y, 0.f, 1.f); - if (io.MouseDown[0]) - { - if (selected_point != (size_t)-1) - { - alpha_control_pts[selected_point] = mouse_pos; + if (io.MouseDown[0]) + { + if (selected_point != (size_t)-1) + { - // Keep the first and last control points at the edges - if (selected_point == 0) - { - alpha_control_pts[selected_point].x = 0.f; - } - else if (selected_point == alpha_control_pts.size() - 1) - { - alpha_control_pts[selected_point].x = 1.f; - } - } - else - { - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); - return dist <= point_radius; }); - // No nearby point, we're adding a new one - if (fnd == alpha_control_pts.end()) - { - alpha_control_pts.push_back(mouse_pos); - } - } - - // Keep alpha control points ordered by x coordinate, update - // selected point index to match - std::sort(alpha_control_pts.begin(), - alpha_control_pts.end(), - [](const vec2f &a, const vec2f &b) - { return a.x < b.x; }); - if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) - { - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); - return dist <= point_radius; }); - selected_point = std::distance(alpha_control_pts.begin(), fnd); - } - update_colormap(); - } - else if (ImGui::IsMouseClicked(1)) + // Keep the first and last control points at the edges + if (selected_point == 0) + { + alpha_control_pts[selected_point].x = 0.f; + } + else if (selected_point == alpha_control_pts.size() - 1) + { + alpha_control_pts[selected_point].x = 1.f; + } + else + { + if (mouse_pos.x - alpha_control_pts[selected_point - 1].x < alpha_points_min_distance || alpha_control_pts[selected_point + 1].x - mouse_pos.x < alpha_points_min_distance) { - selected_point = -1; - // Find and remove the point - auto fnd = std::find_if( - alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) - { - const vec2f pt_pos = p * view_scale + view_offset; - float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); - return dist <= point_radius; }); - // We also want to prevent erasing the first and last points - if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && - fnd != alpha_control_pts.end() - 1) - { - alpha_control_pts.erase(fnd); - } - update_colormap(); + alpha_control_pts[selected_point].y = mouse_pos.y; } else { - selected_point = -1; + alpha_control_pts[selected_point] = mouse_pos; } + } } else { - selected_point = -1; + if (io.MousePos.x - canvas_pos.x <= point_radius) + { + selected_point = 0; + } + else if (io.MousePos.x - canvas_pos.x >= canvas_size.x - point_radius) + { + selected_point = alpha_control_pts.size() - 1; + } + else + { + auto fnd = + std::find_if(alpha_control_pts.begin(), + alpha_control_pts.end(), + [&](const vec2f &p) + { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; + }); + // No nearby point, we're adding a new one + if (fnd == alpha_control_pts.end()) + { + alpha_control_pts.push_back(mouse_pos); + std::sort(alpha_control_pts.begin(), + alpha_control_pts.end(), + [](const vec2f &a, const vec2f &b) + { return a.x < b.x; }); + if (selected_point != 0 && selected_point != alpha_control_pts.size() - 1) + { + fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) + { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; }); + } + } + selected_point = std::distance(alpha_control_pts.begin(), fnd); + } } - - // Draw the alpha control points, and build the points for the polyline - // which connects them - std::vector polyline_pts; - for (const auto &pt : alpha_control_pts) + update_colormap(); + } + else if (ImGui::IsMouseClicked(1)) + { + selected_point = -1; + // Find and remove the point + auto fnd = std::find_if( + alpha_control_pts.begin(), alpha_control_pts.end(), [&](const vec2f &p) + { + const vec2f pt_pos = p * view_scale + view_offset; + float dist = (pt_pos - vec2f(clipped_mouse_pos)).length(); + return dist <= point_radius; }); + // We also want to prevent erasing the first and last points + if (fnd != alpha_control_pts.end() && fnd != alpha_control_pts.begin() && + fnd != alpha_control_pts.end() - 1) { - const vec2f pt_pos = pt * view_scale + view_offset; - polyline_pts.push_back(pt_pos); - draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); + alpha_control_pts.erase(fnd); } - draw_list->AddPolyline( - polyline_pts.data(), (int)polyline_pts.size(), 0xFFFFFFFF, false, 2.f); - draw_list->PopClipRect(); - } - - bool TransferFunctionWidget::changed() const - { - return colormap_changed; + update_colormap(); + } + else + { + selected_point = -1; + } } - - std::vector TransferFunctionWidget::get_colormap() + else { - colormap_changed = false; - return current_colormap; + selected_point = -1; } - std::vector TransferFunctionWidget::get_colormapf() + // Draw the alpha control points, and build the points for the polyline + // which connects them + std::vector polyline_pts; + for (const auto &pt : alpha_control_pts) { - colormap_changed = false; - std::vector colormapf(current_colormap.size(), 0.f); - for (size_t i = 0; i < current_colormap.size(); ++i) - { - colormapf[i] = current_colormap[i] / 255.f; - } - return colormapf; + const vec2f pt_pos = pt * view_scale + view_offset; + polyline_pts.push_back(pt_pos); + draw_list->AddCircleFilled(pt_pos, point_radius, 0xFFFFFFFF); } - - void TransferFunctionWidget::draw_legend() + draw_list->AddPolyline(polyline_pts.data(), (int)polyline_pts.size(), 0xFFFFFFFF, false, 2.f); + draw_list->PopClipRect(); + + // Add Label tick marks + int nbTicks = 5; + vec2f tick_pos = ImGui::GetCursorScreenPos(); + tick_pos.y -= ImGui::GetStyle().ItemSpacing.y; + vec2f tick_size = ImGui::GetContentRegionAvail(); + tick_size.y = 5; + draw_list->PushClipRect(tick_pos, ImVec2(tick_pos.x + tick_size.x, tick_pos.y + tick_size.y)); + + for (int i = 0; i < nbTicks; i++) { - GLint viewport[4]; - GLfloat projection[16]; - GLfloat modelview[16]; - - int min[2] = {50, 100}; - int max[2] = {400, 120}; - - glGetIntegerv(GL_VIEWPORT, &viewport[0]); - glGetFloatv(GL_PROJECTION_MATRIX, &projection[0]); - glGetFloatv(GL_MODELVIEW_MATRIX, &modelview[0]); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, viewport[2], 0, viewport[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], max[1], 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], max[1], 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glEnd(); - glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, 0); - - glBindTexture(GL_TEXTURE_2D, colormap_img); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex3f(min[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 1); - glVertex3f(max[0], 0.5f * (min[1] + max[1]), 0); - glTexCoord2f(1, 0); - glVertex3f(max[0], min[1], 0); - glTexCoord2f(0, 0); - glVertex3f(min[0], min[1], 0); - glEnd(); - glDisable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, 0); - - // Add Label tick marks - int nbTicks = 4; - float diff = (max[0] - min[0]) / nbTicks; - for (int i = 0; i <= nbTicks; i++) - { - float pos_x = min[0] + i * diff; - glBegin(GL_LINES); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glVertex3f(pos_x, min[1], 0); - glVertex3f(pos_x, min[1] - 5, 0); - glEnd(); - - float percentage = float(i) / (nbTicks); - float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; - std::stringstream text; - text << std::fixed << std::setprecision(4) << val; - - FontHandler::getInstance()->renderTextBox(text.str(), pos_x - 100, min[1] - 15, 0, 200.0, 10, TextAlignment::CENTER); - } - - glEnable(GL_LIGHTING); + float percentage = float(i) / (nbTicks - 1); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(projection); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(modelview); + draw_list->AddLine(ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_size.y), + ImVec2(tick_pos.x + percentage * (tick_size.x - 1), tick_pos.y + tick_size.y), ImColor(255, 255, 255, 255), 1); } + draw_list->PopClipRect(); - void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) + // Add Label text + const float ItemSpacing = ImGui::GetStyle().ItemSpacing.x; + for (int i = 0; i < nbTicks; i++) { - bool show_legend = true; - ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); - ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); - ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); - ImGuiIO &io = ImGui::GetIO(); - ImTextureID my_tex_id = io.Fonts->TexID; - float texture_width = (float)io.Fonts->TexWidth; - float texture_height = (float)io.Fonts->TexHeight; - std::string max_str = std::to_string(m_min_max_val[1]); - float offset = 0.008 * (texture_width * max_str.size()); - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); - ImGui::Text("%.0f", m_min_max_val[0]); - ImGui::SameLine((legend_width / 2)); - ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); - ImGui::SameLine(ImGui::GetWindowWidth() - offset); - ImGui::Text(" %.0f", m_min_max_val[1]); - ImGui::End(); + float percentage = float(i) / (nbTicks - 1); + float val = (m_min_max_val[1] - m_min_max_val[0]) * percentage + m_min_max_val[0]; + std::stringstream text; + text << std::fixed << std::setprecision(4) << val; + if (i == 0) + { + } + else if (i == nbTicks - 1) + { + ImGui::SameLine(ImGui::GetWindowWidth() - ItemSpacing - ImGui::CalcTextSize(text.str().c_str()).x); + } + else + { + ImGui::SameLine((ImGui::GetWindowWidth()) * percentage - ImGui::CalcTextSize(text.str().c_str()).x * 0.5); + } + ImGui::Text(text.str().c_str()); } - - void TransferFunctionWidget::get_colormapf(std::vector &color, - std::vector &opacity) + } + + bool TransferFunctionWidget::changed() const + { + return colormap_changed; + } + + std::vector TransferFunctionWidget::get_colormap() + { + colormap_changed = false; + return current_colormap; + } + + std::vector TransferFunctionWidget::get_colormapf() + { + colormap_changed = false; + std::vector colormapf(current_colormap.size(), 0.f); + for (size_t i = 0; i < current_colormap.size(); ++i) { - colormap_changed = false; - color.resize((current_colormap.size() / 4) * 3); - opacity.resize(current_colormap.size() / 4); - for (size_t i = 0; i < current_colormap.size() / 4; ++i) - { - color[i * 3] = current_colormap[i * 4] / 255.f; - color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; - color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; - opacity[i] = current_colormap[i * 4 + 3] / 255.f; - } + colormapf[i] = current_colormap[i] / 255.f; } - - void TransferFunctionWidget::update_gpu_image() + return colormapf; + } + + void TransferFunctionWidget::setHistogram(const std::vector &hist) + { + current_histogram = hist; + } + + std::vector &TransferFunctionWidget::getHistogram() + { + return current_histogram; + } + + void TransferFunctionWidget::setMinMax(const float min, const float max) + { + m_min_max_val[0] = min; + m_min_max_val[1] = max; + } + + void TransferFunctionWidget::setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha) + { + if (hist1.size() != hist2.size()) + return; + + current_histogram.clear(); + for (int i = 0; i < hist1.size(); i++) + current_histogram.push_back(hist1[i] * alpha + hist2[i] * (1.0f - alpha)); + } + + void TransferFunctionWidget::get_colormapf(std::vector &color, std::vector &opacity) + { + colormap_changed = false; + color.resize((current_colormap.size() / 4) * 3); + opacity.resize(current_colormap.size() / 4); + for (size_t i = 0; i < current_colormap.size() / 4; ++i) { - GLint prev_tex_2d = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); - - if (colormap_img == (GLuint)-1) - { - glGenTextures(1, &colormap_img); - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - if (gpu_image_stale) - { - gpu_image_stale = false; - glBindTexture(GL_TEXTURE_2D, colormap_img); - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGB8, - (GLsizei)(current_colormap.size() / 4), - 1, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - current_colormap.data()); - } - glBindTexture(GL_TEXTURE_2D, prev_tex_2d); + color[i * 3] = current_colormap[i * 4] / 255.f; + color[i * 3 + 1] = current_colormap[i * 4 + 1] / 255.f; + color[i * 3 + 2] = current_colormap[i * 4 + 2] / 255.f; + opacity[i] = current_colormap[i * 4 + 3] / 255.f; } - - void TransferFunctionWidget::update_colormap() + } + + void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) + { + bool show_legend = true; + ImGui::SetNextWindowPos(ImVec2(legend_pos_x, legend_pos_y)); + ImGui::SetNextWindowSize(ImVec2(legend_width, legend_height)); + ImGui::Begin("##legend", &show_legend, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); + ImGuiIO &io = ImGui::GetIO(); + ImTextureID my_tex_id = io.Fonts->TexID; + float texture_width = (float)io.Fonts->TexWidth; + float texture_height = (float)io.Fonts->TexHeight; + std::string max_str = std::to_string(m_min_max_val[1]); + float offset = 0.008 * (texture_width * max_str.size()); + ImGui::Image(reinterpret_cast(colormap_img), ImVec2(legend_width, 16)); + ImGui::Text("%.0f", m_min_max_val[0]); + ImGui::SameLine((legend_width / 2)); + ImGui::Text("%.0f", (m_min_max_val[0] + m_min_max_val[1]) / 2); + ImGui::SameLine(ImGui::GetWindowWidth() - offset); + ImGui::Text(" %.0f", m_min_max_val[1]); + ImGui::End(); + } + + void TransferFunctionWidget::draw_histogram() + { + + const ImGuiIO &io = ImGui::GetIO(); + + ImGui::Text("Histogram"); + + vec2f canvas_size = ImGui::GetContentRegionAvail(); + // Note: If you're not using OpenGL for rendering your UI, the setup for + // displaying the colormap texture in the UI will need to be updated. + // TODO(#45) ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); + vec2f canvas_pos = ImGui::GetCursorScreenPos(); + // canvas_size.y -= 250; + canvas_size.y -= 20; + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); + + const vec2f view_scale(canvas_size.x, -canvas_size.y); + const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); + + draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); + + ImGui::InvisibleButton("hstg_canvas", canvas_size); + + // Draw the alpha control points, and build the points for the polyline + // which connects them + + // Code to Draw histogram in the UI + for (int i = 0; i < current_histogram.size(); i++) { - colormap_changed = true; - gpu_image_stale = true; - current_colormap = colormaps[selected_colormap].colormap; - // We only change opacities for now, so go through and update the opacity - // by blending between the neighboring control points - auto a_it = alpha_control_pts.begin(); - const size_t npixels = current_colormap.size() / 4; - for (size_t i = 0; i < npixels; ++i) - { - float x = static_cast(i) / npixels; - auto high = a_it + 1; - if (x > high->x) - { - ++a_it; - ++high; - } - float t = (x - a_it->x) / (high->x - a_it->x); - float alpha = (1.f - t) * a_it->y + t * high->y; - current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); - } + vec2f lp = vec2f(((float)i) / current_histogram.size(), 0.0f); + vec2f hp = vec2f(((float)i + 1.0f) / current_histogram.size(), current_histogram[i]); + draw_list->AddRectFilled(lp * view_scale + view_offset, hp * view_scale + view_offset, 0x77777777); } - void TransferFunctionWidget::set_quantiles(float min, float max) - { - m_quantiles[0] = min; - m_quantiles[1] = max; - } + draw_list->PopClipRect(); + } - void TransferFunctionWidget::get_quantiles(float &min, float &max) - { - min = m_quantiles[0]; - max = m_quantiles[1]; - } + void TransferFunctionWidget::update_gpu_image() + { + GLint prev_tex_2d = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex_2d); - void TransferFunctionWidget::setHistogram(const std::vector &hist) + if (colormap_img == (GLuint)-1) { - current_histogram = hist; + glGenTextures(1, &colormap_img); + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - - std::vector &TransferFunctionWidget::getHistogram() + if (gpu_image_stale) { - return current_histogram; + gpu_image_stale = false; + glBindTexture(GL_TEXTURE_2D, colormap_img); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + (GLsizei)current_colormap.size() / 4, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + current_colormap.data()); } - - void TransferFunctionWidget::setMinMax(const float min, const float max) + glBindTexture(GL_TEXTURE_2D, prev_tex_2d); + } + + void TransferFunctionWidget::update_colormap() + { + colormap_changed = true; + gpu_image_stale = true; + current_colormap = colormaps[selected_colormap].colormap; + // We only change opacities for now, so go through and update the opacity + // by blending between the neighboring control points + auto a_it = alpha_control_pts.begin(); + const size_t npixels = current_colormap.size() / 4; + + for (size_t i = 0; i < npixels; ++i) { - m_min_max_val[0] = min; - m_min_max_val[1] = max; + float x = static_cast(i) / npixels; + auto high = a_it + 1; + if (x > high->x) + { + ++a_it; + ++high; + } + float t = (x - a_it->x) / (high->x - a_it->x); + float alpha = (1.f - t) * a_it->y + t * high->y; + current_colormap[i * 4 + 3] = static_cast(clamp(alpha * 255.f, 0.f, 255.f)); } + } - void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, - size_t size, - const std::string &name) - { - int w, h, n; - uint8_t *img_data = stbi_load_from_memory(buf, (int)size, &w, &h, &n, 4); - auto img = std::vector(img_data, img_data + w * 1 * 4); - stbi_image_free(img_data); + void TransferFunctionWidget::set_quantiles(float min, float max) + { + m_quantiles[0] = min; + m_quantiles[1] = max; + } - colormaps.emplace_back(name, img, ColorSpace::SRGB); + void TransferFunctionWidget::get_quantiles(float &min, float &max) + { + min = m_quantiles[0]; + max = m_quantiles[1]; + } - Colormap &cmap = colormaps.back(); - for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) - { - for (size_t j = 0; j < 3; ++j) - { - const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); - cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); - } - } + void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, + size_t size, + const std::string &name) + { + int w, h, n; + + uint8_t *img_data = stbi_load_from_memory(buf, size, &w, &h, &n, 4); + + int out_w = 256; + uint8_t *output_pixels = (unsigned char *)malloc(out_w * h * n); + + stbir_resize_uint8(img_data, w, h, 0, output_pixels, out_w, h, 0, n); + auto img = std::vector(output_pixels, output_pixels + out_w * 1 * 4); + + stbi_image_free(img_data); + stbi_image_free(output_pixels); + + colormaps.emplace_back(name, img, SRGB); + Colormap &cmap = colormaps.back(); + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) + { + for (size_t j = 0; j < 3; ++j) + { + const float x = srgb_to_linear(cmap.colormap[i * 4 + j] / 255.f); + cmap.colormap[i * 4 + j] = static_cast(clamp(x * 255.f, 0.f, 255.f)); + } } + } } diff --git a/libs/UIHelpers/transfer_function_widget.h b/libs/UIHelpers/transfer_function_widget.h index 981c9ea..60c23e2 100644 --- a/libs/UIHelpers/transfer_function_widget.h +++ b/libs/UIHelpers/transfer_function_widget.h @@ -1,7 +1,10 @@ -#pragma once +#ifndef TransferFunctionWidget_H_ +#define TransferFunctionWidget_H_ + #ifdef _WIN32 #include "GL/glew.h" #include "GL/wglew.h" +#define NOMINMAX #elif (!defined(__APPLE__)) #include "GL/glxew.h" #endif @@ -13,22 +16,21 @@ #include #elif defined(__APPLE__) #define GL_GLEXT_PROTOTYPES -#include -#include +#include +#include #else #define GL_GLEXT_PROTOTYPES #include #endif + #include #include #include -#include "imgui.h" #include "Vec2.h" namespace tfnw { - - enum class ColorSpace + enum ColorSpace { LINEAR, SRGB @@ -41,9 +43,7 @@ namespace tfnw std::vector colormap; ColorSpace color_space; - Colormap(const std::string &name, - const std::vector &img, - const ColorSpace color_space); + Colormap(const std::string &name, const std::vector &img, const ColorSpace color_space); }; class TransferFunctionWidget @@ -60,15 +60,16 @@ namespace tfnw bool colormap_changed = true; GLuint colormap_img = -1; - public: - TransferFunctionWidget(); + std::vector current_histogram; + float m_min_max_val[2]; + public: + size_t selected_colormap = 0; std::vector alpha_control_pts = {vec2f(0.f), vec2f(1.f)}; - size_t selected_colormap = 0; + TransferFunctionWidget(); - // Add a colormap preset. The image should be a 1D RGBA8 image, if the image - // is provided in sRGBA colorspace it will be linearized + // Add a colormap preset. The image should be a 1D RGBA8 image void add_colormap(const Colormap &map); // Add the transfer function UI into the currently active window @@ -84,6 +85,14 @@ namespace tfnw // Get back the RGBA32F color data for the transfer function std::vector get_colormapf(); + void setHistogram(const std::vector &hist); + + std::vector &getHistogram(); + + void setMinMax(const float min, const float max); + + void setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha); + // Get back the RGBA32F color data for the transfer function // as separate color and opacity vectors void get_colormapf(std::vector &color, std::vector &opacity); @@ -98,26 +107,16 @@ namespace tfnw colormap_img = colormap; } - void draw_legend(); - void draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); + void draw_histogram(); + void update_colormap(); void set_quantiles(float min, float max); void get_quantiles(float &min, float &max); - std::vector current_histogram; - - float m_min_max_val[2]; - - void setHistogram(const std::vector &hist); - - std::vector &getHistogram(); - - void setMinMax(const float min, const float max); - private: void update_gpu_image(); @@ -125,4 +124,7 @@ namespace tfnw float m_quantiles[2]; }; + } + +#endif \ No newline at end of file diff --git a/src/UI/UIView.cpp b/src/UI/UIView.cpp index d751b2d..a868815 100644 --- a/src/UI/UIView.cpp +++ b/src/UI/UIView.cpp @@ -1779,14 +1779,6 @@ void UIView::set_volume_time_info(time_t time) } } -void UIView::draw_transfer_function_legend() -{ - if (m_use_transferfunction) - { - tfn_widget[0].draw_legend(); - } -} - void UIView::set_transfer_function_min_max(float min, float max) { if (m_use_transferfunction) @@ -1813,7 +1805,7 @@ void UIView::load_ocean_color_maps() std::string name = color_map_name.substr(0, color_map_name.find_first_of(".")); - tfnw::Colormap color_map(name, img, tfnw::ColorSpace::SRGB); + tfnw::Colormap color_map(name, img, tfnw::SRGB); tfn_widget[0].add_colormap(color_map); } diff --git a/src/vrapp/VRVolumeApp.cpp b/src/vrapp/VRVolumeApp.cpp index c94c10f..30f525f 100644 --- a/src/vrapp/VRVolumeApp.cpp +++ b/src/vrapp/VRVolumeApp.cpp @@ -633,11 +633,6 @@ void VRVolumeApp::render(const MinVR::VRGraphicsState &render_state) m_depthTextures[m_rendercount]->copyDepthbuffer(); (static_cast(m_renders[1]))->setDepthTexture(m_depthTextures[m_rendercount]); - if (m_is2d) - { - m_ui_view->draw_transfer_function_legend(); - } - // drawTime if (m_is2d && m_animated) { diff --git a/superbuild/vr-imgui/CMakeLists.txt b/superbuild/vr-imgui/CMakeLists.txt index dca310b..20089c6 100644 --- a/superbuild/vr-imgui/CMakeLists.txt +++ b/superbuild/vr-imgui/CMakeLists.txt @@ -19,7 +19,7 @@ set(EXTERNAL_DIR_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/../install/) build_git_subproject( NAME vr-imgui URL https://github.com/brown-ccv/VR-imgui.git - TAG v1.0 + TAG v2.0.0 BUILD_ARGS -DBUILD_WITH_VIRTUALKEYBOARD=OFF -DBUILD_WITH_FILEBROWSER=ON From a3204f839c5a464f73f2ab2e6c9a463db78c2783 Mon Sep 17 00:00:00 2001 From: Kmilo9999 Date: Fri, 9 Sep 2022 17:02:59 -0400 Subject: [PATCH 5/5] Go up on vr-imgui 2.0.1. Fix release build of FTGL --- superbuild/CMakeLists.txt | 11 ++++++++++- superbuild/FTGL/CMakeLists.txt | 4 ++-- superbuild/vr-imgui/CMakeLists.txt | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/superbuild/CMakeLists.txt b/superbuild/CMakeLists.txt index f8eb02d..cefa1c3 100644 --- a/superbuild/CMakeLists.txt +++ b/superbuild/CMakeLists.txt @@ -315,10 +315,19 @@ execute_process( # Build external project execute_process( - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/FTGL + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/FTGL --config ${CMAKE_BUILD_TYPE} ) #ENDIF() +# find_package(OpenGL REQUIRED) +FIND_PACKAGE(FTGL REQUIRED ) +if(FTGL_FOUND) + message(STATUS "FTGL found") + message(STATUS "FTGL_INCLUDE_DIR ${FTGL_INCLUDE_DIR}") + else() + message(STATUS "FTGL NOT found" ) +endif() + #CCPFS # Configure external project #IF(NOT EXISTS ${CMAKE_SOURCE_DIR}/CPPFSD/CPPFSD) diff --git a/superbuild/FTGL/CMakeLists.txt b/superbuild/FTGL/CMakeLists.txt index a727817..930e07e 100644 --- a/superbuild/FTGL/CMakeLists.txt +++ b/superbuild/FTGL/CMakeLists.txt @@ -4,7 +4,7 @@ include(ExternalProject) include(GNUInstallDirs) include(${CMAKE_SOURCE_DIR}/../macros.cmake) -message("ZLIB INSTALL_DIR_ABSOLUTE ${INSTALL_DIR_ABSOLUTE}") +message("FTGL INSTALL_DIR_ABSOLUTE ${INSTALL_DIR_ABSOLUTE}") message("FTGL CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}") set(EXTERNAL_DIR_LOCATION ${CMAKE_BINARY_DIR}) @@ -15,7 +15,7 @@ build_git_subproject( URL https://github.com/ulrichard/ftgl.git TAG master BUILD_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBUILD_SHARED_LIBS=OFF -DFREETYPE_INCLUDE_DIR_ft2build=${CMAKE_INSTALL_PREFIX}/include/freetype2 - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ) \ No newline at end of file diff --git a/superbuild/vr-imgui/CMakeLists.txt b/superbuild/vr-imgui/CMakeLists.txt index 20089c6..746ac55 100644 --- a/superbuild/vr-imgui/CMakeLists.txt +++ b/superbuild/vr-imgui/CMakeLists.txt @@ -19,7 +19,7 @@ set(EXTERNAL_DIR_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/../install/) build_git_subproject( NAME vr-imgui URL https://github.com/brown-ccv/VR-imgui.git - TAG v2.0.0 + TAG v2.0.1 BUILD_ARGS -DBUILD_WITH_VIRTUALKEYBOARD=OFF -DBUILD_WITH_FILEBROWSER=ON