diff --git a/include/UI/UIView.h b/include/UI/UIView.h index ae6249b..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; } @@ -202,7 +200,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/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/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..edacf72 100644 --- a/libs/UIHelpers/transfer_function_widget.cpp +++ b/libs/UIHelpers/transfer_function_widget.cpp @@ -1,33 +1,11 @@ - -#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 @@ -37,547 +15,525 @@ #include "stb_image.h" #include "stb_image_resize.h" -template -T clamp(T x, T min, T max) +static const float alpha_points_min_distance = 0.006f; + +namespace tfnw { - if (x < min) + template + inline T clamp(T x, T min, T max) { - return min; + if (x < min) + { + return min; + } + if (x > max) + { + return max; + } + return x; } - if (x > max) + + inline float srgb_to_linear(const float x) { - return max; + if (x <= 0.04045f) + { + return x / 12.92f; + } + else + { + return std::pow((x + 0.055f) / 1.055f, 2.4f); + } } - return x; -} -Colormap::Colormap(const std::string &name, const std::vector &img) - : name(name), colormap(img) -{ -} - -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++) + Colormap::Colormap(const std::string &name, + const std::vector &img, + const ColorSpace color_space) + : name(name), colormap(img), color_space(color_space) { - 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); -} - -void TransferFunctionWidget::draw_ui() -{ - update_gpu_image(); - colormap_changed = false; + 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); + } - const ImGuiIO &io = ImGui::GetIO(); + m_min_max_val[0] = 0.0f; + m_min_max_val[1] = 1.0f; - ImGui::Text("Transfer Function"); - ImGui::TextWrapped( - "Left click to add a point, right click remove. " - "Left click + drag to move points."); + m_quantiles[0] = 0.05f; + m_quantiles[1] = 0.95f; + } - if (ImGui::BeginCombo("Colormap", colormaps[selected_colormap].name.c_str())) + void TransferFunctionWidget::add_colormap(const Colormap &map) { - for (size_t i = 0; i < colormaps.size(); ++i) + colormaps.push_back(map); + if (colormaps.back().color_space == SRGB) { - if (ImGui::Selectable(colormaps[i].name.c_str(), selected_colormap == i)) + Colormap &cmap = colormaps.back(); + cmap.color_space = LINEAR; + for (size_t i = 0; i < cmap.colormap.size() / 4; ++i) { - selected_colormap = i; - update_colormap(); + 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)); + } } } - 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. - ImGui::Image(reinterpret_cast(colormap_img), ImVec2(canvas_size.x, 16)); - vec2f canvas_pos = ImGui::GetCursorScreenPos(); - canvas_size.y -= 80; + void TransferFunctionWidget::draw_ui() + { + update_gpu_image(); + + 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(); + } - const float point_radius = 20.f; + 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; - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); + const float point_radius = 10.f; - const vec2f view_scale(canvas_size.x, -canvas_size.y); - const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(canvas_pos, canvas_pos + canvas_size); - draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); + const vec2f view_scale(canvas_size.x, -canvas_size.y); + const vec2f view_offset(canvas_pos.x, canvas_pos.y + canvas_size.y); - ImGui::InvisibleButton("tfn_canvas", canvas_size); - if (ImGui::IsItemHovered() || selected_point != (size_t)-1) - { + draw_list->AddRect(canvas_pos, canvas_pos + canvas_size, ImColor(180, 180, 180, 255)); - 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); + ImGui::InvisibleButton("tfn_canvas", canvas_size); - if (io.MouseDown[0]) + static bool clicked_on_item = false; + if ((!io.MouseDown[0]) && (!io.MouseDown[1])) { - if (selected_point != (size_t)-1) - { - alpha_control_pts[selected_point] = mouse_pos; + clicked_on_item = false; + } + if (ImGui::IsItemHovered() || selected_point != (size_t)-1 && (io.MouseDown[0] || io.MouseDown[1])) + { + clicked_on_item = true; + } - // make sure point does not cross + 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)); - // 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 + 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]) { - // 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) + if (selected_point != (size_t)-1) { - selected_point = alpha_control_pts.size() - 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) + { + alpha_control_pts[selected_point].y = mouse_pos.y; + } + else + { + alpha_control_pts[selected_point] = mouse_pos; + } + } } 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()) + 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) { - 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) + 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()) { - 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; }); + 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); } - selected_point = std::distance(alpha_control_pts.begin(), fnd); } + update_colormap(); } - 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) + else if (ImGui::IsMouseClicked(1)) { - alpha_control_pts.erase(fnd); + 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(); + } + else + { + selected_point = -1; } - update_colormap(); } 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 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(), (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++) + { + float percentage = float(i) / (nbTicks - 1); - draw_list->AddPolyline(polyline_pts.data(), polyline_pts.size(), 0xFFFFFFFF, false, 2.f); - draw_list->PopClipRect(); + 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 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)); + // 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); + } + ImGui::Text(text.str().c_str()); + } + } - for (int i = 0; i < nbTicks; i++) + bool TransferFunctionWidget::changed() const { - float percentage = float(i) / (nbTicks - 1); + return colormap_changed; + } - 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); + std::vector TransferFunctionWidget::get_colormap() + { + colormap_changed = false; + return current_colormap; } - draw_list->PopClipRect(); - // Add Label text - const float ItemSpacing = ImGui::GetStyle().ItemSpacing.x; - for (int i = 0; i < nbTicks; i++) + std::vector TransferFunctionWidget::get_colormapf() { - 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 + colormap_changed = false; + std::vector colormapf(current_colormap.size(), 0.f); + for (size_t i = 0; i < current_colormap.size(); ++i) { - ImGui::SameLine((ImGui::GetWindowWidth()) * percentage - ImGui::CalcTextSize(text.str().c_str()).x * 0.5); + colormapf[i] = current_colormap[i] / 255.f; } - ImGui::Text(text.str().c_str()); + return colormapf; } -} - -bool TransferFunctionWidget::changed() const -{ - return colormap_changed; -} - -std::vector TransferFunctionWidget::get_colormap() -{ - 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) + void TransferFunctionWidget::setHistogram(const std::vector &hist) { - colormapf[i] = current_colormap[i] / 255.f; + current_histogram = hist; } - return colormapf; -} - -void TransferFunctionWidget::setHistogram(const std::vector &hist) -{ - current_histogram = hist; -} -std::vector &TransferFunctionWidget::getHistogram() -{ - return current_histogram; -} + 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::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; + 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)); -} + 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) -{ - color.resize((current_colormap.size() / 4) * 3); - opacity.resize(current_colormap.size() / 4); - for (size_t i = 0; i < current_colormap.size() / 4; ++i) + void TransferFunctionWidget::get_colormapf(std::vector &color, std::vector &opacity) { - 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; + 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::draw_legend() -{ - 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++) + void TransferFunctionWidget::draw_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height) { - 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); + 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(); } - glEnable(GL_LIGHTING); - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(projection); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(modelview); -} - -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() + { -void TransferFunctionWidget::draw_histogram() -{ + const ImGuiIO &io = ImGui::GetIO(); - const ImGuiIO &io = ImGui::GetIO(); + ImGui::Text("Histogram"); - 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; - 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); - 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("hstg_canvas", canvas_size); - ImGui::InvisibleButton("hstg_canvas", canvas_size); + // Draw the alpha control points, and build the points for the polyline + // which connects them - // 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); + } - // 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(); } - draw_list->PopClipRect(); -} - -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) + 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 (gpu_image_stale) + { + 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()); + } glBindTexture(GL_TEXTURE_2D, prev_tex_2d); } -} -void TransferFunctionWidget::update_colormap() -{ - 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) + void TransferFunctionWidget::update_colormap() { - float x = static_cast(i) / npixels; - auto high = a_it + 1; - if (x > high->x) + 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) { - ++a_it; - ++high; + 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)); } - 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::set_quantiles(float min, float max) -{ - m_quantiles[0] = min; - m_quantiles[1] = max; -} + void TransferFunctionWidget::set_quantiles(float min, float max) + { + m_quantiles[0] = min; + m_quantiles[1] = max; + } -void TransferFunctionWidget::get_quantiles(float &min, float &max) -{ - min = m_quantiles[0]; - max = m_quantiles[1]; -} + void TransferFunctionWidget::get_quantiles(float &min, float &max) + { + min = m_quantiles[0]; + max = m_quantiles[1]; + } -void TransferFunctionWidget::load_embedded_preset(const uint8_t *buf, - size_t size, - const std::string &name) -{ - int w, h, n; + 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); - 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); - 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); - 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); - 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)); + } + } + } - colormaps.emplace_back(name, img); } diff --git a/libs/UIHelpers/transfer_function_widget.h b/libs/UIHelpers/transfer_function_widget.h index 7a15b1c..60c23e2 100644 --- a/libs/UIHelpers/transfer_function_widget.h +++ b/libs/UIHelpers/transfer_function_widget.h @@ -4,6 +4,7 @@ #ifdef _WIN32 #include "GL/glew.h" #include "GL/wglew.h" +#define NOMINMAX #elif (!defined(__APPLE__)) #include "GL/glxew.h" #endif @@ -25,95 +26,105 @@ #include #include #include -#include "imgui/imgui.h" #include "Vec2.h" -struct Colormap +namespace tfnw { - std::string name; - // An RGBA8 1D image - std::vector colormap; + enum ColorSpace + { + LINEAR, + SRGB + }; - Colormap(const std::string &name, const std::vector &img); -}; + struct Colormap + { + std::string name; + // An RGBA8 1D image + std::vector colormap; + ColorSpace color_space; -class TransferFunctionWidget -{ + Colormap(const std::string &name, const std::vector &img, const ColorSpace color_space); + }; + + class TransferFunctionWidget + { - std::vector colormaps; + std::vector colormaps; - std::vector current_colormap; + std::vector current_colormap; - size_t selected_point = -1; + size_t selected_point = -1; - bool colormap_changed = true; - GLuint colormap_img = -1; + bool clicked_on_item = false; + bool gpu_image_stale = true; + bool colormap_changed = true; + GLuint colormap_img = -1; - std::vector current_histogram; - float m_min_max_val[2]; + 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)}; + public: + size_t selected_colormap = 0; + std::vector alpha_control_pts = {vec2f(0.f), vec2f(1.f)}; - TransferFunctionWidget(); + TransferFunctionWidget(); - // 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 + 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); + void setHistogram(const std::vector &hist); - std::vector &getHistogram(); + std::vector &getHistogram(); - void setMinMax(const float min, const float max); + void setMinMax(const float min, const float max); - void setBlendedHistogram(const std::vector &hist1, const std::vector &hist2, float alpha); + 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); + // 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); - GLint get_colormap_gpu() - { - return colormap_img; - } + GLint get_colormap_gpu() + { + return colormap_img; + } - void set_colormap_gpu(GLint colormap) - { - colormap_img = colormap; - } + void set_colormap_gpu(GLint colormap) + { + 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_legend(float legend_pos_x, float legend_pos_y, float legend_width, float legend_height); + void draw_histogram(); - void draw_histogram(); + void update_colormap(); - void update_colormap(); + void set_quantiles(float min, float max); - void set_quantiles(float min, float max); + void get_quantiles(float &min, float &max); - void get_quantiles(float &min, float &max); + private: + void update_gpu_image(); -private: - void update_gpu_image(); + void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); - void load_embedded_preset(const uint8_t *buf, size_t size, const std::string &name); + float m_quantiles[2]; + }; - 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..a868815 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]; @@ -802,9 +802,9 @@ void UIView::draw_ui_callback() if (helper::ends_with_string(fileDialog.selected_fn, ".txt")) { /* - TODO #52 + TODO #52 Thread the data loading process to run in the background and implement loading UI component - + VRDataLoader* insta = VRDataLoader::get_instance(); std::thread t1 ( &VRDataLoader::load_txt_file, std::ref(m_controller_app), fileDialog.selected_path); t1.join(); @@ -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,8 @@ 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); } } diff --git a/src/vrapp/VRVolumeApp.cpp b/src/vrapp/VRVolumeApp.cpp index c2e6857..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) { @@ -760,18 +755,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); } } 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 dca310b..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 v1.0 + TAG v2.0.1 BUILD_ARGS -DBUILD_WITH_VIRTUALKEYBOARD=OFF -DBUILD_WITH_FILEBROWSER=ON