The open source OpenXR runtime
at prediction-2 322 lines 12 kB view raw
1// Copyright 2020, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Custom imgui elements. 6 * @author Christoph Haag <christoph.haag@collabora.com> 7 * 8 * based on ImGui::PlotEx() from dear imgui, v1.76 WIP 9 */ 10 11#include "../imgui/imgui.h" 12 13#ifndef IMGUI_DEFINE_MATH_OPERATORS 14#define IMGUI_DEFINE_MATH_OPERATORS 15#endif 16#include "../imgui/imgui_internal.h" 17 18#include <stdint.h> 19 20#include "cimgui_monado.h" 21 22using namespace ImGui; 23 24static void _draw_line(ImGuiWindow *window, int values_count, float scale_min, 25 float scale_max, float val, const char *unit, 26 const ImRect inner_bb, ImVec2 frame_size, ImU32 color) { 27 const float inv_scale = 28 (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); 29 ImVec2 tp0 = ImVec2( 30 0.0f, 1.0f - ImSaturate((val - scale_min) * 31 inv_scale)); // Point in the normalized space of 32 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); 33 const ImVec2 tp1 = 34 ImVec2(1.0, 1.0f - ImSaturate((val - scale_min) * inv_scale)); 35 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, tp1); 36 window->DrawList->AddLine(pos0, pos1, color); 37 38 char text[100]; 39 snprintf(text, 60, "%.2f %s", val, unit); 40 ImVec2 text_size = ImGui::CalcTextSize(text); 41 ImVec2 text_pos = {pos1.x - text_size.x, pos1.y}; 42 ImGui::PushStyleColor(ImGuiCol_Text, color); 43 ImGui::RenderText(text_pos, text); 44 ImGui::PopStyleColor(1); 45} 46 47static void _draw_grid(ImGuiWindow *window, int values_count, float scale_min, 48 float scale_max, float reference_timing, const char *unit, 49 const ImRect inner_bb, ImVec2 frame_size) { 50 51 const ImVec4 target_color{1.0f, 1.0f, 0.0f, .75f}; 52 _draw_line(window, values_count, scale_min, scale_max, reference_timing, unit, 53 inner_bb, frame_size, GetColorU32(target_color)); 54 55 const ImVec4 passive_color{0.35f, 0.35f, 0.35f, 1.00f}; 56 57 // always draw ~5 lines 58 const uint8_t max_lines = 5; 59 float step = (scale_max - scale_min) / (float)max_lines; 60 for (uint8_t i = 0; i < max_lines; ++i) { 61 float val = scale_min + step * (float)i; 62 _draw_line(window, values_count, scale_min, scale_max, val, unit, inner_bb, 63 frame_size, GetColorU32(passive_color)); 64 } 65} 66 67static void PlotTimings(const char *label, 68 float (*values_getter)(void *data, int idx), void *data, 69 int values_count, int values_offset, 70 const char *overlay_text, ImVec2 frame_size, 71 float reference_timing, bool center_reference_timing, 72 float range, const char *unit, bool dynamic_rescale) { 73 ImGuiWindow *window = GetCurrentWindow(); 74 if (window->SkipItems) 75 return; 76 77 ImGuiContext &g = *GImGui; 78 const ImGuiStyle &style = g.Style; 79 const ImGuiID id = window->GetID(label); 80 81 if (frame_size.x == 0.0f) 82 frame_size.x = CalcItemWidth(); 83 if (frame_size.y == 0.0f) 84 frame_size.y = (style.FramePadding.y * 2); 85 86 const ImRect frame_bb(window->DC.CursorPos, 87 window->DC.CursorPos + frame_size); 88 const ImRect inner_bb(frame_bb.Min + style.FramePadding, 89 frame_bb.Max - style.FramePadding); 90 const ImRect total_bb(frame_bb.Min, frame_bb.Max); 91 ItemSize(total_bb, style.FramePadding.y); 92 if (!ItemAdd(total_bb, 0, &frame_bb)) 93 return; 94 const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); 95 96 float v_min = FLT_MAX; 97 float v_max = -FLT_MAX; 98 for (int i = 0; i < values_count; i++) { 99 const float v = values_getter(data, i); 100 if (v != v) // Ignore NaN values 101 continue; 102 v_min = ImMin(v_min, v); 103 v_max = ImMax(v_max, v); 104 } 105 106 float scale_min = 107 center_reference_timing ? reference_timing - range : reference_timing; 108 float scale_max = reference_timing + range; 109 110 if (dynamic_rescale) { 111 if (v_max > scale_max) { 112 scale_max = v_max; 113 } 114 scale_max = (floorf(scale_max / 10 + 1)) * 10; 115 116 if (center_reference_timing) { 117 if (v_min < scale_min) { 118 scale_min = v_min; 119 } 120 scale_min = (floorf(scale_min / 10)) * 10; 121 122 // make sure reference timing stays centered 123 float lower_range = reference_timing - scale_min; 124 float upper_range = scale_max - reference_timing; 125 if (lower_range > upper_range) { 126 scale_max = reference_timing + lower_range; 127 } else if (upper_range > lower_range) { 128 scale_min = reference_timing - upper_range; 129 } 130 } 131 } 132 133 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, 134 style.FrameRounding); 135 136 _draw_grid(window, values_count, scale_min, scale_max, reference_timing, unit, 137 inner_bb, frame_size); 138 139 ImGuiPlotType plot_type = ImGuiPlotType_Lines; 140 const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; 141 if (values_count >= values_count_min) { 142 int res_w = ImMin((int)frame_size.x, values_count) + 143 ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 144 int item_count = 145 values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 146 147 // Tooltip on hover 148 int v_hovered = -1; 149 if (hovered && inner_bb.Contains(g.IO.MousePos)) { 150 const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / 151 (inner_bb.Max.x - inner_bb.Min.x), 152 0.0f, 0.9999f); 153 const int v_idx = (int)(t * item_count); 154 IM_ASSERT(v_idx >= 0 && v_idx < values_count); 155 156 const float v0 = 157 values_getter(data, (v_idx + values_offset) % values_count); 158 const float v1 = 159 values_getter(data, (v_idx + 1 + values_offset) % values_count); 160 if (plot_type == ImGuiPlotType_Lines) 161 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); 162 else if (plot_type == ImGuiPlotType_Histogram) 163 SetTooltip("%d: %8.4g", v_idx, v0); 164 v_hovered = v_idx; 165 } 166 167 const float t_step = 1.0f / (float)res_w; 168 const float inv_scale = 169 (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); 170 171 float v0 = values_getter(data, (0 + values_offset) % values_count); 172 float t0 = 0.0f; 173 ImVec2 tp0 = ImVec2( 174 t0, 1.0f - ImSaturate((v0 - scale_min) * 175 inv_scale)); // Point in the normalized space of 176 // our target rectangle 177 float histogram_zero_line_t = 178 (scale_min * scale_max < 0.0f) 179 ? (-scale_min * inv_scale) 180 : (scale_min < 0.0f ? 0.0f 181 : 1.0f); // Where does the zero line stands 182 183 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) 184 ? ImGuiCol_PlotLines 185 : ImGuiCol_PlotHistogram); 186 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) 187 ? ImGuiCol_PlotLinesHovered 188 : ImGuiCol_PlotHistogramHovered); 189 190 for (int n = 0; n < res_w; n++) { 191 const float t1 = t0 + t_step; 192 const int v1_idx = (int)(t0 * item_count + 0.5f); 193 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); 194 const float v1 = 195 values_getter(data, (v1_idx + values_offset + 1) % values_count); 196 const ImVec2 tp1 = 197 ImVec2(t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale)); 198 199 // NB: Draw calls are merged together by the DrawList system. Still, we 200 // should render our batch are lower level to save a bit of CPU. 201 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); 202 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, 203 (plot_type == ImGuiPlotType_Lines) 204 ? tp1 205 : ImVec2(tp1.x, histogram_zero_line_t)); 206 207 if (plot_type == ImGuiPlotType_Lines) { 208 window->DrawList->AddLine(pos0, pos1, 209 v_hovered == v1_idx ? col_hovered : col_base); 210 } else if (plot_type == ImGuiPlotType_Histogram) { 211 if (pos1.x >= pos0.x + 2.0f) 212 pos1.x -= 1.0f; 213 window->DrawList->AddRectFilled( 214 pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); 215 } 216 217 t0 = t1; 218 tp0 = tp1; 219 } 220 } 221 222 // Text overlay 223 if (overlay_text) 224 RenderTextClipped( 225 ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), 226 frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); 227 228 const float v = values_getter(data, (values_offset)); 229 ImGui::LabelText(label, "%6.2f %s [%6.2f, %6.2f]", v, unit, v_min, v_max); 230} 231 232extern "C" 233void igPlotTimings(const char *label, 234 float (*values_getter)(void *data, int idx), void *data, 235 int values_count, int values_offset, 236 const char *overlay_text, float scale_min, float scale_max, 237 ImVec2 frame_size, float reference_timing, 238 bool center_reference_timing, float range, const char *unit, 239 bool dynamic_rescale) { 240 PlotTimings(label, values_getter, data, values_count, values_offset, 241 overlay_text, frame_size, reference_timing, 242 center_reference_timing, range, unit, dynamic_rescale); 243} 244 245extern "C" 246void igToggleButton(const char *str_id, bool *v) { 247 ImVec2 p = ImGui::GetCursorScreenPos(); 248 ImDrawList *draw_list = ImGui::GetWindowDrawList(); 249 250 float height = ImGui::GetFrameHeight(); 251 float width = height * 1.55f; 252 float radius = height * 0.50f; 253 254 ImGui::InvisibleButton(str_id, ImVec2(width, height)); 255 if (ImGui::IsItemClicked()) 256 *v = !*v; 257 258 float t = *v ? 1.0f : 0.0f; 259 260 ImGuiContext &g = *GImGui; 261 float ANIM_SPEED = 0.08f; 262 if (g.LastActiveId == 263 g.CurrentWindow->GetID(str_id)) // && g.LastActiveIdTimer < ANIM_SPEED) 264 { 265 float t_anim = ImSaturate(g.LastActiveIdTimer / ANIM_SPEED); 266 t = *v ? (t_anim) : (1.0f - t_anim); 267 } 268 269 ImU32 col_bg; 270 if (ImGui::IsItemHovered()) 271 col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.78f, 0.78f, 0.78f, 1.0f), 272 ImVec4(0.64f, 0.83f, 0.34f, 1.0f), t)); 273 else 274 col_bg = ImGui::GetColorU32(ImLerp(ImVec4(0.85f, 0.85f, 0.85f, 1.0f), 275 ImVec4(0.56f, 0.83f, 0.26f, 1.0f), t)); 276 277 draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, 278 height * 0.5f); 279 draw_list->AddCircleFilled( 280 ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), 281 radius - 1.5f, IM_COL32(255, 255, 255, 255)); 282} 283 284extern "C" 285void igImageBg(ImTextureID user_texture_id, 286 const ImVec2 size, 287 const ImVec2 uv0, const ImVec2 uv1, 288 const ImVec4 tint_col, const ImVec4 border_col, const ImVec4 bg_col) 289{ 290 ImGuiWindow* window = GetCurrentWindow(); 291 if (window->SkipItems) { 292 return; 293 } 294 295 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 296 auto bbImg = bb; 297 if (border_col.w > 0.0f) { 298 bb.Max += ImVec2(2, 2); 299 300 // Move the image pixel down and right, to be inside of the frame. 301 bbImg.Min += ImVec2(1, 1); 302 bbImg.Max += ImVec2(1, 1); 303 } 304 305 ItemSize(bb); 306 if (!ItemAdd(bb, 0)) { 307 return; 308 } 309 310 // Do we have a border? 311 if (border_col.w > 0.0f) { 312 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); 313 } 314 315 // Should we clear the background? 316 if (bg_col.w > 0.0f) { 317 window->DrawList->AddRectFilled(bbImg.Min, bbImg.Max, GetColorU32(bg_col)); 318 } 319 320 // Finally the image. 321 window->DrawList->AddImage(user_texture_id, bbImg.Min, bbImg.Max, uv0, uv1, GetColorU32(tint_col)); 322}