The open source OpenXR runtime
at main 5885 lines 274 kB view raw
1// MIT License 2 3// Copyright (c) 2023 Evan Pezent 4 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to deal 7// in the Software without restriction, including without limitation the rights 8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9// copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11 12// The above copyright notice and this permission notice shall be included in all 13// copies or substantial portions of the Software. 14 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21// SOFTWARE. 22 23// ImPlot v0.17 24 25/* 26 27API BREAKING CHANGES 28==================== 29Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. 30Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. 31When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. 32You can read releases logs https://github.com/epezent/implot/releases for more details. 33 34- 2023/08/20 (0.17) - ImPlotFlags_NoChild was removed as child windows are no longer needed to capture scroll. You can safely remove this flag if you were using it. 35- 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals. 36- 2022/11/25 (0.15) - Make PlotText honor ImPlotItemFlags_NoFit. 37- 2022/06/19 (0.14) - The signature of ColormapScale has changed to accommodate a new ImPlotColormapScaleFlags parameter 38- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. 39 If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see 40 unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil. 41 - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead 42 - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal 43 - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags 44 - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags 45 - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical` 46 - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal ) 47 - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks 48 - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags 49 - ImPlotFormatters should now return an int indicating the size written 50 - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks 51- 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272 52 - TRIVIAL RENAME: 53 - ImPlotLimits -> ImPlotRect 54 - ImPlotYAxis_ -> ImAxis_ 55 - SetPlotYAxis -> SetAxis 56 - BeginDragDropTarget -> BeginDragDropTargetPlot 57 - BeginDragDropSource -> BeginDragDropSourcePlot 58 - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText 59 - SetNextPlotLimits -> SetNextAxesLimits 60 - SetMouseTextLocation -> SetupMouseText 61 - SIGNATURE MODIFIED: 62 - PixelsToPlot/PlotToPixels -> added optional X-Axis arg 63 - GetPlotMousePos -> added optional X-Axis arg 64 - GetPlotLimits -> added optional X-Axis arg 65 - GetPlotSelection -> added optional X-Axis arg 66 - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead) 67 - REPLACED: 68 - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis) 69 - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis) 70 - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis) 71 - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes) 72 - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes) 73 - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis) 74 - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis) 75 - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit 76 - SetLegendLocation -> SetupLegend 77 - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight 78 - ImPlotOrientation -> ImPlotLegendFlags_Horizontal 79 - Annotate -> Annotation 80 - REMOVED: 81 - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect 82 - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks 83 - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat 84 - AnnotateClamped -> use Annotation(bool clamp = true) 85 - OBSOLETED: 86 - BeginPlot (original signature) -> use simplified signature + Setup API 87- 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead. 88- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. 89 ShowColormapScale was changed to ColormapScale and requires additional arguments. 90- 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. 91- 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. 92- 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved 93 to implot_internal.h due to its immaturity. 94- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding 95- 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. 96- 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) 97- 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset 98 is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). 99- 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. 100- 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. 101- 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. 102- 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. 103- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. 104- 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. 105- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. 106- 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. 107- 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 108- 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` 109 and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate 110 that multiple bars will be plotted. 111- 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. 112- 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` 113- 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: 114 - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. 115 It should be fairly obvious what was what. 116 - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent 117 style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. 118- 2020/05/10 (0.2) - The following function/struct names were changes: 119 - ImPlotRange -> ImPlotLimits 120 - GetPlotRange() -> GetPlotLimits() 121 - SetNextPlotRange -> SetNextPlotLimits 122 - SetNextPlotRangeX -> SetNextPlotLimitsX 123 - SetNextPlotRangeY -> SetNextPlotLimitsY 124- 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. 125 126*/ 127 128#define IMGUI_DEFINE_MATH_OPERATORS 129#include "implot.h" 130#include "implot_internal.h" 131 132#include <stdlib.h> 133 134// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. 135#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) 136#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All 137#endif 138 139// Support for pre-1.89.7 versions. 140#if (IMGUI_VERSION_NUM < 18966) 141#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap 142#endif 143 144// Visual Studio warnings 145#ifdef _MSC_VER 146#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 147#endif 148 149// Clang/GCC warnings with -Weverything 150#if defined(__clang__) 151#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal 152#elif defined(__GNUC__) 153#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked 154#endif 155 156// Global plot context 157#ifndef GImPlot 158ImPlotContext* GImPlot = nullptr; 159#endif 160 161//----------------------------------------------------------------------------- 162// Struct Implementations 163//----------------------------------------------------------------------------- 164 165ImPlotInputMap::ImPlotInputMap() { 166 ImPlot::MapInputDefault(this); 167} 168 169ImPlotStyle::ImPlotStyle() { 170 171 LineWeight = 1; 172 Marker = ImPlotMarker_None; 173 MarkerSize = 4; 174 MarkerWeight = 1; 175 FillAlpha = 1; 176 ErrorBarSize = 5; 177 ErrorBarWeight = 1.5f; 178 DigitalBitHeight = 8; 179 DigitalBitGap = 4; 180 181 PlotBorderSize = 1; 182 MinorAlpha = 0.25f; 183 MajorTickLen = ImVec2(10,10); 184 MinorTickLen = ImVec2(5,5); 185 MajorTickSize = ImVec2(1,1); 186 MinorTickSize = ImVec2(1,1); 187 MajorGridSize = ImVec2(1,1); 188 MinorGridSize = ImVec2(1,1); 189 PlotPadding = ImVec2(10,10); 190 LabelPadding = ImVec2(5,5); 191 LegendPadding = ImVec2(10,10); 192 LegendInnerPadding = ImVec2(5,5); 193 LegendSpacing = ImVec2(5,0); 194 MousePosPadding = ImVec2(10,10); 195 AnnotationPadding = ImVec2(2,2); 196 FitPadding = ImVec2(0,0); 197 PlotDefaultSize = ImVec2(400,300); 198 PlotMinSize = ImVec2(200,150); 199 200 ImPlot::StyleColorsAuto(this); 201 202 Colormap = ImPlotColormap_Deep; 203 204 UseLocalTime = false; 205 Use24HourClock = false; 206 UseISO8601 = false; 207} 208 209//----------------------------------------------------------------------------- 210// Style 211//----------------------------------------------------------------------------- 212 213namespace ImPlot { 214 215const char* GetStyleColorName(ImPlotCol col) { 216 static const char* col_names[ImPlotCol_COUNT] = { 217 "Line", 218 "Fill", 219 "MarkerOutline", 220 "MarkerFill", 221 "ErrorBar", 222 "FrameBg", 223 "PlotBg", 224 "PlotBorder", 225 "LegendBg", 226 "LegendBorder", 227 "LegendText", 228 "TitleText", 229 "InlayText", 230 "AxisText", 231 "AxisGrid", 232 "AxisTick", 233 "AxisBg", 234 "AxisBgHovered", 235 "AxisBgActive", 236 "Selection", 237 "Crosshairs" 238 }; 239 return col_names[col]; 240} 241 242const char* GetMarkerName(ImPlotMarker marker) { 243 switch (marker) { 244 case ImPlotMarker_None: return "None"; 245 case ImPlotMarker_Circle: return "Circle"; 246 case ImPlotMarker_Square: return "Square"; 247 case ImPlotMarker_Diamond: return "Diamond"; 248 case ImPlotMarker_Up: return "Up"; 249 case ImPlotMarker_Down: return "Down"; 250 case ImPlotMarker_Left: return "Left"; 251 case ImPlotMarker_Right: return "Right"; 252 case ImPlotMarker_Cross: return "Cross"; 253 case ImPlotMarker_Plus: return "Plus"; 254 case ImPlotMarker_Asterisk: return "Asterisk"; 255 default: return ""; 256 } 257} 258 259ImVec4 GetAutoColor(ImPlotCol idx) { 260 ImVec4 col(0,0,0,1); 261 switch(idx) { 262 case ImPlotCol_Line: return col; // these are plot dependent! 263 case ImPlotCol_Fill: return col; // these are plot dependent! 264 case ImPlotCol_MarkerOutline: return col; // these are plot dependent! 265 case ImPlotCol_MarkerFill: return col; // these are plot dependent! 266 case ImPlotCol_ErrorBar: return ImGui::GetStyleColorVec4(ImGuiCol_Text); 267 case ImPlotCol_FrameBg: return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg); 268 case ImPlotCol_PlotBg: return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); 269 case ImPlotCol_PlotBorder: return ImGui::GetStyleColorVec4(ImGuiCol_Border); 270 case ImPlotCol_LegendBg: return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg); 271 case ImPlotCol_LegendBorder: return GetStyleColorVec4(ImPlotCol_PlotBorder); 272 case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText); 273 case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); 274 case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); 275 case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); 276 case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f); 277 case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid); 278 case ImPlotCol_AxisBg: return ImVec4(0,0,0,0); 279 case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); 280 case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); 281 case ImPlotCol_Selection: return ImVec4(1,1,0,1); 282 case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder); 283 default: return col; 284 } 285} 286 287struct ImPlotStyleVarInfo { 288 ImGuiDataType Type; 289 ImU32 Count; 290 ImU32 Offset; 291 void* GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); } 292}; 293 294static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = 295{ 296 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight 297 { ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker 298 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize 299 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight 300 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha 301 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize 302 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight 303 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight 304 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap 305 306 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize 307 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha 308 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen 309 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen 310 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize 311 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize 312 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize 313 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize 314 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding 315 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine 316 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding 317 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding 318 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing 319 320 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding 321 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding 322 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding) }, // ImPlotStyleVar_FitPadding 323 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize 324 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize 325}; 326 327static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { 328 IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT); 329 IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT); 330 return &GPlotStyleVarInfo[idx]; 331} 332 333//----------------------------------------------------------------------------- 334// Generic Helpers 335//----------------------------------------------------------------------------- 336 337void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *text_begin, const char* text_end) { 338 // the code below is based loosely on ImFont::RenderText 339 if (!text_end) 340 text_end = text_begin + strlen(text_begin); 341 ImGuiContext& g = *GImGui; 342 ImFont* font = g.Font; 343 // Align to be pixel perfect 344 pos.x = ImFloor(pos.x); 345 pos.y = ImFloor(pos.y); 346 const float scale = g.FontSize / font->FontSize; 347 const char* s = text_begin; 348 int chars_exp = (int)(text_end - s); 349 int chars_rnd = 0; 350 const int vtx_count_max = chars_exp * 4; 351 const int idx_count_max = chars_exp * 6; 352 DrawList->PrimReserve(idx_count_max, vtx_count_max); 353 while (s < text_end) { 354 unsigned int c = (unsigned int)*s; 355 if (c < 0x80) { 356 s += 1; 357 } 358 else { 359 s += ImTextCharFromUtf8(&c, s, text_end); 360 if (c == 0) // Malformed UTF-8? 361 break; 362 } 363 const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c); 364 if (glyph == nullptr) { 365 continue; 366 } 367 DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale, 368 pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale, 369 ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0), 370 ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1), 371 col); 372 pos.y -= glyph->AdvanceX * scale; 373 chars_rnd++; 374 } 375 // Give back unused vertices 376 int chars_skp = chars_exp-chars_rnd; 377 DrawList->PrimUnreserve(chars_skp*6, chars_skp*4); 378} 379 380void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) { 381 float txt_ht = ImGui::GetTextLineHeight(); 382 const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end); 383 ImVec2 text_size; 384 float y = 0; 385 while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) { 386 text_size = ImGui::CalcTextSize(text_begin,tmp,true); 387 DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp); 388 text_begin = tmp + 1; 389 y += txt_ht; 390 } 391 text_size = ImGui::CalcTextSize(text_begin,title_end,true); 392 DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end); 393} 394 395double NiceNum(double x, bool round) { 396 double f; 397 double nf; 398 int expv = (int)floor(ImLog10(x)); 399 f = x / ImPow(10.0, (double)expv); 400 if (round) 401 if (f < 1.5) 402 nf = 1; 403 else if (f < 3) 404 nf = 2; 405 else if (f < 7) 406 nf = 5; 407 else 408 nf = 10; 409 else if (f <= 1) 410 nf = 1; 411 else if (f <= 2) 412 nf = 2; 413 else if (f <= 5) 414 nf = 5; 415 else 416 nf = 10; 417 return nf * ImPow(10.0, expv); 418} 419 420//----------------------------------------------------------------------------- 421// Context Utils 422//----------------------------------------------------------------------------- 423 424void SetImGuiContext(ImGuiContext* ctx) { 425 ImGui::SetCurrentContext(ctx); 426} 427 428ImPlotContext* CreateContext() { 429 ImPlotContext* ctx = IM_NEW(ImPlotContext)(); 430 Initialize(ctx); 431 if (GImPlot == nullptr) 432 SetCurrentContext(ctx); 433 return ctx; 434} 435 436void DestroyContext(ImPlotContext* ctx) { 437 if (ctx == nullptr) 438 ctx = GImPlot; 439 if (GImPlot == ctx) 440 SetCurrentContext(nullptr); 441 IM_DELETE(ctx); 442} 443 444ImPlotContext* GetCurrentContext() { 445 return GImPlot; 446} 447 448void SetCurrentContext(ImPlotContext* ctx) { 449 GImPlot = ctx; 450} 451 452#define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) 453#define IM_RGB(r,g,b) IM_COL32(r,g,b,255) 454 455void Initialize(ImPlotContext* ctx) { 456 ResetCtxForNextPlot(ctx); 457 ResetCtxForNextAlignedPlots(ctx); 458 ResetCtxForNextSubplot(ctx); 459 460 const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; 461 const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; 462 const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; 463 const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; 464 const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; 465 const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; 466 const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; 467 const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; 468 const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; 469 const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; 470 const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)}; 471 const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)}; 472 const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)}; 473 const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)}; 474 const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)}; 475 const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK }; 476 477 IMPLOT_APPEND_CMAP(Deep, true); 478 IMPLOT_APPEND_CMAP(Dark, true); 479 IMPLOT_APPEND_CMAP(Pastel, true); 480 IMPLOT_APPEND_CMAP(Paired, true); 481 IMPLOT_APPEND_CMAP(Viridis, false); 482 IMPLOT_APPEND_CMAP(Plasma, false); 483 IMPLOT_APPEND_CMAP(Hot, false); 484 IMPLOT_APPEND_CMAP(Cool, false); 485 IMPLOT_APPEND_CMAP(Pink, false); 486 IMPLOT_APPEND_CMAP(Jet, false); 487 IMPLOT_APPEND_CMAP(Twilight, false); 488 IMPLOT_APPEND_CMAP(RdBu, false); 489 IMPLOT_APPEND_CMAP(BrBG, false); 490 IMPLOT_APPEND_CMAP(PiYG, false); 491 IMPLOT_APPEND_CMAP(Spectral, false); 492 IMPLOT_APPEND_CMAP(Greys, false); 493} 494 495void ResetCtxForNextPlot(ImPlotContext* ctx) { 496 // reset the next plot/item data 497 ctx->NextPlotData.Reset(); 498 ctx->NextItemData.Reset(); 499 // reset labels 500 ctx->Annotations.Reset(); 501 ctx->Tags.Reset(); 502 // reset extents/fit 503 ctx->OpenContextThisFrame = false; 504 // reset digital plot items count 505 ctx->DigitalPlotItemCnt = 0; 506 ctx->DigitalPlotOffset = 0; 507 // nullify plot 508 ctx->CurrentPlot = nullptr; 509 ctx->CurrentItem = nullptr; 510 ctx->PreviousItem = nullptr; 511} 512 513void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) { 514 ctx->CurrentAlignmentH = nullptr; 515 ctx->CurrentAlignmentV = nullptr; 516} 517 518void ResetCtxForNextSubplot(ImPlotContext* ctx) { 519 ctx->CurrentSubplot = nullptr; 520 ctx->CurrentAlignmentH = nullptr; 521 ctx->CurrentAlignmentV = nullptr; 522} 523 524//----------------------------------------------------------------------------- 525// Plot Utils 526//----------------------------------------------------------------------------- 527 528ImPlotPlot* GetPlot(const char* title) { 529 ImGuiWindow* Window = GImGui->CurrentWindow; 530 const ImGuiID ID = Window->GetID(title); 531 return GImPlot->Plots.GetByKey(ID); 532} 533 534ImPlotPlot* GetCurrentPlot() { 535 return GImPlot->CurrentPlot; 536} 537 538void BustPlotCache() { 539 ImPlotContext& gp = *GImPlot; 540 gp.Plots.Clear(); 541 gp.Subplots.Clear(); 542} 543 544//----------------------------------------------------------------------------- 545// Legend Utils 546//----------------------------------------------------------------------------- 547 548ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) { 549 ImVec2 pos; 550 if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East)) 551 pos.x = outer_rect.Min.x + pad.x; 552 else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East)) 553 pos.x = outer_rect.Max.x - pad.x - inner_size.x; 554 else 555 pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f; 556 // legend reference point y 557 if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South)) 558 pos.y = outer_rect.Min.y + pad.y; 559 else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South)) 560 pos.y = outer_rect.Max.y - pad.y - inner_size.y; 561 else 562 pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f; 563 pos.x = IM_ROUND(pos.x); 564 pos.y = IM_ROUND(pos.y); 565 return pos; 566} 567 568ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) { 569 // vars 570 const int nItems = items.GetLegendCount(); 571 const float txt_ht = ImGui::GetTextLineHeight(); 572 const float icon_size = txt_ht; 573 // get label max width 574 float max_label_width = 0; 575 float sum_label_width = 0; 576 for (int i = 0; i < nItems; ++i) { 577 const char* label = items.GetLegendLabel(i); 578 const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; 579 max_label_width = label_width > max_label_width ? label_width : max_label_width; 580 sum_label_width += label_width; 581 } 582 // calc legend size 583 const ImVec2 legend_size = vertical ? 584 ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) : 585 ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht); 586 return legend_size; 587} 588 589bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) { 590 bool clamped = false; 591 ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad); 592 if (legend_rect.Min.x < outer_rect_pad.Min.x) { 593 legend_rect.Min.x = outer_rect_pad.Min.x; 594 clamped = true; 595 } 596 if (legend_rect.Min.y < outer_rect_pad.Min.y) { 597 legend_rect.Min.y = outer_rect_pad.Min.y; 598 clamped = true; 599 } 600 if (legend_rect.Max.x > outer_rect_pad.Max.x) { 601 legend_rect.Max.x = outer_rect_pad.Max.x; 602 clamped = true; 603 } 604 if (legend_rect.Max.y > outer_rect_pad.Max.y) { 605 legend_rect.Max.y = outer_rect_pad.Max.y; 606 clamped = true; 607 } 608 return clamped; 609} 610 611int LegendSortingComp(const void* _a, const void* _b) { 612 ImPlotItemGroup* items = GImPlot->SortItems; 613 const int a = *(const int*)_a; 614 const int b = *(const int*)_b; 615 const char* label_a = items->GetLegendLabel(a); 616 const char* label_b = items->GetLegendLabel(b); 617 return strcmp(label_a,label_b); 618} 619 620bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) { 621 // vars 622 const float txt_ht = ImGui::GetTextLineHeight(); 623 const float icon_size = txt_ht; 624 const float icon_shrink = 2; 625 ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText); 626 ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); 627 // render each legend item 628 float sum_label_width = 0; 629 bool any_item_hovered = false; 630 631 const int num_items = items.GetLegendCount(); 632 if (num_items < 1) 633 return hovered; 634 // build render order 635 ImPlotContext& gp = *GImPlot; 636 ImVector<int>& indices = gp.TempInt1; 637 indices.resize(num_items); 638 for (int i = 0; i < num_items; ++i) 639 indices[i] = i; 640 if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) { 641 gp.SortItems = &items; 642 qsort(indices.Data, num_items, sizeof(int), LegendSortingComp); 643 } 644 // render 645 for (int i = 0; i < num_items; ++i) { 646 const int idx = indices[i]; 647 ImPlotItem* item = items.GetLegendItem(idx); 648 const char* label = items.GetLegendLabel(idx); 649 const float label_width = ImGui::CalcTextSize(label, nullptr, true).x; 650 const ImVec2 top_left = vertical ? 651 legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : 652 legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); 653 sum_label_width += label_width; 654 ImRect icon_bb; 655 icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink); 656 icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink); 657 ImRect label_bb; 658 label_bb.Min = top_left; 659 label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); 660 ImU32 col_txt_hl; 661 ImU32 col_item = ImAlphaU32(item->Color,1); 662 663 ImRect button_bb(icon_bb.Min, label_bb.Max); 664 665 ImGui::KeepAliveID(item->ID); 666 667 bool item_hov = false; 668 bool item_hld = false; 669 bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons) 670 ? false 671 : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld); 672 673 if (item_clk) 674 item->Show = !item->Show; 675 676 677 const bool can_hover = (item_hov) 678 && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem) 679 || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)); 680 681 if (can_hover) { 682 item->LegendHoverRect.Min = icon_bb.Min; 683 item->LegendHoverRect.Max = label_bb.Max; 684 item->LegendHovered = true; 685 col_txt_hl = ImMixU32(col_txt, col_item, 64); 686 any_item_hovered = true; 687 } 688 else { 689 col_txt_hl = ImGui::GetColorU32(col_txt); 690 } 691 ImU32 col_icon; 692 if (item_hld) 693 col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); 694 else if (item_hov) 695 col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f); 696 else 697 col_icon = item->Show ? col_item : col_txt_dis; 698 699 DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon); 700 const char* text_display_end = ImGui::FindRenderedTextEnd(label, nullptr); 701 if (label != text_display_end) 702 DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end); 703 } 704 return hovered && !any_item_hovered; 705} 706 707//----------------------------------------------------------------------------- 708// Locators 709//----------------------------------------------------------------------------- 710 711static const float TICK_FILL_X = 0.8f; 712static const float TICK_FILL_Y = 1.0f; 713 714void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { 715 if (range.Min == range.Max) 716 return; 717 const int nMinor = 10; 718 const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f))); 719 const double nice_range = NiceNum(range.Size() * 0.99, false); 720 const double interval = NiceNum(nice_range / (nMajor - 1), true); 721 const double graphmin = floor(range.Min / interval) * interval; 722 const double graphmax = ceil(range.Max / interval) * interval; 723 bool first_major_set = false; 724 int first_major_idx = 0; 725 const int idx0 = ticker.TickCount(); // ticker may have user custom ticks 726 ImVec2 total_size(0,0); 727 for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { 728 // is this zero? combat zero formatting issues 729 if (major-interval < 0 && major+interval > 0) 730 major = 0; 731 if (range.Contains(major)) { 732 if (!first_major_set) { 733 first_major_idx = ticker.TickCount(); 734 first_major_set = true; 735 } 736 total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize; 737 } 738 for (int i = 1; i < nMinor; ++i) { 739 double minor = major + i * interval / nMinor; 740 if (range.Contains(minor)) { 741 total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize; 742 } 743 } 744 } 745 // prune if necessary 746 if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) { 747 for (int i = first_major_idx-1; i >= idx0; i -= 2) 748 ticker.Ticks[i].ShowLabel = false; 749 for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2) 750 ticker.Ticks[i].ShowLabel = false; 751 } 752} 753 754bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) { 755 if (range.Min * range.Max > 0) { 756 const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers 757 double log_min = ImLog10(ImAbs(range.Min)); 758 double log_max = ImLog10(ImAbs(range.Max)); 759 double log_a = ImMin(log_min,log_max); 760 double log_b = ImMax(log_min,log_max); 761 exp_step = ImMax(1,(int)(log_b - log_a) / nMajor); 762 exp_min = (int)log_a; 763 exp_max = (int)log_b; 764 if (exp_step != 1) { 765 while(exp_step % 3 != 0) exp_step++; // make step size multiple of three 766 while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 767 } 768 return true; 769 } 770 return false; 771} 772 773void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { 774 const double sign = ImSign(range.Max); 775 for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) { 776 double major1 = sign*ImPow(10, (double)(e)); 777 double major2 = sign*ImPow(10, (double)(e + 1)); 778 double interval = (major2 - major1) / 9; 779 if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) 780 ticker.AddTick(major1, true, 0, true, formatter, data); 781 for (int j = 0; j < exp_step; ++j) { 782 major1 = sign*ImPow(10, (double)(e+j)); 783 major2 = sign*ImPow(10, (double)(e+j+1)); 784 interval = (major2 - major1) / 9; 785 for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { 786 double minor = major1 + i * interval; 787 if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) 788 ticker.AddTick(minor, false, 0, false, formatter, data); 789 } 790 } 791 } 792} 793 794void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { 795 int exp_min, exp_max, exp_step; 796 if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step)) 797 AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data); 798} 799 800float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) { 801 double scaleToPixels = pixels / range.Size(); 802 double scaleMin = TransformForward_SymLog(range.Min,nullptr); 803 double scaleMax = TransformForward_SymLog(range.Max,nullptr); 804 double s = TransformForward_SymLog(plt, nullptr); 805 double t = (s - scaleMin) / (scaleMax - scaleMin); 806 plt = range.Min + range.Size() * t; 807 808 return (float)(0 + scaleToPixels * (plt - range.Min)); 809} 810 811void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { 812 if (range.Min >= -1 && range.Max <= 1) { 813 Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data); 814 } 815 else if (range.Min * range.Max < 0) { // cross zero 816 const float pix_min = 0; 817 const float pix_max = pixels; 818 const float pix_p1 = CalcSymLogPixel(1, range, pixels); 819 const float pix_n1 = CalcSymLogPixel(-1, range, pixels); 820 int exp_min_p, exp_max_p, exp_step_p; 821 int exp_min_n, exp_max_n, exp_step_n; 822 CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p); 823 CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n); 824 int exp_step = ImMax(exp_step_n, exp_step_p); 825 ticker.AddTick(0,true,0,true,formatter,formatter_data); 826 AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data); 827 AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data); 828 } 829 else { 830 Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data); 831 } 832} 833 834void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { 835 for (int i = 0; i < n; ++i) { 836 if (labels != nullptr) 837 ticker.AddTick(values[i], false, 0, true, labels[i]); 838 else 839 ticker.AddTick(values[i], false, 0, true, formatter, data); 840 } 841} 842 843//----------------------------------------------------------------------------- 844// Time Ticks and Utils 845//----------------------------------------------------------------------------- 846 847// this may not be thread safe? 848static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = { 849 0.000001, 850 0.001, 851 1, 852 60, 853 3600, 854 86400, 855 2629800, 856 31557600 857}; 858 859inline ImPlotTimeUnit GetUnitForRange(double range) { 860 static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME}; 861 for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) { 862 if (range <= cutoffs[i]) 863 return (ImPlotTimeUnit)i; 864 } 865 return ImPlotTimeUnit_Yr; 866} 867 868inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) { 869 if (max_divs < divs[0]) 870 return 0; 871 for (int i = 1; i < size; ++i) { 872 if (max_divs < divs[i]) 873 return step[i-1]; 874 } 875 return step[size-1]; 876} 877 878inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) { 879 if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) { 880 static const int step[] = {500,250,200,100,50,25,20,10,5,2,1}; 881 static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000}; 882 return LowerBoundStep(max_divs, divs, step, 11); 883 } 884 if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) { 885 static const int step[] = {30,15,10,5,1}; 886 static const int divs[] = {2,4,6,12,60}; 887 return LowerBoundStep(max_divs, divs, step, 5); 888 } 889 else if (unit == ImPlotTimeUnit_Hr) { 890 static const int step[] = {12,6,3,2,1}; 891 static const int divs[] = {2,4,8,12,24}; 892 return LowerBoundStep(max_divs, divs, step, 5); 893 } 894 else if (unit == ImPlotTimeUnit_Day) { 895 static const int step[] = {14,7,2,1}; 896 static const int divs[] = {2,4,14,28}; 897 return LowerBoundStep(max_divs, divs, step, 4); 898 } 899 else if (unit == ImPlotTimeUnit_Mo) { 900 static const int step[] = {6,3,2,1}; 901 static const int divs[] = {2,4,6,12}; 902 return LowerBoundStep(max_divs, divs, step, 4); 903 } 904 return 0; 905} 906 907ImPlotTime MkGmtTime(struct tm *ptm) { 908 ImPlotTime t; 909#ifdef _WIN32 910 t.S = _mkgmtime(ptm); 911#else 912 t.S = timegm(ptm); 913#endif 914 if (t.S < 0) 915 t.S = 0; 916 return t; 917} 918 919tm* GetGmtTime(const ImPlotTime& t, tm* ptm) 920{ 921#ifdef _WIN32 922 if (gmtime_s(ptm, &t.S) == 0) 923 return ptm; 924 else 925 return nullptr; 926#else 927 return gmtime_r(&t.S, ptm); 928#endif 929} 930 931ImPlotTime MkLocTime(struct tm *ptm) { 932 ImPlotTime t; 933 t.S = mktime(ptm); 934 if (t.S < 0) 935 t.S = 0; 936 return t; 937} 938 939tm* GetLocTime(const ImPlotTime& t, tm* ptm) { 940#ifdef _WIN32 941 if (localtime_s(ptm, &t.S) == 0) 942 return ptm; 943 else 944 return nullptr; 945#else 946 return localtime_r(&t.S, ptm); 947#endif 948} 949 950inline ImPlotTime MkTime(struct tm *ptm) { 951 if (GetStyle().UseLocalTime) 952 return MkLocTime(ptm); 953 else 954 return MkGmtTime(ptm); 955} 956 957inline tm* GetTime(const ImPlotTime& t, tm* ptm) { 958 if (GetStyle().UseLocalTime) 959 return GetLocTime(t,ptm); 960 else 961 return GetGmtTime(t,ptm); 962} 963 964ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) { 965 tm& Tm = GImPlot->Tm; 966 967 int yr = year - 1900; 968 if (yr < 0) 969 yr = 0; 970 971 sec = sec + us / 1000000; 972 us = us % 1000000; 973 974 Tm.tm_sec = sec; 975 Tm.tm_min = min; 976 Tm.tm_hour = hour; 977 Tm.tm_mday = day; 978 Tm.tm_mon = month; 979 Tm.tm_year = yr; 980 981 ImPlotTime t = MkTime(&Tm); 982 983 t.Us = us; 984 return t; 985} 986 987int GetYear(const ImPlotTime& t) { 988 tm& Tm = GImPlot->Tm; 989 GetTime(t, &Tm); 990 return Tm.tm_year + 1900; 991} 992 993ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) { 994 tm& Tm = GImPlot->Tm; 995 ImPlotTime t_out = t; 996 switch(unit) { 997 case ImPlotTimeUnit_Us: t_out.Us += count; break; 998 case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break; 999 case ImPlotTimeUnit_S: t_out.S += count; break; 1000 case ImPlotTimeUnit_Min: t_out.S += count * 60; break; 1001 case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break; 1002 case ImPlotTimeUnit_Day: t_out.S += count * 86400; break; 1003 case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) { 1004 GetTime(t_out, &Tm); 1005 if (count > 0) 1006 t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon); 1007 else if (count < 0) 1008 t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING 1009 } 1010 break; 1011 case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) { 1012 if (count > 0) 1013 t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out))); 1014 else if (count < 0) 1015 t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1)); 1016 // this is incorrect if leap year and we are past Feb 28 1017 } 1018 break; 1019 default: break; 1020 } 1021 t_out.RollOver(); 1022 return t_out; 1023} 1024 1025ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) { 1026 ImPlotContext& gp = *GImPlot; 1027 GetTime(t, &gp.Tm); 1028 switch (unit) { 1029 case ImPlotTimeUnit_S: return ImPlotTime(t.S, 0); 1030 case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000); 1031 case ImPlotTimeUnit_Us: return t; 1032 case ImPlotTimeUnit_Yr: gp.Tm.tm_mon = 0; // fall-through 1033 case ImPlotTimeUnit_Mo: gp.Tm.tm_mday = 1; // fall-through 1034 case ImPlotTimeUnit_Day: gp.Tm.tm_hour = 0; // fall-through 1035 case ImPlotTimeUnit_Hr: gp.Tm.tm_min = 0; // fall-through 1036 case ImPlotTimeUnit_Min: gp.Tm.tm_sec = 0; break; 1037 default: return t; 1038 } 1039 return MkTime(&gp.Tm); 1040} 1041 1042ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit) { 1043 return AddTime(FloorTime(t, unit), unit, 1); 1044} 1045 1046ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) { 1047 ImPlotTime t1 = FloorTime(t, unit); 1048 ImPlotTime t2 = AddTime(t1,unit,1); 1049 if (t1.S == t2.S) 1050 return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2; 1051 return t.S - t1.S < t2.S - t.S ? t1 : t2; 1052} 1053 1054ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) { 1055 ImPlotContext& gp = *GImPlot; 1056 tm& Tm = gp.Tm; 1057 GetTime(date_part, &gp.Tm); 1058 int y = Tm.tm_year; 1059 int m = Tm.tm_mon; 1060 int d = Tm.tm_mday; 1061 GetTime(tod_part, &gp.Tm); 1062 Tm.tm_year = y; 1063 Tm.tm_mon = m; 1064 Tm.tm_mday = d; 1065 ImPlotTime t = MkTime(&Tm); 1066 t.Us = tod_part.Us; 1067 return t; 1068} 1069 1070// TODO: allow users to define these 1071static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; 1072static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; 1073static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; 1074 1075int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, bool use_24_hr_clk) { 1076 tm& Tm = GImPlot->Tm; 1077 GetTime(t, &Tm); 1078 const int us = t.Us % 1000; 1079 const int ms = t.Us / 1000; 1080 const int sec = Tm.tm_sec; 1081 const int min = Tm.tm_min; 1082 if (use_24_hr_clk) { 1083 const int hr = Tm.tm_hour; 1084 switch(fmt) { 1085 case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); 1086 case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); 1087 case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); 1088 case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); 1089 case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); 1090 case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); 1091 case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec); 1092 case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min); 1093 case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr); 1094 default: return 0; 1095 } 1096 } 1097 else { 1098 const char* ap = Tm.tm_hour < 12 ? "am" : "pm"; 1099 const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12; 1100 switch(fmt) { 1101 case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); 1102 case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); 1103 case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); 1104 case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); 1105 case ImPlotTimeFmt_MinSMs: return ImFormatString(buffer, size, ":%02d:%02d.%03d", min, sec, ms); 1106 case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); 1107 case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); 1108 case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap); 1109 case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap); 1110 default: return 0; 1111 } 1112 } 1113} 1114 1115int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601) { 1116 tm& Tm = GImPlot->Tm; 1117 GetTime(t, &Tm); 1118 const int day = Tm.tm_mday; 1119 const int mon = Tm.tm_mon + 1; 1120 const int year = Tm.tm_year + 1900; 1121 const int yr = year % 100; 1122 if (use_iso_8601) { 1123 switch (fmt) { 1124 case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day); 1125 case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day); 1126 case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon); 1127 case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon); 1128 case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); 1129 default: return 0; 1130 } 1131 } 1132 else { 1133 switch (fmt) { 1134 case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day); 1135 case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr); 1136 case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); 1137 case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); 1138 case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); 1139 default: return 0; 1140 } 1141 } 1142 } 1143 1144int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) { 1145 int written = 0; 1146 if (fmt.Date != ImPlotDateFmt_None) 1147 written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601); 1148 if (fmt.Time != ImPlotTimeFmt_None) { 1149 if (fmt.Date != ImPlotDateFmt_None) 1150 buffer[written++] = ' '; 1151 written += FormatTime(t, &buffer[written], size - written, fmt.Time, fmt.Use24HourClock); 1152 } 1153 return written; 1154} 1155 1156inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) { 1157 static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width 1158 char buffer[32]; 1159 FormatDateTime(t_max_width, buffer, 32, fmt); 1160 return ImGui::CalcTextSize(buffer).x; 1161} 1162 1163inline bool TimeLabelSame(const char* l1, const char* l2) { 1164 size_t len1 = strlen(l1); 1165 size_t len2 = strlen(l2); 1166 size_t n = len1 < len2 ? len1 : len2; 1167 return strcmp(l1 + len1 - n, l2 + len2 - n) == 0; 1168} 1169 1170static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { 1171 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), 1172 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), 1173 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S), 1174 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), 1175 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), 1176 ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), 1177 ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), 1178 ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) 1179}; 1180 1181static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { 1182 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), 1183 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), 1184 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), 1185 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), 1186 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), 1187 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), 1188 ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), 1189 ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) 1190}; 1191 1192static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { 1193 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), 1194 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), 1195 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), 1196 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), 1197 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), 1198 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), 1199 ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), 1200 ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) 1201}; 1202 1203static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { 1204 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), 1205 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), 1206 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), 1207 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), 1208 ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), 1209 ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), 1210 ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), 1211 ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) 1212}; 1213 1214inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) { 1215 ImPlotStyle& style = GetStyle(); 1216 ImPlotDateTimeSpec fmt = ctx[idx]; 1217 fmt.UseISO8601 = style.UseISO8601; 1218 fmt.Use24HourClock = style.Use24HourClock; 1219 return fmt; 1220} 1221 1222void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { 1223 IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!"); 1224 (void)vertical; 1225 // get units for level 0 and level 1 labels 1226 const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top) 1227 const ImPlotTimeUnit unit1 = ImClamp(unit0 + 1, 0, ImPlotTimeUnit_COUNT-1); // level = 1 (bottom) 1228 // get time format specs 1229 const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); 1230 const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); 1231 const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); 1232 // min max times 1233 const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min); 1234 const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max); 1235 // maximum allowable density of labels 1236 const float max_density = 0.5f; 1237 // book keeping 1238 int last_major_offset = -1; 1239 // formatter data 1240 Formatter_Time_Data ftd; 1241 ftd.UserFormatter = formatter; 1242 ftd.UserFormatterData = formatter_data; 1243 if (unit0 != ImPlotTimeUnit_Yr) { 1244 // pixels per major (level 1) division 1245 const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]); 1246 // nominal pixels taken up by labels 1247 const float fmt0_width = GetDateTimeWidth(fmt0); 1248 const float fmt1_width = GetDateTimeWidth(fmt1); 1249 const float fmtf_width = GetDateTimeWidth(fmtf); 1250 // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions 1251 const int minor_per_major = (int)(max_density * pix_per_major_div / fmt0_width); 1252 // the minor step size (level 0) 1253 const int step = GetTimeStep(minor_per_major, unit0); 1254 // generate ticks 1255 ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1); 1256 while (t1 < t_max) { 1257 // get next major 1258 const ImPlotTime t2 = AddTime(t1, unit1, 1); 1259 // add major tick 1260 if (t1 >= t_min && t1 <= t_max) { 1261 // minor level 0 tick 1262 ftd.Time = t1; ftd.Spec = fmt0; 1263 ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd); 1264 // major level 1 tick 1265 ftd.Time = t1; ftd.Spec = last_major_offset < 0 ? fmtf : fmt1; 1266 ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd); 1267 const char* this_major = ticker.GetText(tick_maj); 1268 if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major)) 1269 tick_maj.ShowLabel = false; 1270 last_major_offset = tick_maj.TextOffset; 1271 } 1272 // add minor ticks up until next major 1273 if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { 1274 ImPlotTime t12 = AddTime(t1, unit0, step); 1275 while (t12 < t2) { 1276 float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels; 1277 if (t12 >= t_min && t12 <= t_max) { 1278 ftd.Time = t12; ftd.Spec = fmt0; 1279 ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd); 1280 if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { 1281 ftd.Time = t12; ftd.Spec = fmtf; 1282 ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd); 1283 last_major_offset = tick_maj.TextOffset; 1284 } 1285 } 1286 t12 = AddTime(t12, unit0, step); 1287 } 1288 } 1289 t1 = t2; 1290 } 1291 } 1292 else { 1293 const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); 1294 const float label_width = GetDateTimeWidth(fmty); 1295 const int max_labels = (int)(max_density * pixels / label_width); 1296 const int year_min = GetYear(t_min); 1297 const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr)); 1298 const double nice_range = NiceNum((year_max - year_min)*0.99,false); 1299 const double interval = NiceNum(nice_range / (max_labels - 1), true); 1300 const int graphmin = (int)(floor(year_min / interval) * interval); 1301 const int graphmax = (int)(ceil(year_max / interval) * interval); 1302 const int step = (int)interval <= 0 ? 1 : (int)interval; 1303 1304 for (int y = graphmin; y < graphmax; y += step) { 1305 ImPlotTime t = MakeTime(y); 1306 if (t >= t_min && t <= t_max) { 1307 ftd.Time = t; ftd.Spec = fmty; 1308 ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd); 1309 } 1310 } 1311 } 1312} 1313 1314//----------------------------------------------------------------------------- 1315// Context Menu 1316//----------------------------------------------------------------------------- 1317 1318template <typename F> 1319bool DragFloat(const char*, F*, float, F, F) { 1320 return false; 1321} 1322 1323template <> 1324bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) { 1325 return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3g", 1); 1326} 1327 1328template <> 1329bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) { 1330 return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3g", 1); 1331} 1332 1333inline void BeginDisabledControls(bool cond) { 1334 if (cond) { 1335 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 1336 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); 1337 } 1338} 1339 1340inline void EndDisabledControls(bool cond) { 1341 if (cond) { 1342 ImGui::PopItemFlag(); 1343 ImGui::PopStyleVar(); 1344 } 1345} 1346 1347void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) { 1348 1349 ImGui::PushItemWidth(75); 1350 bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); 1351 bool label = axis.HasLabel(); 1352 bool grid = axis.HasGridLines(); 1353 bool ticks = axis.HasTickMarks(); 1354 bool labels = axis.HasTickLabels(); 1355 double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. 1356 1357 if (axis.Scale == ImPlotScale_Time) { 1358 ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); 1359 ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); 1360 1361 BeginDisabledControls(always_locked); 1362 ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); 1363 EndDisabledControls(always_locked); 1364 ImGui::SameLine(); 1365 BeginDisabledControls(axis.IsLockedMin() || always_locked); 1366 if (ImGui::BeginMenu("Min Time")) { 1367 if (ShowTimePicker("mintime", &tmin)) { 1368 if (tmin >= tmax) 1369 tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); 1370 axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); 1371 } 1372 ImGui::Separator(); 1373 if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { 1374 tmin = CombineDateTime(axis.PickerTimeMin, tmin); 1375 if (tmin >= tmax) 1376 tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); 1377 axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); 1378 } 1379 ImGui::EndMenu(); 1380 } 1381 EndDisabledControls(axis.IsLockedMin() || always_locked); 1382 1383 BeginDisabledControls(always_locked); 1384 ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); 1385 EndDisabledControls(always_locked); 1386 ImGui::SameLine(); 1387 BeginDisabledControls(axis.IsLockedMax() || always_locked); 1388 if (ImGui::BeginMenu("Max Time")) { 1389 if (ShowTimePicker("maxtime", &tmax)) { 1390 if (tmax <= tmin) 1391 tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); 1392 axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); 1393 } 1394 ImGui::Separator(); 1395 if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { 1396 tmax = CombineDateTime(axis.PickerTimeMax, tmax); 1397 if (tmax <= tmin) 1398 tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); 1399 axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); 1400 } 1401 ImGui::EndMenu(); 1402 } 1403 EndDisabledControls(axis.IsLockedMax() || always_locked); 1404 } 1405 else { 1406 BeginDisabledControls(always_locked); 1407 ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); 1408 EndDisabledControls(always_locked); 1409 ImGui::SameLine(); 1410 BeginDisabledControls(axis.IsLockedMin() || always_locked); 1411 double temp_min = axis.Range.Min; 1412 if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { 1413 axis.SetMin(temp_min,true); 1414 if (equal_axis != nullptr) 1415 equal_axis->SetAspect(axis.GetAspect()); 1416 } 1417 EndDisabledControls(axis.IsLockedMin() || always_locked); 1418 1419 BeginDisabledControls(always_locked); 1420 ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); 1421 EndDisabledControls(always_locked); 1422 ImGui::SameLine(); 1423 BeginDisabledControls(axis.IsLockedMax() || always_locked); 1424 double temp_max = axis.Range.Max; 1425 if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { 1426 axis.SetMax(temp_max,true); 1427 if (equal_axis != nullptr) 1428 equal_axis->SetAspect(axis.GetAspect()); 1429 } 1430 EndDisabledControls(axis.IsLockedMax() || always_locked); 1431 } 1432 1433 ImGui::Separator(); 1434 1435 ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); 1436 // TODO 1437 // BeginDisabledControls(axis.IsTime() && time_allowed); 1438 // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); 1439 // EndDisabledControls(axis.IsTime() && time_allowed); 1440 // if (time_allowed) { 1441 // BeginDisabledControls(axis.IsLog() || axis.IsSymLog()); 1442 // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); 1443 // EndDisabledControls(axis.IsLog() || axis.IsSymLog()); 1444 // } 1445 ImGui::Separator(); 1446 ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); 1447 ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); 1448 ImGui::Separator(); 1449 BeginDisabledControls(axis.LabelOffset == -1); 1450 if (ImGui::Checkbox("Label", &label)) 1451 ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); 1452 EndDisabledControls(axis.LabelOffset == -1); 1453 if (ImGui::Checkbox("Grid Lines", &grid)) 1454 ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); 1455 if (ImGui::Checkbox("Tick Marks", &ticks)) 1456 ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); 1457 if (ImGui::Checkbox("Tick Labels", &labels)) 1458 ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); 1459 1460} 1461 1462bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) { 1463 const float s = ImGui::GetFrameHeight(); 1464 bool ret = false; 1465 if (ImGui::Checkbox("Show",&visible)) 1466 ret = true; 1467 if (legend.CanGoInside) 1468 ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside); 1469 if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) 1470 legend.Flags |= ImPlotLegendFlags_Horizontal; 1471 ImGui::SameLine(); 1472 if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) 1473 legend.Flags &= ~ImPlotLegendFlags_Horizontal; 1474 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2)); 1475 if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine(); 1476 if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine(); 1477 if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; } 1478 if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine(); 1479 if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine(); 1480 if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; } 1481 if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine(); 1482 if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine(); 1483 if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; } 1484 ImGui::PopStyleVar(); 1485 return ret; 1486} 1487 1488void ShowSubplotsContextMenu(ImPlotSubplot& subplot) { 1489 if ((ImGui::BeginMenu("Linking"))) { 1490 if (ImGui::MenuItem("Link Rows",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows))) 1491 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); 1492 if (ImGui::MenuItem("Link Cols",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols))) 1493 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); 1494 if (ImGui::MenuItem("Link All X",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX))) 1495 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); 1496 if (ImGui::MenuItem("Link All Y",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY))) 1497 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); 1498 ImGui::EndMenu(); 1499 } 1500 if ((ImGui::BeginMenu("Settings"))) { 1501 BeginDisabledControls(!subplot.HasTitle); 1502 if (ImGui::MenuItem("Title",nullptr,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))) 1503 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle); 1504 EndDisabledControls(!subplot.HasTitle); 1505 if (ImGui::MenuItem("Resizable",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize))) 1506 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize); 1507 if (ImGui::MenuItem("Align",nullptr,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign))) 1508 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign); 1509 if (ImGui::MenuItem("Share Items",nullptr,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems))) 1510 ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); 1511 ImGui::EndMenu(); 1512 } 1513} 1514 1515void ShowPlotContextMenu(ImPlotPlot& plot) { 1516 ImPlotContext& gp = *GImPlot; 1517 const bool owns_legend = gp.CurrentItems == &plot.Items; 1518 const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); 1519 1520 char buf[16] = {}; 1521 1522 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 1523 ImPlotAxis& x_axis = plot.XAxis(i); 1524 if (!x_axis.Enabled || !x_axis.HasMenus()) 1525 continue; 1526 ImGui::PushID(i); 1527 ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1); 1528 if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) { 1529 ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : nullptr, false); 1530 ImGui::EndMenu(); 1531 } 1532 ImGui::PopID(); 1533 } 1534 1535 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 1536 ImPlotAxis& y_axis = plot.YAxis(i); 1537 if (!y_axis.Enabled || !y_axis.HasMenus()) 1538 continue; 1539 ImGui::PushID(i); 1540 ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); 1541 if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) { 1542 ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : nullptr, false); 1543 ImGui::EndMenu(); 1544 } 1545 ImGui::PopID(); 1546 } 1547 1548 ImGui::Separator(); 1549 if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) { 1550 if ((ImGui::BeginMenu("Legend"))) { 1551 if (owns_legend) { 1552 if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) 1553 ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); 1554 } 1555 else if (gp.CurrentSubplot != nullptr) { 1556 if (ShowLegendContextMenu(gp.CurrentSubplot->Items.Legend, !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend))) 1557 ImFlipFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend); 1558 } 1559 ImGui::EndMenu(); 1560 } 1561 } 1562 if ((ImGui::BeginMenu("Settings"))) { 1563 if (ImGui::MenuItem("Equal", nullptr, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) 1564 ImFlipFlag(plot.Flags, ImPlotFlags_Equal); 1565 if (ImGui::MenuItem("Box Select",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) 1566 ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); 1567 BeginDisabledControls(plot.TitleOffset == -1); 1568 if (ImGui::MenuItem("Title",nullptr,plot.HasTitle())) 1569 ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); 1570 EndDisabledControls(plot.TitleOffset == -1); 1571 if (ImGui::MenuItem("Mouse Position",nullptr,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText))) 1572 ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText); 1573 if (ImGui::MenuItem("Crosshairs",nullptr,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) 1574 ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); 1575 ImGui::EndMenu(); 1576 } 1577 if (gp.CurrentSubplot != nullptr && !ImHasFlag(gp.CurrentSubplot->Flags, ImPlotSubplotFlags_NoMenus)) { 1578 ImGui::Separator(); 1579 if ((ImGui::BeginMenu("Subplots"))) { 1580 ShowSubplotsContextMenu(*gp.CurrentSubplot); 1581 ImGui::EndMenu(); 1582 } 1583 } 1584} 1585 1586//----------------------------------------------------------------------------- 1587// Axis Utils 1588//----------------------------------------------------------------------------- 1589 1590static inline int AxisPrecision(const ImPlotAxis& axis) { 1591 const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size(); 1592 return Precision(range); 1593} 1594 1595static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { 1596 return RoundTo(value, AxisPrecision(axis)); 1597} 1598 1599void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { 1600 ImPlotContext& gp = *GImPlot; 1601 // TODO: We shouldn't explicitly check that the axis is Time here. Ideally, 1602 // Formatter_Time would handle the formatting for us, but the code below 1603 // needs additional arguments which are not currently available in ImPlotFormatter 1604 if (axis.Locator == Locator_Time) { 1605 ImPlotTimeUnit unit = axis.Vertical 1606 ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! 1607 : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! 1608 FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); 1609 } 1610 else { 1611 if (round) 1612 value = RoundAxisValue(axis, value); 1613 axis.Formatter(value, buff, size, axis.FormatterData); 1614 } 1615} 1616 1617void UpdateAxisColors(ImPlotAxis& axis) { 1618 const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid); 1619 axis.ColorMaj = ImGui::GetColorU32(col_grid); 1620 axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); 1621 axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick); 1622 axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText); 1623 axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg); 1624 axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered); 1625 axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive); 1626 // axis.ColorHiLi = IM_COL32_BLACK_TRANS; 1627} 1628 1629void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) { 1630 1631 ImPlotContext& gp = *GImPlot; 1632 1633 const float T = ImGui::GetTextLineHeight(); 1634 const float P = gp.Style.LabelPadding.y; 1635 const float K = gp.Style.MinorTickLen.x; 1636 1637 int count_T = 0; 1638 int count_B = 0; 1639 float last_T = plot.AxesRect.Min.y; 1640 float last_B = plot.AxesRect.Max.y; 1641 1642 for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward 1643 ImPlotAxis& axis = plot.XAxis(i); 1644 if (!axis.Enabled) 1645 continue; 1646 const bool label = axis.HasLabel(); 1647 const bool ticks = axis.HasTickLabels(); 1648 const bool opp = axis.IsOpposite(); 1649 const bool time = axis.Scale == ImPlotScale_Time; 1650 if (opp) { 1651 if (count_T++ > 0) 1652 pad_T += K + P; 1653 if (label) 1654 pad_T += T + P; 1655 if (ticks) 1656 pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); 1657 axis.Datum1 = plot.CanvasRect.Min.y + pad_T; 1658 axis.Datum2 = last_T; 1659 last_T = axis.Datum1; 1660 } 1661 else { 1662 if (count_B++ > 0) 1663 pad_B += K + P; 1664 if (label) 1665 pad_B += T + P; 1666 if (ticks) 1667 pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); 1668 axis.Datum1 = plot.CanvasRect.Max.y - pad_B; 1669 axis.Datum2 = last_B; 1670 last_B = axis.Datum1; 1671 } 1672 } 1673 1674 if (align) { 1675 count_T = count_B = 0; 1676 float delta_T, delta_B; 1677 align->Update(pad_T,pad_B,delta_T,delta_B); 1678 for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { 1679 ImPlotAxis& axis = plot.XAxis(i); 1680 if (!axis.Enabled) 1681 continue; 1682 if (axis.IsOpposite()) { 1683 axis.Datum1 += delta_T; 1684 axis.Datum2 += count_T++ > 1 ? delta_T : 0; 1685 } 1686 else { 1687 axis.Datum1 -= delta_B; 1688 axis.Datum2 -= count_B++ > 1 ? delta_B : 0; 1689 } 1690 } 1691 } 1692} 1693 1694void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) { 1695 1696 // [ pad_L ] [ pad_R ] 1697 // .................CanvasRect................ 1698 // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT: 1699 // :A # |- A # |- -| # A -| # A: 1700 // :X | X | | X | x: 1701 // :I # |- I # |- -| # I -| # I: 1702 // :S | S | | S | S: 1703 // :3 # |- 0 # |-_______________-| # 1 -| # 2: 1704 // :.........................................: 1705 // 1706 // T = text height 1707 // P = label padding 1708 // K = minor tick length 1709 // W = label width 1710 1711 ImPlotContext& gp = *GImPlot; 1712 1713 const float T = ImGui::GetTextLineHeight(); 1714 const float P = gp.Style.LabelPadding.x; 1715 const float K = gp.Style.MinorTickLen.y; 1716 1717 int count_L = 0; 1718 int count_R = 0; 1719 float last_L = plot.AxesRect.Min.x; 1720 float last_R = plot.AxesRect.Max.x; 1721 1722 for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward 1723 ImPlotAxis& axis = plot.YAxis(i); 1724 if (!axis.Enabled) 1725 continue; 1726 const bool label = axis.HasLabel(); 1727 const bool ticks = axis.HasTickLabels(); 1728 const bool opp = axis.IsOpposite(); 1729 if (opp) { 1730 if (count_R++ > 0) 1731 pad_R += K + P; 1732 if (label) 1733 pad_R += T + P; 1734 if (ticks) 1735 pad_R += axis.Ticker.MaxSize.x + P; 1736 axis.Datum1 = plot.CanvasRect.Max.x - pad_R; 1737 axis.Datum2 = last_R; 1738 last_R = axis.Datum1; 1739 } 1740 else { 1741 if (count_L++ > 0) 1742 pad_L += K + P; 1743 if (label) 1744 pad_L += T + P; 1745 if (ticks) 1746 pad_L += axis.Ticker.MaxSize.x + P; 1747 axis.Datum1 = plot.CanvasRect.Min.x + pad_L; 1748 axis.Datum2 = last_L; 1749 last_L = axis.Datum1; 1750 } 1751 } 1752 1753 plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L; 1754 plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R; 1755 1756 if (align) { 1757 count_L = count_R = 0; 1758 float delta_L, delta_R; 1759 align->Update(pad_L,pad_R,delta_L,delta_R); 1760 for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { 1761 ImPlotAxis& axis = plot.YAxis(i); 1762 if (!axis.Enabled) 1763 continue; 1764 if (axis.IsOpposite()) { 1765 axis.Datum1 -= delta_R; 1766 axis.Datum2 -= count_R++ > 1 ? delta_R : 0; 1767 } 1768 else { 1769 axis.Datum1 += delta_L; 1770 axis.Datum2 += count_L++ > 1 ? delta_L : 0; 1771 } 1772 } 1773 } 1774} 1775 1776//----------------------------------------------------------------------------- 1777// RENDERING 1778//----------------------------------------------------------------------------- 1779 1780static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { 1781 const float density = ticker.TickCount() / rect.GetWidth(); 1782 ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); 1783 col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); 1784 col_min = ImGui::ColorConvertFloat4ToU32(col_min4); 1785 for (int t = 0; t < ticker.TickCount(); t++) { 1786 const ImPlotTick& xt = ticker.Ticks[t]; 1787 if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) 1788 continue; 1789 if (xt.Level == 0) { 1790 if (xt.Major) 1791 DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj); 1792 else if (density < 0.2f) 1793 DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_min, size_min); 1794 } 1795 } 1796} 1797 1798static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { 1799 const float density = ticker.TickCount() / rect.GetHeight(); 1800 ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); 1801 col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); 1802 col_min = ImGui::ColorConvertFloat4ToU32(col_min4); 1803 for (int t = 0; t < ticker.TickCount(); t++) { 1804 const ImPlotTick& yt = ticker.Ticks[t]; 1805 if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) 1806 continue; 1807 if (yt.Major) 1808 DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj); 1809 else if (density < 0.2f) 1810 DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_min, size_min); 1811 } 1812} 1813 1814static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min, const ImVec2& p_max, const ImVec4& col) { 1815 const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f)); 1816 const ImU32 col_bd = ImGui::GetColorU32(col); 1817 DrawList.AddRectFilled(p_min, p_max, col_bg); 1818 DrawList.AddRect(p_min, p_max, col_bd); 1819} 1820 1821//----------------------------------------------------------------------------- 1822// Input Handling 1823//----------------------------------------------------------------------------- 1824 1825static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f; 1826static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f; 1827 1828bool UpdateInput(ImPlotPlot& plot) { 1829 1830 bool changed = false; 1831 1832 ImPlotContext& gp = *GImPlot; 1833 ImGuiIO& IO = ImGui::GetIO(); 1834 1835 // BUTTON STATE ----------------------------------------------------------- 1836 1837 const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowOverlap 1838 | ImGuiButtonFlags_PressedOnClick 1839 | ImGuiButtonFlags_PressedOnDoubleClick 1840 | ImGuiButtonFlags_MouseButtonLeft 1841 | ImGuiButtonFlags_MouseButtonRight 1842 | ImGuiButtonFlags_MouseButtonMiddle; 1843 const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren 1844 | plot_button_flags; 1845 1846 const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); 1847#if (IMGUI_VERSION_NUM < 18966) 1848 ImGui::SetItemAllowOverlap(); // Handled by ButtonBehavior() 1849#endif 1850 1851 if (plot_clicked) { 1852 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { 1853 plot.Selecting = true; 1854 plot.SelectStart = IO.MousePos; 1855 plot.SelectRect = ImRect(0,0,0,0); 1856 } 1857 if (IO.MouseDoubleClicked[gp.InputMap.Fit]) { 1858 plot.FitThisFrame = true; 1859 for (int i = 0; i < ImAxis_COUNT; ++i) 1860 plot.Axes[i].FitThisFrame = true; 1861 } 1862 } 1863 1864 const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod); 1865 1866 plot.Held = plot.Held && can_pan; 1867 1868 bool x_click[IMPLOT_NUM_X_AXES] = {false}; 1869 bool x_held[IMPLOT_NUM_X_AXES] = {false}; 1870 bool x_hov[IMPLOT_NUM_X_AXES] = {false}; 1871 1872 bool y_click[IMPLOT_NUM_Y_AXES] = {false}; 1873 bool y_held[IMPLOT_NUM_Y_AXES] = {false}; 1874 bool y_hov[IMPLOT_NUM_Y_AXES] = {false}; 1875 1876 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 1877 ImPlotAxis& xax = plot.XAxis(i); 1878 if (xax.Enabled) { 1879 ImGui::KeepAliveID(xax.ID); 1880 x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags); 1881 if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) 1882 plot.FitThisFrame = xax.FitThisFrame = true; 1883 xax.Held = xax.Held && can_pan; 1884 x_hov[i] = xax.Hovered || plot.Hovered; 1885 x_held[i] = xax.Held || plot.Held; 1886 } 1887 } 1888 1889 for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { 1890 ImPlotAxis& yax = plot.YAxis(i); 1891 if (yax.Enabled) { 1892 ImGui::KeepAliveID(yax.ID); 1893 y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags); 1894 if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) 1895 plot.FitThisFrame = yax.FitThisFrame = true; 1896 yax.Held = yax.Held && can_pan; 1897 y_hov[i] = yax.Hovered || plot.Hovered; 1898 y_held[i] = yax.Held || plot.Held; 1899 } 1900 } 1901 1902 // cancel due to DND activity 1903 if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0)) 1904 return false; 1905 1906 // STATE ------------------------------------------------------------------- 1907 1908 const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); 1909 1910 const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); 1911 const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); 1912 const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); 1913 const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); 1914 const bool any_hov = any_x_hov || any_y_hov; 1915 const bool any_held = any_x_held || any_y_held; 1916 1917 const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select); 1918 const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan); 1919 const float select_drag_sq = ImLengthSqr(select_drag); 1920 const float pan_drag_sq = ImLengthSqr(pan_drag); 1921 const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; 1922 const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; 1923 1924 // CONTEXT MENU ----------------------------------------------------------- 1925 1926 if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked) 1927 gp.OpenContextThisFrame = true; 1928 1929 if (selecting || panning) 1930 plot.ContextLocked = true; 1931 else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu])) 1932 plot.ContextLocked = false; 1933 1934 // DRAG INPUT ------------------------------------------------------------- 1935 1936 if (any_held && !plot.Selecting) { 1937 int drag_direction = 0; 1938 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 1939 ImPlotAxis& x_axis = plot.XAxis(i); 1940 if (x_held[i] && !x_axis.IsInputLocked()) { 1941 drag_direction |= (1 << 1); 1942 bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0; 1943 if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { 1944 const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); 1945 const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); 1946 x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); 1947 x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); 1948 if (axis_equal && x_axis.OrthoAxis != nullptr) 1949 x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); 1950 changed = true; 1951 } 1952 } 1953 } 1954 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 1955 ImPlotAxis& y_axis = plot.YAxis(i); 1956 if (y_held[i] && !y_axis.IsInputLocked()) { 1957 drag_direction |= (1 << 2); 1958 bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0; 1959 if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { 1960 const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); 1961 const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); 1962 y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); 1963 y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); 1964 if (axis_equal && y_axis.OrthoAxis != nullptr) 1965 y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); 1966 changed = true; 1967 } 1968 } 1969 } 1970 if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { 1971 switch (drag_direction) { 1972 case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break; 1973 case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break; 1974 case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break; 1975 default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break; 1976 } 1977 } 1978 } 1979 1980 // SCROLL INPUT ----------------------------------------------------------- 1981 1982 if (any_hov && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { 1983 1984 float zoom_rate = gp.InputMap.ZoomRate; 1985 if (IO.MouseWheel == 0.0f) 1986 zoom_rate = 0; 1987 else if (IO.MouseWheel > 0) 1988 zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); 1989 ImVec2 rect_size = plot.PlotRect.GetSize(); 1990 float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); 1991 float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); 1992 1993 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 1994 ImPlotAxis& x_axis = plot.XAxis(i); 1995 const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr; 1996 const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); 1997 if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { 1998 ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID); 1999 if (zoom_rate != 0.0f) { 2000 float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; 2001 const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); 2002 const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); 2003 x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); 2004 x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); 2005 if (axis_equal && x_axis.OrthoAxis != nullptr) 2006 x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); 2007 changed = true; 2008 } 2009 } 2010 } 2011 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2012 ImPlotAxis& y_axis = plot.YAxis(i); 2013 const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr; 2014 const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); 2015 if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { 2016 ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID); 2017 if (zoom_rate != 0.0f) { 2018 float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; 2019 const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); 2020 const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); 2021 y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); 2022 y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); 2023 if (axis_equal && y_axis.OrthoAxis != nullptr) 2024 y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); 2025 changed = true; 2026 } 2027 } 2028 } 2029 } 2030 2031 // BOX-SELECTION ---------------------------------------------------------- 2032 2033 if (plot.Selecting) { 2034 const ImVec2 d = plot.SelectStart - IO.MousePos; 2035 const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2; 2036 const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2; 2037 // confirm 2038 if (IO.MouseReleased[gp.InputMap.Select]) { 2039 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2040 ImPlotAxis& x_axis = plot.XAxis(i); 2041 if (!x_axis.IsInputLocked() && x_can_change) { 2042 const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); 2043 const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); 2044 x_axis.SetMin(ImMin(p1, p2)); 2045 x_axis.SetMax(ImMax(p1, p2)); 2046 changed = true; 2047 } 2048 } 2049 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2050 ImPlotAxis& y_axis = plot.YAxis(i); 2051 if (!y_axis.IsInputLocked() && y_can_change) { 2052 const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); 2053 const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); 2054 y_axis.SetMin(ImMin(p1, p2)); 2055 y_axis.SetMax(ImMax(p1, p2)); 2056 changed = true; 2057 } 2058 } 2059 if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod))) 2060 gp.OpenContextThisFrame = false; 2061 plot.Selected = plot.Selecting = false; 2062 } 2063 // cancel 2064 else if (IO.MouseReleased[gp.InputMap.SelectCancel]) { 2065 plot.Selected = plot.Selecting = false; 2066 gp.OpenContextThisFrame = false; 2067 } 2068 else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) { 2069 // bad selection 2070 if (plot.IsInputLocked()) { 2071 ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); 2072 gp.OpenContextThisFrame = false; 2073 plot.Selected = false; 2074 } 2075 else { 2076 // TODO: Handle only min or max locked cases 2077 const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); 2078 const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); 2079 plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); 2080 plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); 2081 plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); 2082 plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); 2083 plot.SelectRect.Min -= plot.PlotRect.Min; 2084 plot.SelectRect.Max -= plot.PlotRect.Min; 2085 plot.Selected = true; 2086 } 2087 } 2088 else { 2089 plot.Selected = false; 2090 } 2091 } 2092 return changed; 2093} 2094 2095//----------------------------------------------------------------------------- 2096// Next Plot Data (Legacy) 2097//----------------------------------------------------------------------------- 2098 2099void ApplyNextPlotData(ImAxis idx) { 2100 ImPlotContext& gp = *GImPlot; 2101 ImPlotPlot& plot = *gp.CurrentPlot; 2102 ImPlotAxis& axis = plot.Axes[idx]; 2103 if (!axis.Enabled) 2104 return; 2105 double* npd_lmin = gp.NextPlotData.LinkedMin[idx]; 2106 double* npd_lmax = gp.NextPlotData.LinkedMax[idx]; 2107 bool npd_rngh = gp.NextPlotData.HasRange[idx]; 2108 ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx]; 2109 ImPlotRange npd_rngv = gp.NextPlotData.Range[idx]; 2110 axis.LinkedMin = npd_lmin; 2111 axis.LinkedMax = npd_lmax; 2112 axis.PullLinks(); 2113 if (npd_rngh) { 2114 if (!plot.Initialized || npd_rngc == ImPlotCond_Always) 2115 axis.SetRange(npd_rngv); 2116 } 2117 axis.HasRange = npd_rngh; 2118 axis.RangeCond = npd_rngc; 2119} 2120 2121//----------------------------------------------------------------------------- 2122// Setup 2123//----------------------------------------------------------------------------- 2124 2125void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { 2126 ImPlotContext& gp = *GImPlot; 2127 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2128 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2129 // get plot and axis 2130 ImPlotPlot& plot = *gp.CurrentPlot; 2131 ImPlotAxis& axis = plot.Axes[idx]; 2132 // set ID 2133 axis.ID = plot.ID + idx + 1; 2134 // check and set flags 2135 if (plot.JustCreated || flags != axis.PreviousFlags) 2136 axis.Flags = flags; 2137 axis.PreviousFlags = flags; 2138 // enable axis 2139 axis.Enabled = true; 2140 // set label 2141 plot.SetAxisLabel(axis,label); 2142 // cache colors 2143 UpdateAxisColors(axis); 2144} 2145 2146void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) { 2147 ImPlotContext& gp = *GImPlot; 2148 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2149 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis 2150 ImPlotPlot& plot = *gp.CurrentPlot; 2151 ImPlotAxis& axis = plot.Axes[idx]; 2152 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2153 if (!plot.Initialized || cond == ImPlotCond_Always) 2154 axis.SetRange(min_lim, max_lim); 2155 axis.HasRange = true; 2156 axis.RangeCond = cond; 2157} 2158 2159void SetupAxisFormat(ImAxis idx, const char* fmt) { 2160 ImPlotContext& gp = *GImPlot; 2161 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2162 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2163 ImPlotPlot& plot = *gp.CurrentPlot; 2164 ImPlotAxis& axis = plot.Axes[idx]; 2165 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2166 axis.HasFormatSpec = fmt != nullptr; 2167 if (fmt != nullptr) 2168 ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec)); 2169} 2170 2171void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) { 2172 ImPlotContext& gp = *GImPlot; 2173 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2174 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2175 ImPlotPlot& plot = *gp.CurrentPlot; 2176 ImPlotAxis& axis = plot.Axes[idx]; 2177 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2178 axis.LinkedMin = min_lnk; 2179 axis.LinkedMax = max_lnk; 2180 axis.PullLinks(); 2181} 2182 2183void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) { 2184 ImPlotContext& gp = *GImPlot; 2185 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2186 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2187 ImPlotPlot& plot = *gp.CurrentPlot; 2188 ImPlotAxis& axis = plot.Axes[idx]; 2189 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2190 axis.Formatter = formatter; 2191 axis.FormatterData = data; 2192} 2193 2194void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) { 2195 ImPlotContext& gp = *GImPlot; 2196 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2197 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2198 ImPlotPlot& plot = *gp.CurrentPlot; 2199 ImPlotAxis& axis = plot.Axes[idx]; 2200 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2201 axis.ShowDefaultTicks = show_default; 2202 AddTicksCustom(values, 2203 labels, 2204 n_ticks, 2205 axis.Ticker, 2206 axis.Formatter ? axis.Formatter : Formatter_Default, 2207 (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); 2208} 2209 2210void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) { 2211 ImPlotContext& gp = *GImPlot; 2212 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2213 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2214 n_ticks = n_ticks < 2 ? 2 : n_ticks; 2215 FillRange(gp.TempDouble1, n_ticks, v_min, v_max); 2216 SetupAxisTicks(idx, gp.TempDouble1.Data, n_ticks, labels, show_default); 2217} 2218 2219void SetupAxisScale(ImAxis idx, ImPlotScale scale) { 2220 ImPlotContext& gp = *GImPlot; 2221 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2222 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2223 ImPlotPlot& plot = *gp.CurrentPlot; 2224 ImPlotAxis& axis = plot.Axes[idx]; 2225 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2226 axis.Scale = scale; 2227 switch (scale) 2228 { 2229 case ImPlotScale_Time: 2230 axis.TransformForward = nullptr; 2231 axis.TransformInverse = nullptr; 2232 axis.TransformData = nullptr; 2233 axis.Locator = Locator_Time; 2234 axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME); 2235 axis.Ticker.Levels = 2; 2236 break; 2237 case ImPlotScale_Log10: 2238 axis.TransformForward = TransformForward_Log10; 2239 axis.TransformInverse = TransformInverse_Log10; 2240 axis.TransformData = nullptr; 2241 axis.Locator = Locator_Log10; 2242 axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY); 2243 break; 2244 case ImPlotScale_SymLog: 2245 axis.TransformForward = TransformForward_SymLog; 2246 axis.TransformInverse = TransformInverse_SymLog; 2247 axis.TransformData = nullptr; 2248 axis.Locator = Locator_SymLog; 2249 axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); 2250 break; 2251 default: 2252 axis.TransformForward = nullptr; 2253 axis.TransformInverse = nullptr; 2254 axis.TransformData = nullptr; 2255 axis.Locator = nullptr; 2256 axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); 2257 break; 2258 } 2259} 2260 2261void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) { 2262 ImPlotContext& gp = *GImPlot; 2263 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2264 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2265 ImPlotPlot& plot = *gp.CurrentPlot; 2266 ImPlotAxis& axis = plot.Axes[idx]; 2267 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2268 axis.Scale = IMPLOT_AUTO; 2269 axis.TransformForward = fwd; 2270 axis.TransformInverse = inv; 2271 axis.TransformData = data; 2272} 2273 2274void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) { 2275 ImPlotContext& gp = *GImPlot; 2276 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2277 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2278 ImPlotPlot& plot = *gp.CurrentPlot; 2279 ImPlotAxis& axis = plot.Axes[idx]; 2280 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2281 axis.ConstraintRange.Min = v_min; 2282 axis.ConstraintRange.Max = v_max; 2283} 2284 2285void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) { 2286 ImPlotContext& gp = *GImPlot; 2287 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2288 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2289 ImPlotPlot& plot = *gp.CurrentPlot; 2290 ImPlotAxis& axis = plot.Axes[idx]; 2291 IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 2292 axis.ConstraintZoom.Min = z_min; 2293 axis.ConstraintZoom.Max = z_max; 2294} 2295 2296void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { 2297 SetupAxis(ImAxis_X1, x_label, x_flags); 2298 SetupAxis(ImAxis_Y1, y_label, y_flags); 2299} 2300 2301void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { 2302 SetupAxisLimits(ImAxis_X1, x_min, x_max, cond); 2303 SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond); 2304} 2305 2306void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { 2307 ImPlotContext& gp = *GImPlot; 2308 IM_ASSERT_USER_ERROR((gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked) || (gp.CurrentSubplot != nullptr && gp.CurrentPlot == nullptr), 2309 "Setup needs to be called after BeginPlot or BeginSubplots and before any setup locking functions (e.g. PlotX)!"); 2310 if (gp.CurrentItems) { 2311 ImPlotLegend& legend = gp.CurrentItems->Legend; 2312 // check and set location 2313 if (location != legend.PreviousLocation) 2314 legend.Location = location; 2315 legend.PreviousLocation = location; 2316 // check and set flags 2317 if (flags != legend.PreviousFlags) 2318 legend.Flags = flags; 2319 legend.PreviousFlags = flags; 2320 } 2321} 2322 2323void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { 2324 ImPlotContext& gp = *GImPlot; 2325 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, 2326 "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); 2327 gp.CurrentPlot->MouseTextLocation = location; 2328 gp.CurrentPlot->MouseTextFlags = flags; 2329} 2330 2331//----------------------------------------------------------------------------- 2332// SetNext 2333//----------------------------------------------------------------------------- 2334 2335void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) { 2336 ImPlotContext& gp = *GImPlot; 2337 IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLimits() needs to be called before BeginPlot()!"); 2338 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 2339 gp.NextPlotData.HasRange[axis] = true; 2340 gp.NextPlotData.RangeCond[axis] = cond; 2341 gp.NextPlotData.Range[axis].Min = v_min; 2342 gp.NextPlotData.Range[axis].Max = v_max; 2343} 2344 2345void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) { 2346 ImPlotContext& gp = *GImPlot; 2347 IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisLinks() needs to be called before BeginPlot()!"); 2348 gp.NextPlotData.LinkedMin[axis] = link_min; 2349 gp.NextPlotData.LinkedMax[axis] = link_max; 2350} 2351 2352void SetNextAxisToFit(ImAxis axis) { 2353 ImPlotContext& gp = *GImPlot; 2354 IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "SetNextAxisToFit() needs to be called before BeginPlot()!"); 2355 gp.NextPlotData.Fit[axis] = true; 2356} 2357 2358void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { 2359 SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond); 2360 SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond); 2361} 2362 2363void SetNextAxesToFit() { 2364 for (int i = 0; i < ImAxis_COUNT; ++i) 2365 SetNextAxisToFit(i); 2366} 2367 2368//----------------------------------------------------------------------------- 2369// BeginPlot 2370//----------------------------------------------------------------------------- 2371 2372bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { 2373 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 2374 ImPlotContext& gp = *GImPlot; 2375 IM_ASSERT_USER_ERROR(gp.CurrentPlot == nullptr, "Mismatched BeginPlot()/EndPlot()!"); 2376 2377 // FRONT MATTER ----------------------------------------------------------- 2378 2379 if (gp.CurrentSubplot != nullptr) 2380 ImGui::PushID(gp.CurrentSubplot->CurrentIdx); 2381 2382 // get globals 2383 ImGuiContext &G = *GImGui; 2384 ImGuiWindow* Window = G.CurrentWindow; 2385 2386 // skip if needed 2387 if (Window->SkipItems && !gp.CurrentSubplot) { 2388 ResetCtxForNextPlot(GImPlot); 2389 return false; 2390 } 2391 2392 // ID and age (TODO: keep track of plot age in frames) 2393 const ImGuiID ID = Window->GetID(title_id); 2394 const bool just_created = gp.Plots.GetByKey(ID) == nullptr; 2395 gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); 2396 2397 ImPlotPlot &plot = *gp.CurrentPlot; 2398 plot.ID = ID; 2399 plot.Items.ID = ID - 1; 2400 plot.JustCreated = just_created; 2401 plot.SetupLocked = false; 2402 2403 // check flags 2404 if (plot.JustCreated) 2405 plot.Flags = flags; 2406 else if (flags != plot.PreviousFlags) 2407 plot.Flags = flags; 2408 plot.PreviousFlags = flags; 2409 2410 // setup default axes 2411 if (plot.JustCreated) { 2412 SetupAxis(ImAxis_X1); 2413 SetupAxis(ImAxis_Y1); 2414 } 2415 2416 // reset axes 2417 for (int i = 0; i < ImAxis_COUNT; ++i) { 2418 plot.Axes[i].Reset(); 2419 UpdateAxisColors(plot.Axes[i]); 2420 } 2421 // ensure first axes enabled 2422 plot.Axes[ImAxis_X1].Enabled = true; 2423 plot.Axes[ImAxis_Y1].Enabled = true; 2424 // set initial axes 2425 plot.CurrentX = ImAxis_X1; 2426 plot.CurrentY = ImAxis_Y1; 2427 2428 // process next plot data (legacy) 2429 for (int i = 0; i < ImAxis_COUNT; ++i) 2430 ApplyNextPlotData(i); 2431 2432 // clear text buffers 2433 plot.ClearTextBuffer(); 2434 plot.SetTitle(title_id); 2435 2436 // set frame size 2437 ImVec2 frame_size; 2438 if (gp.CurrentSubplot != nullptr) 2439 frame_size = gp.CurrentSubplot->CellSize; 2440 else 2441 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); 2442 2443 if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != nullptr)) 2444 frame_size.x = gp.Style.PlotMinSize.x; 2445 if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != nullptr)) 2446 frame_size.y = gp.Style.PlotMinSize.y; 2447 2448 plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); 2449 ImGui::ItemSize(plot.FrameRect); 2450 if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) { 2451 ResetCtxForNextPlot(GImPlot); 2452 return false; 2453 } 2454 2455 // setup items (or dont) 2456 if (gp.CurrentItems == nullptr) 2457 gp.CurrentItems = &plot.Items; 2458 2459 return true; 2460} 2461 2462//----------------------------------------------------------------------------- 2463// SetupFinish 2464//----------------------------------------------------------------------------- 2465 2466void SetupFinish() { 2467 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 2468 ImPlotContext& gp = *GImPlot; 2469 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetupFinish needs to be called after BeginPlot!"); 2470 2471 ImGuiContext& G = *GImGui; 2472 ImDrawList& DrawList = *G.CurrentWindow->DrawList; 2473 const ImGuiStyle& Style = G.Style; 2474 2475 ImPlotPlot &plot = *gp.CurrentPlot; 2476 2477 // lock setup 2478 plot.SetupLocked = true; 2479 2480 // finalize axes and set default formatter/locator 2481 for (int i = 0; i < ImAxis_COUNT; ++i) { 2482 ImPlotAxis& axis = plot.Axes[i]; 2483 if (axis.Enabled) { 2484 axis.Constrain(); 2485 if (!plot.Initialized && axis.CanInitFit()) 2486 plot.FitThisFrame = axis.FitThisFrame = true; 2487 } 2488 if (axis.Formatter == nullptr) { 2489 axis.Formatter = Formatter_Default; 2490 if (axis.HasFormatSpec) 2491 axis.FormatterData = axis.FormatSpec; 2492 else 2493 axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT; 2494 } 2495 if (axis.Locator == nullptr) { 2496 axis.Locator = Locator_Default; 2497 } 2498 } 2499 2500 // setup nullptr orthogonal axes 2501 const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); 2502 for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) { 2503 ImPlotAxis& x_axis = plot.Axes[ix]; 2504 ImPlotAxis& y_axis = plot.Axes[iy]; 2505 if (x_axis.Enabled && y_axis.Enabled) { 2506 if (x_axis.OrthoAxis == nullptr) 2507 x_axis.OrthoAxis = &y_axis; 2508 if (y_axis.OrthoAxis == nullptr) 2509 y_axis.OrthoAxis = &x_axis; 2510 } 2511 else if (x_axis.Enabled) 2512 { 2513 if (x_axis.OrthoAxis == nullptr && !axis_equal) 2514 x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1]; 2515 } 2516 else if (y_axis.Enabled) { 2517 if (y_axis.OrthoAxis == nullptr && !axis_equal) 2518 y_axis.OrthoAxis = &plot.Axes[ImAxis_X1]; 2519 } 2520 } 2521 2522 // canvas/axes bb 2523 plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); 2524 plot.AxesRect = plot.FrameRect; 2525 2526 // outside legend adjustments 2527 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) { 2528 ImPlotLegend& legend = plot.Items.Legend; 2529 const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); 2530 const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); 2531 const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); 2532 const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); 2533 const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); 2534 const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); 2535 if ((west && !horz) || (west && horz && !north && !south)) { 2536 plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); 2537 plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); 2538 } 2539 if ((east && !horz) || (east && horz && !north && !south)) { 2540 plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); 2541 plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); 2542 } 2543 if ((north && horz) || (north && !horz && !west && !east)) { 2544 plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); 2545 plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); 2546 } 2547 if ((south && horz) || (south && !horz && !west && !east)) { 2548 plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); 2549 plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); 2550 } 2551 } 2552 2553 // plot bb 2554 float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0; 2555 2556 // (0) calc top padding form title 2557 ImVec2 title_size(0.0f, 0.0f); 2558 if (plot.HasTitle()) 2559 title_size = ImGui::CalcTextSize(plot.GetTitle(), nullptr, true); 2560 if (title_size.x > 0) { 2561 pad_top += title_size.y + gp.Style.LabelPadding.y; 2562 plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top; 2563 } 2564 2565 // (1) calc addition top padding and bot padding 2566 PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); 2567 2568 const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; 2569 2570 // (2) get y tick labels (needed for left/right pad) 2571 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2572 ImPlotAxis& axis = plot.YAxis(i); 2573 if (axis.WillRender() && axis.ShowDefaultTicks && plot_height > 0) { 2574 axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); 2575 } 2576 } 2577 2578 // (3) calc left/right pad 2579 PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV); 2580 2581 const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; 2582 2583 // (4) get x ticks 2584 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2585 ImPlotAxis& axis = plot.XAxis(i); 2586 if (axis.WillRender() && axis.ShowDefaultTicks && plot_width > 0) { 2587 axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); 2588 } 2589 } 2590 2591 // (5) calc plot bb 2592 plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); 2593 2594 // HOVER------------------------------------------------------------ 2595 2596 // axes hover rect, pixel ranges 2597 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 2598 ImPlotAxis& xax = plot.XAxis(i); 2599 xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)), 2600 ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2))); 2601 xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x; 2602 xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x; 2603 xax.UpdateTransformCache(); 2604 } 2605 2606 for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { 2607 ImPlotAxis& yax = plot.YAxis(i); 2608 yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y), 2609 ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y)); 2610 yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y; 2611 yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y; 2612 yax.UpdateTransformCache(); 2613 } 2614 // Equal axis constraint. Must happen after we set Pixels 2615 // constrain equal axes for primary x and y if not approximately equal 2616 // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case 2617 if (axis_equal) { 2618 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 2619 ImPlotAxis& x_axis = plot.XAxis(i); 2620 if (x_axis.OrthoAxis == nullptr) 2621 continue; 2622 double xar = x_axis.GetAspect(); 2623 double yar = x_axis.OrthoAxis->GetAspect(); 2624 // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range 2625 // NB: because of feedback across several frames, the user's x request may not be perfectly honored 2626 if (x_axis.HasRange) 2627 x_axis.OrthoAxis->SetAspect(xar); 2628 else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked()) 2629 x_axis.SetAspect(yar); 2630 } 2631 } 2632 2633 // INPUT ------------------------------------------------------------------ 2634 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs)) 2635 UpdateInput(plot); 2636 2637 // fit from FitNextPlotAxes or auto fit 2638 for (int i = 0; i < ImAxis_COUNT; ++i) { 2639 if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) { 2640 plot.FitThisFrame = true; 2641 plot.Axes[i].FitThisFrame = true; 2642 } 2643 } 2644 2645 // RENDER ----------------------------------------------------------------- 2646 2647 const float txt_height = ImGui::GetTextLineHeight(); 2648 2649 // render frame 2650 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame)) 2651 ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); 2652 2653 // grid bg 2654 DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); 2655 2656 // transform ticks 2657 for (int i = 0; i < ImAxis_COUNT; i++) { 2658 ImPlotAxis& axis = plot.Axes[i]; 2659 if (axis.WillRender()) { 2660 for (int t = 0; t < axis.Ticker.TickCount(); t++) { 2661 ImPlotTick& tk = axis.Ticker.Ticks[t]; 2662 tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); 2663 } 2664 } 2665 } 2666 2667 // render grid (background) 2668 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2669 ImPlotAxis& x_axis = plot.XAxis(i); 2670 if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) 2671 RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); 2672 } 2673 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2674 ImPlotAxis& y_axis = plot.YAxis(i); 2675 if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) 2676 RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); 2677 } 2678 2679 // render x axis button, label, tick labels 2680 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2681 ImPlotAxis& ax = plot.XAxis(i); 2682 if (!ax.Enabled) 2683 continue; 2684 if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) 2685 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); 2686 else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { 2687 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); 2688 ax.ColorHiLi = IM_COL32_BLACK_TRANS; 2689 } 2690 else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { 2691 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); 2692 } 2693 const ImPlotTicker& tkr = ax.Ticker; 2694 const bool opp = ax.IsOpposite(); 2695 if (ax.HasLabel()) { 2696 const char* label = plot.GetAxisLabel(ax); 2697 const ImVec2 label_size = ImGui::CalcTextSize(label); 2698 const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) 2699 + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y) 2700 + gp.Style.LabelPadding.y; 2701 const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, 2702 opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); 2703 DrawList.AddText(label_pos, ax.ColorTxt, label); 2704 } 2705 if (ax.HasTickLabels()) { 2706 for (int j = 0; j < tkr.TickCount(); ++j) { 2707 const ImPlotTick& tk = tkr.Ticks[j]; 2708 const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) 2709 : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); 2710 if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { 2711 ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); 2712 DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); 2713 } 2714 } 2715 } 2716 } 2717 2718 // render y axis button, label, tick labels 2719 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2720 ImPlotAxis& ax = plot.YAxis(i); 2721 if (!ax.Enabled) 2722 continue; 2723 if ((ax.Hovered || ax.Held) && !plot.Held && !ImHasFlag(ax.Flags, ImPlotAxisFlags_NoHighlight)) 2724 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); 2725 else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { 2726 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); 2727 ax.ColorHiLi = IM_COL32_BLACK_TRANS; 2728 } 2729 else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { 2730 DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); 2731 } 2732 const ImPlotTicker& tkr = ax.Ticker; 2733 const bool opp = ax.IsOpposite(); 2734 if (ax.HasLabel()) { 2735 const char* label = plot.GetAxisLabel(ax); 2736 const ImVec2 label_size = CalcTextSizeVertical(label); 2737 const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) 2738 + gp.Style.LabelPadding.x; 2739 const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, 2740 plot.PlotRect.GetCenter().y + label_size.y * 0.5f); 2741 AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); 2742 } 2743 if (ax.HasTickLabels()) { 2744 for (int j = 0; j < tkr.TickCount(); ++j) { 2745 const ImPlotTick& tk = tkr.Ticks[j]; 2746 const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); 2747 if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { 2748 ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); 2749 DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); 2750 } 2751 } 2752 } 2753 } 2754 2755 2756 // clear legend (TODO: put elsewhere) 2757 plot.Items.Legend.Reset(); 2758 // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!) 2759 ImGui::PushOverrideID(gp.CurrentItems->ID); 2760} 2761 2762//----------------------------------------------------------------------------- 2763// EndPlot() 2764//----------------------------------------------------------------------------- 2765 2766void EndPlot() { 2767 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 2768 ImPlotContext& gp = *GImPlot; 2769 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Mismatched BeginPlot()/EndPlot()!"); 2770 2771 SetupLock(); 2772 2773 ImGuiContext &G = *GImGui; 2774 ImPlotPlot &plot = *gp.CurrentPlot; 2775 ImGuiWindow * Window = G.CurrentWindow; 2776 ImDrawList & DrawList = *Window->DrawList; 2777 const ImGuiIO & IO = ImGui::GetIO(); 2778 2779 // FINAL RENDER ----------------------------------------------------------- 2780 2781 const bool render_border = gp.Style.PlotBorderSize > 0 && GetStyleColorVec4(ImPlotCol_PlotBorder).w > 0; 2782 const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); 2783 const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); 2784 2785 ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); 2786 2787 // render grid (foreground) 2788 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2789 ImPlotAxis& x_axis = plot.XAxis(i); 2790 if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) 2791 RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); 2792 } 2793 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2794 ImPlotAxis& y_axis = plot.YAxis(i); 2795 if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) 2796 RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); 2797 } 2798 2799 2800 // render title 2801 if (plot.HasTitle()) { 2802 ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); 2803 AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle()); 2804 } 2805 2806 // render x ticks 2807 int count_B = 0, count_T = 0; 2808 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 2809 const ImPlotAxis& ax = plot.XAxis(i); 2810 if (!ax.Enabled) 2811 continue; 2812 const ImPlotTicker& tkr = ax.Ticker; 2813 const bool opp = ax.IsOpposite(); 2814 const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); 2815 if (ax.HasTickMarks()) { 2816 const float direction = opp ? 1.0f : -1.0f; 2817 for (int j = 0; j < tkr.TickCount(); ++j) { 2818 const ImPlotTick& tk = tkr.Ticks[j]; 2819 if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) 2820 continue; 2821 const ImVec2 start(tk.PixelPos, ax.Datum1); 2822 const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x; 2823 const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x; 2824 DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk); 2825 } 2826 if (aux || !render_border) 2827 DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x); 2828 } 2829 count_B += !opp; 2830 count_T += opp; 2831 } 2832 2833 // render y ticks 2834 int count_L = 0, count_R = 0; 2835 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 2836 const ImPlotAxis& ax = plot.YAxis(i); 2837 if (!ax.Enabled) 2838 continue; 2839 const ImPlotTicker& tkr = ax.Ticker; 2840 const bool opp = ax.IsOpposite(); 2841 const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); 2842 if (ax.HasTickMarks()) { 2843 const float direction = opp ? -1.0f : 1.0f; 2844 for (int j = 0; j < tkr.TickCount(); ++j) { 2845 const ImPlotTick& tk = tkr.Ticks[j]; 2846 if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) 2847 continue; 2848 const ImVec2 start(ax.Datum1, tk.PixelPos); 2849 const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; 2850 const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; 2851 DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk); 2852 } 2853 if (aux || !render_border) 2854 DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y); 2855 } 2856 count_L += !opp; 2857 count_R += opp; 2858 } 2859 ImGui::PopClipRect(); 2860 2861 // render annotations 2862 PushPlotClipRect(); 2863 for (int i = 0; i < gp.Annotations.Size; ++i) { 2864 const char* txt = gp.Annotations.GetText(i); 2865 ImPlotAnnotation& an = gp.Annotations.Annotations[i]; 2866 const ImVec2 txt_size = ImGui::CalcTextSize(txt); 2867 const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2; 2868 ImVec2 pos = an.Pos; 2869 if (an.Offset.x == 0) 2870 pos.x -= size.x / 2; 2871 else if (an.Offset.x > 0) 2872 pos.x += an.Offset.x; 2873 else 2874 pos.x -= size.x - an.Offset.x; 2875 if (an.Offset.y == 0) 2876 pos.y -= size.y / 2; 2877 else if (an.Offset.y > 0) 2878 pos.y += an.Offset.y; 2879 else 2880 pos.y -= size.y - an.Offset.y; 2881 if (an.Clamp) 2882 pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max); 2883 ImRect rect(pos,pos+size); 2884 if (an.Offset.x != 0 || an.Offset.y != 0) { 2885 ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; 2886 int min_corner = 0; 2887 float min_len = FLT_MAX; 2888 for (int c = 0; c < 4; ++c) { 2889 float len = ImLengthSqr(an.Pos - corners[c]); 2890 if (len < min_len) { 2891 min_corner = c; 2892 min_len = len; 2893 } 2894 } 2895 DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg); 2896 } 2897 DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); 2898 DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); 2899 } 2900 2901 // render selection 2902 if (plot.Selected) 2903 RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection)); 2904 2905 // render crosshairs 2906 if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) { 2907 ImGui::SetMouseCursor(ImGuiMouseCursor_None); 2908 ImVec2 xy = IO.MousePos; 2909 ImVec2 h1(plot.PlotRect.Min.x, xy.y); 2910 ImVec2 h2(xy.x - 5, xy.y); 2911 ImVec2 h3(xy.x + 5, xy.y); 2912 ImVec2 h4(plot.PlotRect.Max.x, xy.y); 2913 ImVec2 v1(xy.x, plot.PlotRect.Min.y); 2914 ImVec2 v2(xy.x, xy.y - 5); 2915 ImVec2 v3(xy.x, xy.y + 5); 2916 ImVec2 v4(xy.x, plot.PlotRect.Max.y); 2917 ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs); 2918 DrawList.AddLine(h1, h2, col); 2919 DrawList.AddLine(h3, h4, col); 2920 DrawList.AddLine(v1, v2, col); 2921 DrawList.AddLine(v3, v4, col); 2922 } 2923 2924 // render mouse pos 2925 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) { 2926 2927 const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes); 2928 const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat); 2929 2930 ImGuiTextBuffer& builder = gp.MousePosStringBuilder; 2931 builder.Buf.shrink(0); 2932 char buff[IMPLOT_LABEL_MAX_SIZE]; 2933 2934 const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES; 2935 for (int i = 0; i < num_x; ++i) { 2936 ImPlotAxis& x_axis = plot.XAxis(i); 2937 if (!x_axis.Enabled) 2938 continue; 2939 if (i > 0) 2940 builder.append(", ("); 2941 double v = x_axis.PixelsToPlot(IO.MousePos.x); 2942 if (no_fmt) 2943 Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); 2944 else 2945 LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); 2946 builder.append(buff); 2947 if (i > 0) 2948 builder.append(")"); 2949 } 2950 builder.append(", "); 2951 const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES; 2952 for (int i = 0; i < num_y; ++i) { 2953 ImPlotAxis& y_axis = plot.YAxis(i); 2954 if (!y_axis.Enabled) 2955 continue; 2956 if (i > 0) 2957 builder.append(", ("); 2958 double v = y_axis.PixelsToPlot(IO.MousePos.y); 2959 if (no_fmt) 2960 Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); 2961 else 2962 LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); 2963 builder.append(buff); 2964 if (i > 0) 2965 builder.append(")"); 2966 } 2967 2968 if (!builder.empty()) { 2969 const ImVec2 size = ImGui::CalcTextSize(builder.c_str()); 2970 const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding); 2971 DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str()); 2972 } 2973 } 2974 PopPlotClipRect(); 2975 2976 // axis side switch 2977 if (!plot.Held) { 2978 ImVec2 mouse_pos = ImGui::GetIO().MousePos; 2979 ImRect trigger_rect = plot.PlotRect; 2980 trigger_rect.Expand(-10); 2981 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 2982 ImPlotAxis& x_axis = plot.XAxis(i); 2983 if (ImHasFlag(x_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) 2984 continue; 2985 if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) { 2986 const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite); 2987 if (!opp) { 2988 ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, 2989 plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5); 2990 if (mouse_pos.y < plot.PlotRect.Max.y - 10) 2991 DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); 2992 if (rect.Contains(mouse_pos)) 2993 x_axis.Flags |= ImPlotAxisFlags_Opposite; 2994 } 2995 else { 2996 ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5, 2997 plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); 2998 if (mouse_pos.y > plot.PlotRect.Min.y + 10) 2999 DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); 3000 if (rect.Contains(mouse_pos)) 3001 x_axis.Flags &= ~ImPlotAxisFlags_Opposite; 3002 } 3003 } 3004 } 3005 for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { 3006 ImPlotAxis& y_axis = plot.YAxis(i); 3007 if (ImHasFlag(y_axis.Flags, ImPlotAxisFlags_NoSideSwitch)) 3008 continue; 3009 if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) { 3010 const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite); 3011 if (!opp) { 3012 ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5, 3013 plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); 3014 if (mouse_pos.x > plot.PlotRect.Min.x + 10) 3015 DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); 3016 if (rect.Contains(mouse_pos)) 3017 y_axis.Flags |= ImPlotAxisFlags_Opposite; 3018 } 3019 else { 3020 ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, 3021 plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5); 3022 if (mouse_pos.x < plot.PlotRect.Max.x - 10) 3023 DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); 3024 if (rect.Contains(mouse_pos)) 3025 y_axis.Flags &= ~ImPlotAxisFlags_Opposite; 3026 } 3027 } 3028 } 3029 } 3030 3031 // reset legend hovers 3032 plot.Items.Legend.Hovered = false; 3033 for (int i = 0; i < plot.Items.GetItemCount(); ++i) 3034 plot.Items.GetItemByIndex(i)->LegendHovered = false; 3035 // render legend 3036 if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) { 3037 ImPlotLegend& legend = plot.Items.Legend; 3038 const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside); 3039 const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); 3040 const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); 3041 const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect, 3042 legend_size, 3043 legend.Location, 3044 legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); 3045 legend.Rect = ImRect(legend_pos, legend_pos + legend_size); 3046 legend.RectClamped = legend.Rect; 3047 const bool legend_scrollable = ClampLegendRect(legend.RectClamped, 3048 legend_out ? plot.FrameRect : plot.PlotRect, 3049 legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding 3050 ); 3051 const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap 3052 | ImGuiButtonFlags_PressedOnClick 3053 | ImGuiButtonFlags_PressedOnDoubleClick 3054 | ImGuiButtonFlags_MouseButtonLeft 3055 | ImGuiButtonFlags_MouseButtonRight 3056 | ImGuiButtonFlags_MouseButtonMiddle 3057 | ImGuiButtonFlags_FlattenChildren; 3058 ImGui::KeepAliveID(plot.Items.ID); 3059 ImGui::ButtonBehavior(legend.RectClamped, plot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags); 3060 legend.Hovered = legend.Hovered || (ImGui::IsWindowHovered() && legend.RectClamped.Contains(IO.MousePos)); 3061 3062 if (legend_scrollable) { 3063 if (legend.Hovered) { 3064 ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.Items.ID); 3065 if (IO.MouseWheel != 0.0f) { 3066 ImVec2 max_step = legend.Rect.GetSize() * 0.67f; 3067 float font_size = ImGui::GetCurrentWindow()->CalcFontSize(); 3068 float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x)); 3069 legend.Scroll.x += scroll_step * IO.MouseWheel; 3070 legend.Scroll.y += scroll_step * IO.MouseWheel; 3071 } 3072 } 3073 const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize(); 3074 legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f); 3075 legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f); 3076 const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y); 3077 ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset; 3078 legend.Rect.Min += legend_offset; 3079 legend.Rect.Max += legend_offset; 3080 } else { 3081 legend.Scroll = ImVec2(0,0); 3082 } 3083 3084 const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); 3085 const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); 3086 ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true); 3087 DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg); 3088 bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) 3089 && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); 3090 DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd); 3091 ImGui::PopClipRect(); 3092 3093 // main ctx menu 3094 if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) 3095 ImGui::OpenPopup("##LegendContext"); 3096 3097 if (ImGui::BeginPopup("##LegendContext")) { 3098 ImGui::Text("Legend"); ImGui::Separator(); 3099 if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) 3100 ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); 3101 ImGui::EndPopup(); 3102 } 3103 } 3104 else { 3105 plot.Items.Legend.Rect = ImRect(); 3106 } 3107 3108 // render border 3109 if (render_border) 3110 DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize); 3111 3112 // render tags 3113 for (int i = 0; i < gp.Tags.Size; ++i) { 3114 ImPlotTag& tag = gp.Tags.Tags[i]; 3115 ImPlotAxis& axis = plot.Axes[tag.Axis]; 3116 if (!axis.Enabled || !axis.Range.Contains(tag.Value)) 3117 continue; 3118 const char* txt = gp.Tags.GetText(i); 3119 ImVec2 text_size = ImGui::CalcTextSize(txt); 3120 ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; 3121 ImVec2 pos; 3122 axis.Ticker.OverrideSizeLate(size); 3123 float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); 3124 if (axis.Vertical) { 3125 if (axis.IsOpposite()) { 3126 pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f); 3127 DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg); 3128 } 3129 else { 3130 pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f); 3131 DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg); 3132 } 3133 } 3134 else { 3135 if (axis.IsOpposite()) { 3136 pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y ); 3137 DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg); 3138 } 3139 else { 3140 pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y); 3141 DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg); 3142 } 3143 } 3144 DrawList.AddRectFilled(pos,pos+size,tag.ColorBg); 3145 DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt); 3146 } 3147 3148 // FIT DATA -------------------------------------------------------------- 3149 const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); 3150 if (plot.FitThisFrame) { 3151 for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { 3152 ImPlotAxis& x_axis = plot.XAxis(i); 3153 if (x_axis.FitThisFrame) { 3154 x_axis.ApplyFit(gp.Style.FitPadding.x); 3155 if (axis_equal && x_axis.OrthoAxis != nullptr) { 3156 double aspect = x_axis.GetAspect(); 3157 ImPlotAxis& y_axis = *x_axis.OrthoAxis; 3158 if (y_axis.FitThisFrame) { 3159 y_axis.ApplyFit(gp.Style.FitPadding.y); 3160 y_axis.FitThisFrame = false; 3161 aspect = ImMax(aspect, y_axis.GetAspect()); 3162 } 3163 x_axis.SetAspect(aspect); 3164 y_axis.SetAspect(aspect); 3165 } 3166 } 3167 } 3168 for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { 3169 ImPlotAxis& y_axis = plot.YAxis(i); 3170 if (y_axis.FitThisFrame) { 3171 y_axis.ApplyFit(gp.Style.FitPadding.y); 3172 if (axis_equal && y_axis.OrthoAxis != nullptr) { 3173 double aspect = y_axis.GetAspect(); 3174 ImPlotAxis& x_axis = *y_axis.OrthoAxis; 3175 if (x_axis.FitThisFrame) { 3176 x_axis.ApplyFit(gp.Style.FitPadding.x); 3177 x_axis.FitThisFrame = false; 3178 aspect = ImMax(x_axis.GetAspect(), aspect); 3179 } 3180 x_axis.SetAspect(aspect); 3181 y_axis.SetAspect(aspect); 3182 } 3183 } 3184 } 3185 plot.FitThisFrame = false; 3186 } 3187 3188 // CONTEXT MENUS ----------------------------------------------------------- 3189 3190 ImGui::PushOverrideID(plot.ID); 3191 3192 const bool can_ctx = gp.OpenContextThisFrame && 3193 !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && 3194 !plot.Items.Legend.Hovered; 3195 3196 3197 3198 // main ctx menu 3199 if (can_ctx && plot.Hovered) 3200 ImGui::OpenPopup("##PlotContext"); 3201 if (ImGui::BeginPopup("##PlotContext")) { 3202 ShowPlotContextMenu(plot); 3203 ImGui::EndPopup(); 3204 } 3205 3206 // axes ctx menus 3207 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 3208 ImGui::PushID(i); 3209 ImPlotAxis& x_axis = plot.XAxis(i); 3210 if (can_ctx && x_axis.Hovered && x_axis.HasMenus()) 3211 ImGui::OpenPopup("##XContext"); 3212 if (ImGui::BeginPopup("##XContext")) { 3213 ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1); 3214 ImGui::Separator(); 3215 ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : nullptr, true); 3216 ImGui::EndPopup(); 3217 } 3218 ImGui::PopID(); 3219 } 3220 for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { 3221 ImGui::PushID(i); 3222 ImPlotAxis& y_axis = plot.YAxis(i); 3223 if (can_ctx && y_axis.Hovered && y_axis.HasMenus()) 3224 ImGui::OpenPopup("##YContext"); 3225 if (ImGui::BeginPopup("##YContext")) { 3226 ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); 3227 ImGui::Separator(); 3228 ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : nullptr, false); 3229 ImGui::EndPopup(); 3230 } 3231 ImGui::PopID(); 3232 } 3233 ImGui::PopID(); 3234 3235 // LINKED AXES ------------------------------------------------------------ 3236 3237 for (int i = 0; i < ImAxis_COUNT; ++i) 3238 plot.Axes[i].PushLinks(); 3239 3240 3241 // CLEANUP ---------------------------------------------------------------- 3242 3243 // remove items 3244 if (gp.CurrentItems == &plot.Items) 3245 gp.CurrentItems = nullptr; 3246 // reset the plot items for the next frame 3247 for (int i = 0; i < plot.Items.GetItemCount(); ++i) { 3248 plot.Items.GetItemByIndex(i)->SeenThisFrame = false; 3249 } 3250 3251 // mark the plot as initialized, i.e. having made it through one frame completely 3252 plot.Initialized = true; 3253 // Pop ImGui::PushID at the end of BeginPlot 3254 ImGui::PopID(); 3255 // Reset context for next plot 3256 ResetCtxForNextPlot(GImPlot); 3257 3258 // setup next subplot 3259 if (gp.CurrentSubplot != nullptr) { 3260 ImGui::PopID(); 3261 SubplotNextCell(); 3262 } 3263} 3264 3265//----------------------------------------------------------------------------- 3266// BEGIN/END SUBPLOT 3267//----------------------------------------------------------------------------- 3268 3269static const float SUBPLOT_BORDER_SIZE = 1.0f; 3270static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f; 3271static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f; 3272 3273void SubplotSetCell(int row, int col) { 3274 ImPlotContext& gp = *GImPlot; 3275 ImPlotSubplot& subplot = *gp.CurrentSubplot; 3276 if (row >= subplot.Rows || col >= subplot.Cols) 3277 return; 3278 float xoff = 0; 3279 float yoff = 0; 3280 for (int c = 0; c < col; ++c) 3281 xoff += subplot.ColRatios[c]; 3282 for (int r = 0; r < row; ++r) 3283 yoff += subplot.RowRatios[r]; 3284 const ImVec2 grid_size = subplot.GridRect.GetSize(); 3285 ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y); 3286 cpos.x = IM_ROUND(cpos.x); 3287 cpos.y = IM_ROUND(cpos.y); 3288 ImGui::GetCurrentWindow()->DC.CursorPos = cpos; 3289 // set cell size 3290 subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]); 3291 subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]); 3292 // setup links 3293 const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); 3294 const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); 3295 const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); 3296 const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); 3297 3298 SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : nullptr, 3299 lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : nullptr); 3300 SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : nullptr, 3301 ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : nullptr); 3302 // setup alignment 3303 if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) { 3304 gp.CurrentAlignmentH = &subplot.RowAlignmentData[row]; 3305 gp.CurrentAlignmentV = &subplot.ColAlignmentData[col]; 3306 } 3307 // set idx 3308 if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) 3309 subplot.CurrentIdx = col * subplot.Rows + row; 3310 else 3311 subplot.CurrentIdx = row * subplot.Cols + col; 3312} 3313 3314void SubplotSetCell(int idx) { 3315 ImPlotContext& gp = *GImPlot; 3316 ImPlotSubplot& subplot = *gp.CurrentSubplot; 3317 if (idx >= subplot.Rows * subplot.Cols) 3318 return; 3319 int row = 0, col = 0; 3320 if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) { 3321 row = idx % subplot.Rows; 3322 col = idx / subplot.Rows; 3323 } 3324 else { 3325 row = idx / subplot.Cols; 3326 col = idx % subplot.Cols; 3327 } 3328 return SubplotSetCell(row, col); 3329} 3330 3331void SubplotNextCell() { 3332 ImPlotContext& gp = *GImPlot; 3333 ImPlotSubplot& subplot = *gp.CurrentSubplot; 3334 SubplotSetCell(++subplot.CurrentIdx); 3335} 3336 3337bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) { 3338 IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!"); 3339 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 3340 ImPlotContext& gp = *GImPlot; 3341 IM_ASSERT_USER_ERROR(gp.CurrentSubplot == nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); 3342 ImGuiContext &G = *GImGui; 3343 ImGuiWindow * Window = G.CurrentWindow; 3344 if (Window->SkipItems) 3345 return false; 3346 const ImGuiID ID = Window->GetID(title); 3347 bool just_created = gp.Subplots.GetByKey(ID) == nullptr; 3348 gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID); 3349 ImPlotSubplot& subplot = *gp.CurrentSubplot; 3350 subplot.ID = ID; 3351 subplot.Items.ID = ID - 1; 3352 subplot.HasTitle = ImGui::FindRenderedTextEnd(title, nullptr) != title; 3353 // push ID 3354 ImGui::PushID(ID); 3355 3356 if (just_created) 3357 subplot.Flags = flags; 3358 else if (flags != subplot.PreviousFlags) 3359 subplot.Flags = flags; 3360 subplot.PreviousFlags = flags; 3361 3362 // check for change in rows and cols 3363 if (subplot.Rows != rows || subplot.Cols != cols) { 3364 subplot.RowAlignmentData.resize(rows); 3365 subplot.RowLinkData.resize(rows); 3366 subplot.RowRatios.resize(rows); 3367 for (int r = 0; r < rows; ++r) { 3368 subplot.RowAlignmentData[r].Reset(); 3369 subplot.RowLinkData[r] = ImPlotRange(0,1); 3370 subplot.RowRatios[r] = 1.0f / rows; 3371 } 3372 subplot.ColAlignmentData.resize(cols); 3373 subplot.ColLinkData.resize(cols); 3374 subplot.ColRatios.resize(cols); 3375 for (int c = 0; c < cols; ++c) { 3376 subplot.ColAlignmentData[c].Reset(); 3377 subplot.ColLinkData[c] = ImPlotRange(0,1); 3378 subplot.ColRatios[c] = 1.0f / cols; 3379 } 3380 } 3381 // check incoming size requests 3382 float row_sum = 0, col_sum = 0; 3383 if (row_sizes != nullptr) { 3384 row_sum = ImSum(row_sizes, rows); 3385 for (int r = 0; r < rows; ++r) 3386 subplot.RowRatios[r] = row_sizes[r] / row_sum; 3387 } 3388 if (col_sizes != nullptr) { 3389 col_sum = ImSum(col_sizes, cols); 3390 for (int c = 0; c < cols; ++c) 3391 subplot.ColRatios[c] = col_sizes[c] / col_sum; 3392 } 3393 subplot.Rows = rows; 3394 subplot.Cols = cols; 3395 3396 // calc plot frame sizes 3397 ImVec2 title_size(0.0f, 0.0f); 3398 if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)) 3399 title_size = ImGui::CalcTextSize(title, nullptr, true); 3400 const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0; 3401 const ImVec2 half_pad = gp.Style.PlotPadding/2; 3402 const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); 3403 subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); 3404 subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); 3405 subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; 3406 subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows|ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); 3407 3408 // outside legend adjustments (TODO: make function) 3409 const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); 3410 if (share_items) 3411 gp.CurrentItems = &subplot.Items; 3412 if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { 3413 ImPlotLegend& legend = subplot.Items.Legend; 3414 const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); 3415 const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); 3416 const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); 3417 const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); 3418 const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); 3419 const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); 3420 if ((west && !horz) || (west && horz && !north && !south)) 3421 subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); 3422 if ((east && !horz) || (east && horz && !north && !south)) 3423 subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); 3424 if ((north && horz) || (north && !horz && !west && !east)) 3425 subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); 3426 if ((south && horz) || (south && !horz && !west && !east)) 3427 subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); 3428 } 3429 3430 // render single background frame 3431 ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding); 3432 // render title 3433 if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) { 3434 const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); 3435 AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title); 3436 } 3437 3438 // render splitters 3439 if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) { 3440 ImDrawList& DrawList = *ImGui::GetWindowDrawList(); 3441 const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]); 3442 const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); 3443 float xpos = subplot.GridRect.Min.x; 3444 float ypos = subplot.GridRect.Min.y; 3445 int separator = 1; 3446 // bool pass = false; 3447 for (int r = 0; r < subplot.Rows-1; ++r) { 3448 ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight(); 3449 const ImGuiID sep_id = subplot.ID + separator; 3450 ImGui::KeepAliveID(sep_id); 3451 const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); 3452 bool sep_hov = false, sep_hld = false; 3453 const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); 3454 if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { 3455 if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { 3456 float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; 3457 subplot.RowRatios[r] = subplot.RowRatios[r+1] = p; 3458 } 3459 if (sep_clk) { 3460 subplot.TempSizes[0] = subplot.RowRatios[r]; 3461 subplot.TempSizes[1] = subplot.RowRatios[r+1]; 3462 } 3463 if (sep_hld) { 3464 float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight(); 3465 if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { 3466 subplot.RowRatios[r] = subplot.TempSizes[0] + dp; 3467 subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp; 3468 } 3469 } 3470 DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)), 3471 ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)), 3472 sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); 3473 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); 3474 } 3475 separator++; 3476 } 3477 for (int c = 0; c < subplot.Cols-1; ++c) { 3478 xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth(); 3479 const ImGuiID sep_id = subplot.ID + separator; 3480 ImGui::KeepAliveID(sep_id); 3481 const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); 3482 bool sep_hov = false, sep_hld = false; 3483 const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); 3484 if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { 3485 if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { 3486 float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; 3487 subplot.ColRatios[c] = subplot.ColRatios[c+1] = p; 3488 } 3489 if (sep_clk) { 3490 subplot.TempSizes[0] = subplot.ColRatios[c]; 3491 subplot.TempSizes[1] = subplot.ColRatios[c+1]; 3492 } 3493 if (sep_hld) { 3494 float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth(); 3495 if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { 3496 subplot.ColRatios[c] = subplot.TempSizes[0] + dp; 3497 subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp; 3498 } 3499 } 3500 DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)), 3501 ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)), 3502 sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); 3503 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); 3504 } 3505 separator++; 3506 } 3507 } 3508 3509 // set outgoing sizes 3510 if (row_sizes != nullptr) { 3511 for (int r = 0; r < rows; ++r) 3512 row_sizes[r] = subplot.RowRatios[r] * row_sum; 3513 } 3514 if (col_sizes != nullptr) { 3515 for (int c = 0; c < cols; ++c) 3516 col_sizes[c] = subplot.ColRatios[c] * col_sum; 3517 } 3518 3519 // push styling 3520 PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS); 3521 PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad); 3522 PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0)); 3523 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0); 3524 3525 // set initial cursor pos 3526 Window->DC.CursorPos = subplot.GridRect.Min; 3527 // begin alignments 3528 for (int r = 0; r < subplot.Rows; ++r) 3529 subplot.RowAlignmentData[r].Begin(); 3530 for (int c = 0; c < subplot.Cols; ++c) 3531 subplot.ColAlignmentData[c].Begin(); 3532 // clear legend data 3533 subplot.Items.Legend.Reset(); 3534 // Setup first subplot 3535 SubplotSetCell(0,0); 3536 return true; 3537} 3538 3539void EndSubplots() { 3540 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 3541 ImPlotContext& gp = *GImPlot; 3542 IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "Mismatched BeginSubplots()/EndSubplots()!"); 3543 ImPlotSubplot& subplot = *gp.CurrentSubplot; 3544 const ImGuiIO& IO = ImGui::GetIO(); 3545 // set alignments 3546 for (int r = 0; r < subplot.Rows; ++r) 3547 subplot.RowAlignmentData[r].End(); 3548 for (int c = 0; c < subplot.Cols; ++c) 3549 subplot.ColAlignmentData[c].End(); 3550 // pop styling 3551 PopStyleColor(); 3552 PopStyleVar(); 3553 PopStyleVar(); 3554 ImGui::PopStyleVar(); 3555 // legend 3556 subplot.Items.Legend.Hovered = false; 3557 for (int i = 0; i < subplot.Items.GetItemCount(); ++i) 3558 subplot.Items.GetItemByIndex(i)->LegendHovered = false; 3559 // render legend 3560 const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); 3561 ImDrawList& DrawList = *ImGui::GetWindowDrawList(); 3562 if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { 3563 ImPlotLegend& legend = subplot.Items.Legend; 3564 const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); 3565 const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); 3566 const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, legend.Location, gp.Style.PlotPadding); 3567 legend.Rect = ImRect(legend_pos, legend_pos + legend_size); 3568 legend.RectClamped = legend.Rect; 3569 const bool legend_scrollable = ClampLegendRect(legend.RectClamped,subplot.FrameRect, gp.Style.PlotPadding); 3570 const ImGuiButtonFlags legend_button_flags = ImGuiButtonFlags_AllowOverlap 3571 | ImGuiButtonFlags_PressedOnClick 3572 | ImGuiButtonFlags_PressedOnDoubleClick 3573 | ImGuiButtonFlags_MouseButtonLeft 3574 | ImGuiButtonFlags_MouseButtonRight 3575 | ImGuiButtonFlags_MouseButtonMiddle 3576 | ImGuiButtonFlags_FlattenChildren; 3577 ImGui::KeepAliveID(subplot.Items.ID); 3578 ImGui::ButtonBehavior(legend.RectClamped, subplot.Items.ID, &legend.Hovered, &legend.Held, legend_button_flags); 3579 legend.Hovered = legend.Hovered || (subplot.FrameHovered && legend.RectClamped.Contains(ImGui::GetIO().MousePos)); 3580 3581 if (legend_scrollable) { 3582 if (legend.Hovered) { 3583 ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, subplot.Items.ID); 3584 if (IO.MouseWheel != 0.0f) { 3585 ImVec2 max_step = legend.Rect.GetSize() * 0.67f; 3586 float font_size = ImGui::GetCurrentWindow()->CalcFontSize(); 3587 float scroll_step = ImFloor(ImMin(2 * font_size, max_step.x)); 3588 legend.Scroll.x += scroll_step * IO.MouseWheel; 3589 legend.Scroll.y += scroll_step * IO.MouseWheel; 3590 } 3591 } 3592 const ImVec2 min_scroll_offset = legend.RectClamped.GetSize() - legend.Rect.GetSize(); 3593 legend.Scroll.x = ImClamp(legend.Scroll.x, min_scroll_offset.x, 0.0f); 3594 legend.Scroll.y = ImClamp(legend.Scroll.y, min_scroll_offset.y, 0.0f); 3595 const ImVec2 scroll_offset = legend_horz ? ImVec2(legend.Scroll.x, 0) : ImVec2(0, legend.Scroll.y); 3596 ImVec2 legend_offset = legend.RectClamped.Min - legend.Rect.Min + scroll_offset; 3597 legend.Rect.Min += legend_offset; 3598 legend.Rect.Max += legend_offset; 3599 } else { 3600 legend.Scroll = ImVec2(0,0); 3601 } 3602 3603 const ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); 3604 const ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); 3605 ImGui::PushClipRect(legend.RectClamped.Min, legend.RectClamped.Max, true); 3606 DrawList.AddRectFilled(legend.RectClamped.Min, legend.RectClamped.Max, col_bg); 3607 bool legend_contextable = ShowLegendEntries(subplot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) 3608 && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); 3609 DrawList.AddRect(legend.RectClamped.Min, legend.RectClamped.Max, col_bd); 3610 ImGui::PopClipRect(); 3611 3612 if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) 3613 ImGui::OpenPopup("##LegendContext"); 3614 if (ImGui::BeginPopup("##LegendContext")) { 3615 ImGui::Text("Legend"); ImGui::Separator(); 3616 if (ShowLegendContextMenu(legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) 3617 ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); 3618 ImGui::EndPopup(); 3619 } 3620 } 3621 else { 3622 subplot.Items.Legend.Rect = ImRect(); 3623 } 3624 // remove items 3625 if (gp.CurrentItems == &subplot.Items) 3626 gp.CurrentItems = nullptr; 3627 // reset the plot items for the next frame (TODO: put this elswhere) 3628 for (int i = 0; i < subplot.Items.GetItemCount(); ++i) { 3629 subplot.Items.GetItemByIndex(i)->SeenThisFrame = false; 3630 } 3631 // pop id 3632 ImGui::PopID(); 3633 // set DC back correctly 3634 GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min; 3635 ImGui::Dummy(subplot.FrameRect.GetSize()); 3636 ResetCtxForNextSubplot(GImPlot); 3637 3638} 3639 3640//----------------------------------------------------------------------------- 3641// [SECTION] Plot Utils 3642//----------------------------------------------------------------------------- 3643 3644void SetAxis(ImAxis axis) { 3645 ImPlotContext& gp = *GImPlot; 3646 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxis() needs to be called between BeginPlot() and EndPlot()!"); 3647 IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!"); 3648 IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 3649 SetupLock(); 3650 if (axis < ImAxis_Y1) 3651 gp.CurrentPlot->CurrentX = axis; 3652 else 3653 gp.CurrentPlot->CurrentY = axis; 3654} 3655 3656void SetAxes(ImAxis x_idx, ImAxis y_idx) { 3657 ImPlotContext& gp = *GImPlot; 3658 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "SetAxes() needs to be called between BeginPlot() and EndPlot()!"); 3659 IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!"); 3660 IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!"); 3661 IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 3662 IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); 3663 SetupLock(); 3664 gp.CurrentPlot->CurrentX = x_idx; 3665 gp.CurrentPlot->CurrentY = y_idx; 3666} 3667 3668ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) { 3669 ImPlotContext& gp = *GImPlot; 3670 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); 3671 IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); 3672 IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); 3673 SetupLock(); 3674 ImPlotPlot& plot = *gp.CurrentPlot; 3675 ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; 3676 ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; 3677 return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) ); 3678} 3679 3680ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) { 3681 return PixelsToPlot(pix.x, pix.y, x_idx, y_idx); 3682} 3683 3684ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) { 3685 ImPlotContext& gp = *GImPlot; 3686 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); 3687 IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); 3688 IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); 3689 SetupLock(); 3690 ImPlotPlot& plot = *gp.CurrentPlot; 3691 ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; 3692 ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; 3693 return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) ); 3694} 3695 3696ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) { 3697 return PlotToPixels(plt.x, plt.y, x_idx, y_idx); 3698} 3699 3700ImVec2 GetPlotPos() { 3701 ImPlotContext& gp = *GImPlot; 3702 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); 3703 SetupLock(); 3704 return gp.CurrentPlot->PlotRect.Min; 3705} 3706 3707ImVec2 GetPlotSize() { 3708 ImPlotContext& gp = *GImPlot; 3709 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); 3710 SetupLock(); 3711 return gp.CurrentPlot->PlotRect.GetSize(); 3712} 3713 3714ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) { 3715 IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); 3716 SetupLock(); 3717 return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx); 3718} 3719 3720ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) { 3721 ImPlotContext& gp = *GImPlot; 3722 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); 3723 IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); 3724 IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); 3725 SetupLock(); 3726 ImPlotPlot& plot = *gp.CurrentPlot; 3727 ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; 3728 ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; 3729 ImPlotRect limits; 3730 limits.X = x_axis.Range; 3731 limits.Y = y_axis.Range; 3732 return limits; 3733} 3734 3735bool IsPlotHovered() { 3736 ImPlotContext& gp = *GImPlot; 3737 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); 3738 SetupLock(); 3739 return gp.CurrentPlot->Hovered; 3740} 3741 3742bool IsAxisHovered(ImAxis axis) { 3743 ImPlotContext& gp = *GImPlot; 3744 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); 3745 SetupLock(); 3746 return gp.CurrentPlot->Axes[axis].Hovered; 3747} 3748 3749bool IsSubplotsHovered() { 3750 ImPlotContext& gp = *GImPlot; 3751 IM_ASSERT_USER_ERROR(gp.CurrentSubplot != nullptr, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!"); 3752 return gp.CurrentSubplot->FrameHovered; 3753} 3754 3755bool IsPlotSelected() { 3756 ImPlotContext& gp = *GImPlot; 3757 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!"); 3758 SetupLock(); 3759 return gp.CurrentPlot->Selected; 3760} 3761 3762ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) { 3763 ImPlotContext& gp = *GImPlot; 3764 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); 3765 SetupLock(); 3766 ImPlotPlot& plot = *gp.CurrentPlot; 3767 if (!plot.Selected) 3768 return ImPlotRect(0,0,0,0); 3769 ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx); 3770 ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx); 3771 ImPlotRect result; 3772 result.X.Min = ImMin(p1.x, p2.x); 3773 result.X.Max = ImMax(p1.x, p2.x); 3774 result.Y.Min = ImMin(p1.y, p2.y); 3775 result.Y.Max = ImMax(p1.y, p2.y); 3776 return result; 3777} 3778 3779void CancelPlotSelection() { 3780 ImPlotContext& gp = *GImPlot; 3781 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); 3782 SetupLock(); 3783 ImPlotPlot& plot = *gp.CurrentPlot; 3784 if (plot.Selected) 3785 plot.Selected = plot.Selecting = false; 3786} 3787 3788void HideNextItem(bool hidden, ImPlotCond cond) { 3789 ImPlotContext& gp = *GImPlot; 3790 gp.NextItemData.HasHidden = true; 3791 gp.NextItemData.Hidden = hidden; 3792 gp.NextItemData.HiddenCond = cond; 3793} 3794 3795//----------------------------------------------------------------------------- 3796// [SECTION] Plot Tools 3797//----------------------------------------------------------------------------- 3798 3799void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) { 3800 ImPlotContext& gp = *GImPlot; 3801 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); 3802 SetupLock(); 3803 char x_buff[IMPLOT_LABEL_MAX_SIZE]; 3804 char y_buff[IMPLOT_LABEL_MAX_SIZE]; 3805 ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; 3806 ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY]; 3807 LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round); 3808 LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round); 3809 Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff); 3810} 3811 3812void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) { 3813 ImPlotContext& gp = *GImPlot; 3814 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); 3815 SetupLock(); 3816 ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO); 3817 ImU32 bg = ImGui::GetColorU32(col); 3818 ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); 3819 gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args); 3820} 3821 3822void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) { 3823 va_list args; 3824 va_start(args, fmt); 3825 AnnotationV(x,y,col,offset,clamp,fmt,args); 3826 va_end(args); 3827} 3828 3829void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) { 3830 ImPlotContext& gp = *GImPlot; 3831 SetupLock(); 3832 ImU32 bg = ImGui::GetColorU32(col); 3833 ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col); 3834 gp.Tags.AppendV(axis,v,bg,fg,fmt,args); 3835} 3836 3837void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) { 3838 va_list args; 3839 va_start(args, fmt); 3840 TagV(axis,v,col,fmt,args); 3841 va_end(args); 3842} 3843 3844void Tag(ImAxis axis, double v, const ImVec4& color, bool round) { 3845 ImPlotContext& gp = *GImPlot; 3846 SetupLock(); 3847 char buff[IMPLOT_LABEL_MAX_SIZE]; 3848 ImPlotAxis& ax = gp.CurrentPlot->Axes[axis]; 3849 LabelAxisValue(ax, v, buff, sizeof(buff), round); 3850 Tag(axis,v,color,"%s",buff); 3851} 3852 3853IMPLOT_API void TagX(double x, const ImVec4& color, bool round) { 3854 ImPlotContext& gp = *GImPlot; 3855 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); 3856 Tag(gp.CurrentPlot->CurrentX, x, color, round); 3857} 3858 3859IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) { 3860 ImPlotContext& gp = *GImPlot; 3861 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); 3862 va_list args; 3863 va_start(args, fmt); 3864 TagV(gp.CurrentPlot->CurrentX,x,color,fmt,args); 3865 va_end(args); 3866} 3867 3868IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) { 3869 ImPlotContext& gp = *GImPlot; 3870 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagX() needs to be called between BeginPlot() and EndPlot()!"); 3871 TagV(gp.CurrentPlot->CurrentX, x, color, fmt, args); 3872} 3873 3874IMPLOT_API void TagY(double y, const ImVec4& color, bool round) { 3875 ImPlotContext& gp = *GImPlot; 3876 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); 3877 Tag(gp.CurrentPlot->CurrentY, y, color, round); 3878} 3879 3880IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) { 3881 ImPlotContext& gp = *GImPlot; 3882 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); 3883 va_list args; 3884 va_start(args, fmt); 3885 TagV(gp.CurrentPlot->CurrentY,y,color,fmt,args); 3886 va_end(args); 3887} 3888 3889IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) { 3890 ImPlotContext& gp = *GImPlot; 3891 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "TagY() needs to be called between BeginPlot() and EndPlot()!"); 3892 TagV(gp.CurrentPlot->CurrentY, y, color, fmt, args); 3893} 3894 3895static const float DRAG_GRAB_HALF_SIZE = 4.0f; 3896 3897bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { 3898 ImGui::PushID("#IMPLOT_DRAG_POINT"); 3899 IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); 3900 SetupLock(); 3901 3902 if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { 3903 FitPoint(ImPlotPoint(*x,*y)); 3904 } 3905 3906 const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); 3907 const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); 3908 const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); 3909 const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius); 3910 const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; 3911 const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); 3912 3913 ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); 3914 const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); 3915 ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size); 3916 bool hovered = false, held = false; 3917 3918 ImGui::KeepAliveID(id); 3919 if (input) { 3920 bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); 3921 if (out_clicked) *out_clicked = clicked; 3922 if (out_hovered) *out_hovered = hovered; 3923 if (out_held) *out_held = held; 3924 } 3925 3926 bool modified = false; 3927 if (held && ImGui::IsMouseDragging(0)) { 3928 *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; 3929 *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; 3930 modified = true; 3931 } 3932 3933 PushPlotClipRect(); 3934 ImDrawList& DrawList = *GetPlotDrawList(); 3935 if ((hovered || held) && show_curs) 3936 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 3937 if (modified && no_delay) 3938 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); 3939 DrawList.AddCircleFilled(pos, radius, col32); 3940 PopPlotClipRect(); 3941 3942 ImGui::PopID(); 3943 return modified; 3944} 3945 3946bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { 3947 // ImGui::PushID("#IMPLOT_DRAG_LINE_X"); 3948 ImPlotContext& gp = *GImPlot; 3949 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); 3950 SetupLock(); 3951 3952 if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { 3953 FitPointX(*value); 3954 } 3955 3956 const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); 3957 const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); 3958 const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); 3959 const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); 3960 float yt = gp.CurrentPlot->PlotRect.Min.y; 3961 float yb = gp.CurrentPlot->PlotRect.Max.y; 3962 float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); 3963 const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); 3964 ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb); 3965 bool hovered = false, held = false; 3966 3967 ImGui::KeepAliveID(id); 3968 if (input) { 3969 bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); 3970 if (out_clicked) *out_clicked = clicked; 3971 if (out_hovered) *out_hovered = hovered; 3972 if (out_held) *out_held = held; 3973 } 3974 3975 if ((hovered || held) && show_curs) 3976 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); 3977 3978 float len = gp.Style.MajorTickLen.x; 3979 ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; 3980 ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); 3981 3982 bool modified = false; 3983 if (held && ImGui::IsMouseDragging(0)) { 3984 *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; 3985 modified = true; 3986 } 3987 3988 PushPlotClipRect(); 3989 ImDrawList& DrawList = *GetPlotDrawList(); 3990 if (modified && no_delay) 3991 x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); 3992 DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); 3993 DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); 3994 DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); 3995 PopPlotClipRect(); 3996 3997 // ImGui::PopID(); 3998 return modified; 3999} 4000 4001bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { 4002 ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); 4003 ImPlotContext& gp = *GImPlot; 4004 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); 4005 SetupLock(); 4006 4007 if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { 4008 FitPointY(*value); 4009 } 4010 4011 const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); 4012 const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); 4013 const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); 4014 const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); 4015 float xl = gp.CurrentPlot->PlotRect.Min.x; 4016 float xr = gp.CurrentPlot->PlotRect.Max.x; 4017 float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); 4018 4019 const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); 4020 ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size); 4021 bool hovered = false, held = false; 4022 4023 ImGui::KeepAliveID(id); 4024 if (input) { 4025 bool clicked = ImGui::ButtonBehavior(rect,id,&hovered,&held); 4026 if (out_clicked) *out_clicked = clicked; 4027 if (out_hovered) *out_hovered = hovered; 4028 if (out_held) *out_held = held; 4029 } 4030 4031 if ((hovered || held) && show_curs) 4032 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); 4033 4034 float len = gp.Style.MajorTickLen.y; 4035 ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; 4036 ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); 4037 4038 bool modified = false; 4039 if (held && ImGui::IsMouseDragging(0)) { 4040 *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; 4041 modified = true; 4042 } 4043 4044 PushPlotClipRect(); 4045 ImDrawList& DrawList = *GetPlotDrawList(); 4046 if (modified && no_delay) 4047 y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); 4048 DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); 4049 DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); 4050 DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); 4051 PopPlotClipRect(); 4052 4053 ImGui::PopID(); 4054 return modified; 4055} 4056 4057bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { 4058 ImGui::PushID("#IMPLOT_DRAG_RECT"); 4059 IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != nullptr, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); 4060 SetupLock(); 4061 4062 if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { 4063 FitPoint(ImPlotPoint(*x_min,*y_min)); 4064 FitPoint(ImPlotPoint(*x_max,*y_max)); 4065 } 4066 4067 const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); 4068 const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); 4069 const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); 4070 bool h[] = {true,false,true,false}; 4071 double* x[] = {x_min,x_max,x_max,x_min}; 4072 double* y[] = {y_min,y_min,y_max,y_max}; 4073 ImVec2 p[4]; 4074 for (int i = 0; i < 4; ++i) 4075 p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); 4076 ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); 4077 ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); 4078 ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE); 4079 4080 ImGuiMouseCursor cur[4]; 4081 if (show_curs) { 4082 cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW; 4083 cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 4084 cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 4085 cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 4086 } 4087 4088 ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; 4089 ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); 4090 color.w *= 0.25f; 4091 ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); 4092 const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); 4093 4094 bool modified = false; 4095 bool clicked = false, hovered = false, held = false; 4096 ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); 4097 4098 ImGui::KeepAliveID(id); 4099 if (input) { 4100 // middle point 4101 clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); 4102 if (out_clicked) *out_clicked = clicked; 4103 if (out_hovered) *out_hovered = hovered; 4104 if (out_held) *out_held = held; 4105 } 4106 4107 if ((hovered || held) && show_curs) 4108 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); 4109 if (held && ImGui::IsMouseDragging(0)) { 4110 for (int i = 0; i < 4; ++i) { 4111 ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); 4112 *y[i] = pp.y; 4113 *x[i] = pp.x; 4114 } 4115 modified = true; 4116 } 4117 4118 for (int i = 0; i < 4; ++i) { 4119 // points 4120 b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); 4121 ImGuiID p_id = id + i + 1; 4122 ImGui::KeepAliveID(p_id); 4123 if (input) { 4124 clicked = ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); 4125 if (out_clicked) *out_clicked = *out_clicked || clicked; 4126 if (out_hovered) *out_hovered = *out_hovered || hovered; 4127 if (out_held) *out_held = *out_held || held; 4128 } 4129 if ((hovered || held) && show_curs) 4130 ImGui::SetMouseCursor(cur[i]); 4131 4132 if (held && ImGui::IsMouseDragging(0)) { 4133 *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; 4134 *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; 4135 modified = true; 4136 } 4137 4138 // edges 4139 ImVec2 e_min = ImMin(p[i],p[(i+1)%4]); 4140 ImVec2 e_max = ImMax(p[i],p[(i+1)%4]); 4141 b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE) 4142 : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); 4143 ImGuiID e_id = id + i + 5; 4144 ImGui::KeepAliveID(e_id); 4145 if (input) { 4146 clicked = ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); 4147 if (out_clicked) *out_clicked = *out_clicked || clicked; 4148 if (out_hovered) *out_hovered = *out_hovered || hovered; 4149 if (out_held) *out_held = *out_held || held; 4150 } 4151 if ((hovered || held) && show_curs) 4152 h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); 4153 if (held && ImGui::IsMouseDragging(0)) { 4154 if (h[i]) 4155 *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; 4156 else 4157 *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; 4158 modified = true; 4159 } 4160 if (hovered && ImGui::IsMouseDoubleClicked(0)) 4161 { 4162 ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); 4163 if (h[i]) 4164 *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; 4165 else 4166 *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; 4167 modified = true; 4168 } 4169 } 4170 4171 const bool mouse_inside = rect_grab.Contains(ImGui::GetMousePos()); 4172 const bool mouse_clicked = ImGui::IsMouseClicked(0); 4173 const bool mouse_down = ImGui::IsMouseDown(0); 4174 if (input && mouse_inside) { 4175 if (out_clicked) *out_clicked = *out_clicked || mouse_clicked; 4176 if (out_hovered) *out_hovered = true; 4177 if (out_held) *out_held = *out_held || mouse_down; 4178 } 4179 4180 PushPlotClipRect(); 4181 ImDrawList& DrawList = *GetPlotDrawList(); 4182 if (modified && no_delay) { 4183 for (int i = 0; i < 4; ++i) 4184 p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); 4185 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); 4186 rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); 4187 } 4188 DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); 4189 DrawList.AddRect(rect.Min, rect.Max, col32); 4190 if (input && (modified || mouse_inside)) { 4191 DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); 4192 for (int i = 0; i < 4; ++i) 4193 DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); 4194 } 4195 PopPlotClipRect(); 4196 ImGui::PopID(); 4197 return modified; 4198} 4199 4200bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags, bool* out_clicked, bool* out_hovered, bool* out_held) { 4201 return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags, out_clicked, out_hovered, out_held); 4202} 4203 4204//----------------------------------------------------------------------------- 4205// [SECTION] Legend Utils and Tools 4206//----------------------------------------------------------------------------- 4207 4208bool IsLegendEntryHovered(const char* label_id) { 4209 ImPlotContext& gp = *GImPlot; 4210 IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "IsPlotItemHighlight() needs to be called within an itemized context!"); 4211 SetupLock(); 4212 ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); 4213 ImPlotItem* item = gp.CurrentItems->GetItem(id); 4214 return item && item->LegendHovered; 4215} 4216 4217bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { 4218 ImPlotContext& gp = *GImPlot; 4219 IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginLegendPopup() needs to be called within an itemized context!"); 4220 SetupLock(); 4221 ImGuiWindow* window = GImGui->CurrentWindow; 4222 if (window->SkipItems) 4223 return false; 4224 ImGuiID id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); 4225 if (ImGui::IsMouseReleased(mouse_button)) { 4226 ImPlotItem* item = gp.CurrentItems->GetItem(id); 4227 if (item && item->LegendHovered) 4228 ImGui::OpenPopupEx(id); 4229 } 4230 return ImGui::BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 4231} 4232 4233void EndLegendPopup() { 4234 SetupLock(); 4235 ImGui::EndPopup(); 4236} 4237 4238void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) { 4239 ImPlotContext& gp = *GImPlot; 4240 ImGuiContext &G = *GImGui; 4241 ImGuiWindow * Window = G.CurrentWindow; 4242 if (Window->SkipItems) 4243 return; 4244 ImDrawList &DrawList = *Window->DrawList; 4245 ImPlotPlot* plot = GetPlot(title_id); 4246 ImVec2 legend_size; 4247 ImVec2 default_size = gp.Style.LegendPadding * 2; 4248 if (plot != nullptr) { 4249 legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical); 4250 default_size = legend_size + gp.Style.LegendPadding * 2; 4251 } 4252 ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y); 4253 ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); 4254 ImGui::ItemSize(bb_frame); 4255 if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) 4256 return; 4257 ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); 4258 DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true); 4259 if (plot != nullptr) { 4260 const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding); 4261 const ImRect legend_bb(legend_pos, legend_pos + legend_size); 4262 interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos); 4263 // render legend box 4264 ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); 4265 ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); 4266 DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); 4267 DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); 4268 // render entries 4269 ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList); 4270 } 4271 DrawList.PopClipRect(); 4272} 4273 4274//----------------------------------------------------------------------------- 4275// [SECTION] Drag and Drop Utils 4276//----------------------------------------------------------------------------- 4277 4278bool BeginDragDropTargetPlot() { 4279 SetupLock(); 4280 ImPlotContext& gp = *GImPlot; 4281 ImRect rect = gp.CurrentPlot->PlotRect; 4282 return ImGui::BeginDragDropTargetCustom(rect, gp.CurrentPlot->ID); 4283} 4284 4285bool BeginDragDropTargetAxis(ImAxis axis) { 4286 SetupLock(); 4287 ImPlotPlot& plot = *GImPlot->CurrentPlot; 4288 ImPlotAxis& ax = plot.Axes[axis]; 4289 ImRect rect = ax.HoverRect; 4290 rect.Expand(-3.5f); 4291 return ImGui::BeginDragDropTargetCustom(rect, ax.ID); 4292} 4293 4294bool BeginDragDropTargetLegend() { 4295 SetupLock(); 4296 ImPlotItemGroup& items = *GImPlot->CurrentItems; 4297 ImRect rect = items.Legend.RectClamped; 4298 return ImGui::BeginDragDropTargetCustom(rect, items.ID); 4299} 4300 4301void EndDragDropTarget() { 4302 SetupLock(); 4303 ImGui::EndDragDropTarget(); 4304} 4305 4306bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) { 4307 SetupLock(); 4308 ImPlotContext& gp = *GImPlot; 4309 ImPlotPlot* plot = gp.CurrentPlot; 4310 if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID) 4311 return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags); 4312 return false; 4313} 4314 4315bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) { 4316 SetupLock(); 4317 ImPlotContext& gp = *GImPlot; 4318 ImPlotAxis& axis = gp.CurrentPlot->Axes[idx]; 4319 if (GImGui->IO.KeyMods == gp.InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID) 4320 return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags); 4321 return false; 4322} 4323 4324bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { 4325 SetupLock(); 4326 ImPlotContext& gp = *GImPlot; 4327 IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "BeginDragDropSourceItem() needs to be called within an itemized context!"); 4328 ImGuiID item_id = ImGui::GetIDWithSeed(label_id, nullptr, gp.CurrentItems->ID); 4329 ImPlotItem* item = gp.CurrentItems->GetItem(item_id); 4330 if (item != nullptr) { 4331 return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags); 4332 } 4333 return false; 4334} 4335 4336void EndDragDropSource() { 4337 SetupLock(); 4338 ImGui::EndDragDropSource(); 4339} 4340 4341//----------------------------------------------------------------------------- 4342// [SECTION] Aligned Plots 4343//----------------------------------------------------------------------------- 4344 4345bool BeginAlignedPlots(const char* group_id, bool vertical) { 4346 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 4347 ImPlotContext& gp = *GImPlot; 4348 IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH == nullptr && gp.CurrentAlignmentV == nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); 4349 ImGuiContext &G = *GImGui; 4350 ImGuiWindow * Window = G.CurrentWindow; 4351 if (Window->SkipItems) 4352 return false; 4353 const ImGuiID ID = Window->GetID(group_id); 4354 ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID); 4355 if (vertical) 4356 gp.CurrentAlignmentV = alignment; 4357 else 4358 gp.CurrentAlignmentH = alignment; 4359 if (alignment->Vertical != vertical) 4360 alignment->Reset(); 4361 alignment->Vertical = vertical; 4362 alignment->Begin(); 4363 return true; 4364} 4365 4366void EndAlignedPlots() { 4367 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 4368 ImPlotContext& gp = *GImPlot; 4369 IM_ASSERT_USER_ERROR(gp.CurrentAlignmentH != nullptr || gp.CurrentAlignmentV != nullptr, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); 4370 ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != nullptr ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != nullptr ? gp.CurrentAlignmentV : nullptr); 4371 if (alignment) 4372 alignment->End(); 4373 ResetCtxForNextAlignedPlots(GImPlot); 4374} 4375 4376//----------------------------------------------------------------------------- 4377// [SECTION] Plot and Item Styling 4378//----------------------------------------------------------------------------- 4379 4380ImPlotStyle& GetStyle() { 4381 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 4382 ImPlotContext& gp = *GImPlot; 4383 return gp.Style; 4384} 4385 4386void PushStyleColor(ImPlotCol idx, ImU32 col) { 4387 ImPlotContext& gp = *GImPlot; 4388 ImGuiColorMod backup; 4389 backup.Col = (ImGuiCol)idx; 4390 backup.BackupValue = gp.Style.Colors[idx]; 4391 gp.ColorModifiers.push_back(backup); 4392 gp.Style.Colors[idx] = ImGui::ColorConvertU32ToFloat4(col); 4393} 4394 4395void PushStyleColor(ImPlotCol idx, const ImVec4& col) { 4396 ImPlotContext& gp = *GImPlot; 4397 ImGuiColorMod backup; 4398 backup.Col = (ImGuiCol)idx; 4399 backup.BackupValue = gp.Style.Colors[idx]; 4400 gp.ColorModifiers.push_back(backup); 4401 gp.Style.Colors[idx] = col; 4402} 4403 4404void PopStyleColor(int count) { 4405 ImPlotContext& gp = *GImPlot; 4406 IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!"); 4407 while (count > 0) 4408 { 4409 ImGuiColorMod& backup = gp.ColorModifiers.back(); 4410 gp.Style.Colors[backup.Col] = backup.BackupValue; 4411 gp.ColorModifiers.pop_back(); 4412 count--; 4413 } 4414} 4415 4416void PushStyleVar(ImPlotStyleVar idx, float val) { 4417 ImPlotContext& gp = *GImPlot; 4418 const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); 4419 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { 4420 float* pvar = (float*)var_info->GetVarPtr(&gp.Style); 4421 gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); 4422 *pvar = val; 4423 return; 4424 } 4425 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); 4426} 4427 4428void PushStyleVar(ImPlotStyleVar idx, int val) { 4429 ImPlotContext& gp = *GImPlot; 4430 const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); 4431 if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) { 4432 int* pvar = (int*)var_info->GetVarPtr(&gp.Style); 4433 gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); 4434 *pvar = val; 4435 return; 4436 } 4437 else if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) { 4438 float* pvar = (float*)var_info->GetVarPtr(&gp.Style); 4439 gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); 4440 *pvar = (float)val; 4441 return; 4442 } 4443 IM_ASSERT(0 && "Called PushStyleVar() int variant but variable is not a int!"); 4444} 4445 4446void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val) 4447{ 4448 ImPlotContext& gp = *GImPlot; 4449 const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx); 4450 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) 4451 { 4452 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&gp.Style); 4453 gp.StyleModifiers.push_back(ImGuiStyleMod((ImGuiStyleVar)idx, *pvar)); 4454 *pvar = val; 4455 return; 4456 } 4457 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); 4458} 4459 4460void PopStyleVar(int count) { 4461 ImPlotContext& gp = *GImPlot; 4462 IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!"); 4463 while (count > 0) { 4464 ImGuiStyleMod& backup = gp.StyleModifiers.back(); 4465 const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx); 4466 void* data = info->GetVarPtr(&gp.Style); 4467 if (info->Type == ImGuiDataType_Float && info->Count == 1) { 4468 ((float*)data)[0] = backup.BackupFloat[0]; 4469 } 4470 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { 4471 ((float*)data)[0] = backup.BackupFloat[0]; 4472 ((float*)data)[1] = backup.BackupFloat[1]; 4473 } 4474 else if (info->Type == ImGuiDataType_S32 && info->Count == 1) { 4475 ((int*)data)[0] = backup.BackupInt[0]; 4476 } 4477 gp.StyleModifiers.pop_back(); 4478 count--; 4479 } 4480} 4481 4482//------------------------------------------------------------------------------ 4483// [Section] Colormaps 4484//------------------------------------------------------------------------------ 4485 4486ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { 4487 ImPlotContext& gp = *GImPlot; 4488 IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); 4489 IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!"); 4490 ImVector<ImU32> buffer; 4491 buffer.resize(size); 4492 for (int i = 0; i < size; ++i) 4493 buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]); 4494 return gp.ColormapData.Append(name, buffer.Data, size, qual); 4495} 4496 4497ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) { 4498 ImPlotContext& gp = *GImPlot; 4499 IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); 4500 IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!"); 4501 return gp.ColormapData.Append(name, colormap, size, qual); 4502} 4503 4504int GetColormapCount() { 4505 ImPlotContext& gp = *GImPlot; 4506 return gp.ColormapData.Count; 4507} 4508 4509const char* GetColormapName(ImPlotColormap colormap) { 4510 ImPlotContext& gp = *GImPlot; 4511 return gp.ColormapData.GetName(colormap); 4512} 4513 4514ImPlotColormap GetColormapIndex(const char* name) { 4515 ImPlotContext& gp = *GImPlot; 4516 return gp.ColormapData.GetIndex(name); 4517} 4518 4519void PushColormap(ImPlotColormap colormap) { 4520 ImPlotContext& gp = *GImPlot; 4521 IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); 4522 gp.ColormapModifiers.push_back(gp.Style.Colormap); 4523 gp.Style.Colormap = colormap; 4524} 4525 4526void PushColormap(const char* name) { 4527 ImPlotContext& gp = *GImPlot; 4528 ImPlotColormap idx = gp.ColormapData.GetIndex(name); 4529 IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!"); 4530 PushColormap(idx); 4531} 4532 4533void PopColormap(int count) { 4534 ImPlotContext& gp = *GImPlot; 4535 IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!"); 4536 while (count > 0) { 4537 const ImPlotColormap& backup = gp.ColormapModifiers.back(); 4538 gp.Style.Colormap = backup; 4539 gp.ColormapModifiers.pop_back(); 4540 count--; 4541 } 4542} 4543 4544ImU32 NextColormapColorU32() { 4545 ImPlotContext& gp = *GImPlot; 4546 IM_ASSERT_USER_ERROR(gp.CurrentItems != nullptr, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); 4547 int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); 4548 ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); 4549 gp.CurrentItems->ColormapIdx++; 4550 return col; 4551} 4552 4553ImVec4 NextColormapColor() { 4554 return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); 4555} 4556 4557int GetColormapSize(ImPlotColormap cmap) { 4558 ImPlotContext& gp = *GImPlot; 4559 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4560 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4561 return gp.ColormapData.GetKeyCount(cmap); 4562} 4563 4564ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { 4565 ImPlotContext& gp = *GImPlot; 4566 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4567 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4568 idx = idx % gp.ColormapData.GetKeyCount(cmap); 4569 return gp.ColormapData.GetKeyColor(cmap, idx); 4570} 4571 4572ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) { 4573 return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap)); 4574} 4575 4576ImU32 SampleColormapU32(float t, ImPlotColormap cmap) { 4577 ImPlotContext& gp = *GImPlot; 4578 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4579 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4580 return gp.ColormapData.LerpTable(cmap, t); 4581} 4582 4583ImVec4 SampleColormap(float t, ImPlotColormap cmap) { 4584 return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); 4585} 4586 4587void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { 4588 const int n = continuous ? size - 1 : size; 4589 ImU32 col1, col2; 4590 if (vert) { 4591 const float step = bounds.GetHeight() / n; 4592 ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step); 4593 for (int i = 0; i < n; ++i) { 4594 if (reversed) { 4595 col1 = colors[size-i-1]; 4596 col2 = continuous ? colors[size-i-2] : col1; 4597 } 4598 else { 4599 col1 = colors[i]; 4600 col2 = continuous ? colors[i+1] : col1; 4601 } 4602 DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); 4603 rect.TranslateY(step); 4604 } 4605 } 4606 else { 4607 const float step = bounds.GetWidth() / n; 4608 ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y); 4609 for (int i = 0; i < n; ++i) { 4610 if (reversed) { 4611 col1 = colors[size-i-1]; 4612 col2 = continuous ? colors[size-i-2] : col1; 4613 } 4614 else { 4615 col1 = colors[i]; 4616 col2 = continuous ? colors[i+1] : col1; 4617 } 4618 DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); 4619 rect.TranslateX(step); 4620 } 4621 } 4622} 4623 4624void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, const char* format, ImPlotColormapScaleFlags flags, ImPlotColormap cmap) { 4625 ImGuiContext &G = *GImGui; 4626 ImGuiWindow * Window = G.CurrentWindow; 4627 if (Window->SkipItems) 4628 return; 4629 4630 const ImGuiID ID = Window->GetID(label); 4631 ImVec2 label_size(0,0); 4632 if (!ImHasFlag(flags, ImPlotColormapScaleFlags_NoLabel)) { 4633 label_size = ImGui::CalcTextSize(label,nullptr,true); 4634 } 4635 4636 ImPlotContext& gp = *GImPlot; 4637 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4638 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4639 4640 ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y); 4641 if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) 4642 frame_size.y = gp.Style.PlotMinSize.y; 4643 4644 ImPlotRange range(ImMin(scale_min,scale_max), ImMax(scale_min,scale_max)); 4645 gp.CTicker.Reset(); 4646 Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)format); 4647 4648 const bool rend_label = label_size.x > 0; 4649 const float txt_off = gp.Style.LabelPadding.x; 4650 const float pad = txt_off + gp.CTicker.MaxSize.x + (rend_label ? txt_off + label_size.y : 0); 4651 float bar_w = 20; 4652 if (frame_size.x == 0) 4653 frame_size.x = bar_w + pad + 2 * gp.Style.PlotPadding.x; 4654 else { 4655 bar_w = frame_size.x - (pad + 2 * gp.Style.PlotPadding.x); 4656 if (bar_w < gp.Style.MajorTickLen.y) 4657 bar_w = gp.Style.MajorTickLen.y; 4658 } 4659 4660 ImDrawList &DrawList = *Window->DrawList; 4661 ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); 4662 ImGui::ItemSize(bb_frame); 4663 if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame)) 4664 return; 4665 4666 ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); 4667 4668 const bool opposite = ImHasFlag(flags, ImPlotColormapScaleFlags_Opposite); 4669 const bool inverted = ImHasFlag(flags, ImPlotColormapScaleFlags_Invert); 4670 const bool reversed = scale_min > scale_max; 4671 4672 float bb_grad_shift = opposite ? pad : 0; 4673 ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding + ImVec2(bb_grad_shift, 0), 4674 bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x + bb_grad_shift, 4675 frame_size.y - gp.Style.PlotPadding.y)); 4676 4677 ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); 4678 const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); 4679 4680 const bool invert_scale = inverted ? (reversed ? false : true) : (reversed ? true : false); 4681 const float y_min = invert_scale ? bb_grad.Max.y : bb_grad.Min.y; 4682 const float y_max = invert_scale ? bb_grad.Min.y : bb_grad.Max.y; 4683 4684 RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, !inverted, !gp.ColormapData.IsQual(cmap)); 4685 for (int i = 0; i < gp.CTicker.TickCount(); ++i) { 4686 const double y_pos_plt = gp.CTicker.Ticks[i].PlotPos; 4687 const float y_pos = ImRemap((float)y_pos_plt, (float)range.Max, (float)range.Min, y_min, y_max); 4688 const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; 4689 const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; 4690 const float tick_t = (float)((y_pos_plt - scale_min) / (scale_max - scale_min)); 4691 const ImU32 tick_col = CalcTextColor(gp.ColormapData.LerpTable(cmap,tick_t)); 4692 if (y_pos < bb_grad.Max.y - 2 && y_pos > bb_grad.Min.y + 2) { 4693 DrawList.AddLine(opposite ? ImVec2(bb_grad.Min.x+1, y_pos) : ImVec2(bb_grad.Max.x-1, y_pos), 4694 opposite ? ImVec2(bb_grad.Min.x + tick_width, y_pos) : ImVec2(bb_grad.Max.x - tick_width, y_pos), 4695 tick_col, 4696 tick_thick); 4697 } 4698 const float txt_x = opposite ? bb_grad.Min.x - txt_off - gp.CTicker.Ticks[i].LabelSize.x : bb_grad.Max.x + txt_off; 4699 const float txt_y = y_pos - gp.CTicker.Ticks[i].LabelSize.y * 0.5f; 4700 DrawList.AddText(ImVec2(txt_x, txt_y), col_text, gp.CTicker.GetText(i)); 4701 } 4702 4703 if (rend_label) { 4704 const float pos_x = opposite ? bb_frame.Min.x + gp.Style.PlotPadding.x : bb_grad.Max.x + 2 * txt_off + gp.CTicker.MaxSize.x; 4705 const float pos_y = bb_grad.GetCenter().y + label_size.x * 0.5f; 4706 const char* label_end = ImGui::FindRenderedTextEnd(label); 4707 AddTextVertical(&DrawList,ImVec2(pos_x,pos_y),col_text,label,label_end); 4708 } 4709 DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder)); 4710 ImGui::PopClipRect(); 4711} 4712 4713bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) { 4714 *t = ImClamp(*t,0.0f,1.0f); 4715 ImGuiContext &G = *GImGui; 4716 ImGuiWindow * Window = G.CurrentWindow; 4717 if (Window->SkipItems) 4718 return false; 4719 ImPlotContext& gp = *GImPlot; 4720 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4721 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4722 const ImU32* keys = gp.ColormapData.GetKeys(cmap); 4723 const int count = gp.ColormapData.GetKeyCount(cmap); 4724 const bool qual = gp.ColormapData.IsQual(cmap); 4725 const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; 4726 const float w = ImGui::CalcItemWidth(); 4727 const float h = ImGui::GetFrameHeight(); 4728 const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h); 4729 RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); 4730 const ImU32 grab = CalcTextColor(gp.ColormapData.LerpTable(cmap,*t)); 4731 // const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,0.5f)); 4732 ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); 4733 ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); 4734 ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f)); 4735 ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab); 4736 ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab); 4737 ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2); 4738 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); 4739 const bool changed = ImGui::SliderFloat(label,t,0,1,format); 4740 ImGui::PopStyleColor(5); 4741 ImGui::PopStyleVar(2); 4742 if (out != nullptr) 4743 *out = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(cmap,*t)); 4744 return changed; 4745} 4746 4747bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) { 4748 ImGuiContext &G = *GImGui; 4749 const ImGuiStyle& style = G.Style; 4750 ImGuiWindow * Window = G.CurrentWindow; 4751 if (Window->SkipItems) 4752 return false; 4753 ImPlotContext& gp = *GImPlot; 4754 cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; 4755 IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); 4756 const ImU32* keys = gp.ColormapData.GetKeys(cmap); 4757 const int count = gp.ColormapData.GetKeyCount(cmap); 4758 const bool qual = gp.ColormapData.IsQual(cmap); 4759 const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; 4760 const ImVec2 label_size = ImGui::CalcTextSize(label, nullptr, true); 4761 ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); 4762 const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y); 4763 RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); 4764 const ImU32 text = CalcTextColor(gp.ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x)); 4765 ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS); 4766 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f)); 4767 ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f)); 4768 ImGui::PushStyleColor(ImGuiCol_Text,text); 4769 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); 4770 const bool pressed = ImGui::Button(label,size); 4771 ImGui::PopStyleColor(4); 4772 ImGui::PopStyleVar(1); 4773 return pressed; 4774} 4775 4776//----------------------------------------------------------------------------- 4777// [Section] Miscellaneous 4778//----------------------------------------------------------------------------- 4779 4780ImPlotInputMap& GetInputMap() { 4781 IM_ASSERT_USER_ERROR(GImPlot != nullptr, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); 4782 ImPlotContext& gp = *GImPlot; 4783 return gp.InputMap; 4784} 4785 4786void MapInputDefault(ImPlotInputMap* dst) { 4787 ImPlotInputMap& map = dst ? *dst : GetInputMap(); 4788 map.Pan = ImGuiMouseButton_Left; 4789 map.PanMod = ImGuiMod_None; 4790 map.Fit = ImGuiMouseButton_Left; 4791 map.Menu = ImGuiMouseButton_Right; 4792 map.Select = ImGuiMouseButton_Right; 4793 map.SelectMod = ImGuiMod_None; 4794 map.SelectCancel = ImGuiMouseButton_Left; 4795 map.SelectHorzMod = ImGuiMod_Alt; 4796 map.SelectVertMod = ImGuiMod_Shift; 4797 map.OverrideMod = ImGuiMod_Ctrl; 4798 map.ZoomMod = ImGuiMod_None; 4799 map.ZoomRate = 0.1f; 4800} 4801 4802void MapInputReverse(ImPlotInputMap* dst) { 4803 ImPlotInputMap& map = dst ? *dst : GetInputMap(); 4804 map.Pan = ImGuiMouseButton_Right; 4805 map.PanMod = ImGuiMod_None; 4806 map.Fit = ImGuiMouseButton_Left; 4807 map.Menu = ImGuiMouseButton_Right; 4808 map.Select = ImGuiMouseButton_Left; 4809 map.SelectMod = ImGuiMod_None; 4810 map.SelectCancel = ImGuiMouseButton_Right; 4811 map.SelectHorzMod = ImGuiMod_Alt; 4812 map.SelectVertMod = ImGuiMod_Shift; 4813 map.OverrideMod = ImGuiMod_Ctrl; 4814 map.ZoomMod = ImGuiMod_None; 4815 map.ZoomRate = 0.1f; 4816} 4817 4818//----------------------------------------------------------------------------- 4819// [Section] Miscellaneous 4820//----------------------------------------------------------------------------- 4821 4822void ItemIcon(const ImVec4& col) { 4823 ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); 4824} 4825 4826void ItemIcon(ImU32 col) { 4827 const float txt_size = ImGui::GetTextLineHeight(); 4828 ImVec2 size(txt_size-4,txt_size); 4829 ImGuiWindow* window = ImGui::GetCurrentWindow(); 4830 ImVec2 pos = window->DC.CursorPos; 4831 ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); 4832 ImGui::Dummy(size); 4833} 4834 4835void ColormapIcon(ImPlotColormap cmap) { 4836 ImPlotContext& gp = *GImPlot; 4837 const float txt_size = ImGui::GetTextLineHeight(); 4838 ImVec2 size(txt_size-4,txt_size); 4839 ImGuiWindow* window = ImGui::GetCurrentWindow(); 4840 ImVec2 pos = window->DC.CursorPos; 4841 ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); 4842 ImDrawList& DrawList = *ImGui::GetWindowDrawList(); 4843 RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); 4844 ImGui::Dummy(size); 4845} 4846 4847ImDrawList* GetPlotDrawList() { 4848 return ImGui::GetWindowDrawList(); 4849} 4850 4851void PushPlotClipRect(float expand) { 4852 ImPlotContext& gp = *GImPlot; 4853 IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); 4854 SetupLock(); 4855 ImRect rect = gp.CurrentPlot->PlotRect; 4856 rect.Expand(expand); 4857 ImGui::PushClipRect(rect.Min, rect.Max, true); 4858} 4859 4860void PopPlotClipRect() { 4861 SetupLock(); 4862 ImGui::PopClipRect(); 4863} 4864 4865static void HelpMarker(const char* desc) { 4866 ImGui::TextDisabled("(?)"); 4867 if (ImGui::IsItemHovered()) { 4868 ImGui::BeginTooltip(); 4869 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 4870 ImGui::TextUnformatted(desc); 4871 ImGui::PopTextWrapPos(); 4872 ImGui::EndTooltip(); 4873 } 4874} 4875 4876bool ShowStyleSelector(const char* label) 4877{ 4878 static int style_idx = -1; 4879 if (ImGui::Combo(label, &style_idx, "Auto\0Classic\0Dark\0Light\0")) 4880 { 4881 switch (style_idx) 4882 { 4883 case 0: StyleColorsAuto(); break; 4884 case 1: StyleColorsClassic(); break; 4885 case 2: StyleColorsDark(); break; 4886 case 3: StyleColorsLight(); break; 4887 } 4888 return true; 4889 } 4890 return false; 4891} 4892 4893bool ShowColormapSelector(const char* label) { 4894 ImPlotContext& gp = *GImPlot; 4895 bool set = false; 4896 if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) { 4897 for (int i = 0; i < gp.ColormapData.Count; ++i) { 4898 const char* name = gp.ColormapData.GetName(i); 4899 if (ImGui::Selectable(name, gp.Style.Colormap == i)) { 4900 gp.Style.Colormap = i; 4901 ImPlot::BustItemCache(); 4902 set = true; 4903 } 4904 } 4905 ImGui::EndCombo(); 4906 } 4907 return set; 4908} 4909 4910bool ShowInputMapSelector(const char* label) { 4911 static int map_idx = -1; 4912 if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0")) 4913 { 4914 switch (map_idx) 4915 { 4916 case 0: MapInputDefault(); break; 4917 case 1: MapInputReverse(); break; 4918 } 4919 return true; 4920 } 4921 return false; 4922} 4923 4924 4925void ShowStyleEditor(ImPlotStyle* ref) { 4926 ImPlotContext& gp = *GImPlot; 4927 ImPlotStyle& style = GetStyle(); 4928 static ImPlotStyle ref_saved_style; 4929 // Default to using internal storage as reference 4930 static bool init = true; 4931 if (init && ref == nullptr) 4932 ref_saved_style = style; 4933 init = false; 4934 if (ref == nullptr) 4935 ref = &ref_saved_style; 4936 4937 if (ImPlot::ShowStyleSelector("Colors##Selector")) 4938 ref_saved_style = style; 4939 4940 // Save/Revert button 4941 if (ImGui::Button("Save Ref")) 4942 *ref = ref_saved_style = style; 4943 ImGui::SameLine(); 4944 if (ImGui::Button("Revert Ref")) 4945 style = *ref; 4946 ImGui::SameLine(); 4947 HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. " 4948 "Use \"Export\" below to save them somewhere."); 4949 if (ImGui::BeginTabBar("##StyleEditor")) { 4950 if (ImGui::BeginTabItem("Variables")) { 4951 ImGui::Text("Item Styling"); 4952 ImGui::SliderFloat("LineWeight", &style.LineWeight, 0.0f, 5.0f, "%.1f"); 4953 ImGui::SliderFloat("MarkerSize", &style.MarkerSize, 2.0f, 10.0f, "%.1f"); 4954 ImGui::SliderFloat("MarkerWeight", &style.MarkerWeight, 0.0f, 5.0f, "%.1f"); 4955 ImGui::SliderFloat("FillAlpha", &style.FillAlpha, 0.0f, 1.0f, "%.2f"); 4956 ImGui::SliderFloat("ErrorBarSize", &style.ErrorBarSize, 0.0f, 10.0f, "%.1f"); 4957 ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f"); 4958 ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f"); 4959 ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f"); 4960 ImGui::Text("Plot Styling"); 4961 ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f"); 4962 ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f"); 4963 ImGui::SliderFloat2("MajorTickLen", (float*)&style.MajorTickLen, 0.0f, 20.0f, "%.0f"); 4964 ImGui::SliderFloat2("MinorTickLen", (float*)&style.MinorTickLen, 0.0f, 20.0f, "%.0f"); 4965 ImGui::SliderFloat2("MajorTickSize", (float*)&style.MajorTickSize, 0.0f, 2.0f, "%.1f"); 4966 ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); 4967 ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f"); 4968 ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f"); 4969 ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f"); 4970 ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); 4971 ImGui::Text("Plot Padding"); 4972 ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); 4973 ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f"); 4974 ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f"); 4975 ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f"); 4976 ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f"); 4977 ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f"); 4978 ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); 4979 ImGui::SliderFloat2("FitPadding", (float*)&style.FitPadding, 0, 0.2f, "%.2f"); 4980 4981 ImGui::EndTabItem(); 4982 } 4983 if (ImGui::BeginTabItem("Colors")) { 4984 static int output_dest = 0; 4985 static bool output_only_modified = false; 4986 4987 if (ImGui::Button("Export", ImVec2(75,0))) { 4988 if (output_dest == 0) 4989 ImGui::LogToClipboard(); 4990 else 4991 ImGui::LogToTTY(); 4992 ImGui::LogText("ImVec4* colors = ImPlot::GetStyle().Colors;\n"); 4993 for (int i = 0; i < ImPlotCol_COUNT; i++) { 4994 const ImVec4& col = style.Colors[i]; 4995 const char* name = ImPlot::GetStyleColorName(i); 4996 if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) { 4997 if (IsColorAuto(i)) 4998 ImGui::LogText("colors[ImPlotCol_%s]%*s= IMPLOT_AUTO_COL;\n",name,14 - (int)strlen(name), ""); 4999 else 5000 ImGui::LogText("colors[ImPlotCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n", 5001 name, 14 - (int)strlen(name), "", col.x, col.y, col.z, col.w); 5002 } 5003 } 5004 ImGui::LogFinish(); 5005 } 5006 ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); 5007 ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); 5008 5009 static ImGuiTextFilter filter; 5010 filter.Draw("Filter colors", ImGui::GetFontSize() * 16); 5011 5012 static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; 5013 if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); 5014 if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); 5015 if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); 5016 HelpMarker( 5017 "In the color list:\n" 5018 "Left-click on colored square to open color picker,\n" 5019 "Right-click to open edit options menu."); 5020 ImGui::Separator(); 5021 ImGui::PushItemWidth(-160); 5022 for (int i = 0; i < ImPlotCol_COUNT; i++) { 5023 const char* name = ImPlot::GetStyleColorName(i); 5024 if (!filter.PassFilter(name)) 5025 continue; 5026 ImGui::PushID(i); 5027 ImVec4 temp = GetStyleColorVec4(i); 5028 const bool is_auto = IsColorAuto(i); 5029 if (!is_auto) 5030 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); 5031 if (ImGui::Button("Auto")) { 5032 if (is_auto) 5033 style.Colors[i] = temp; 5034 else 5035 style.Colors[i] = IMPLOT_AUTO_COL; 5036 BustItemCache(); 5037 } 5038 if (!is_auto) 5039 ImGui::PopStyleVar(); 5040 ImGui::SameLine(); 5041 if (ImGui::ColorEdit4(name, &temp.x, ImGuiColorEditFlags_NoInputs | alpha_flags)) { 5042 style.Colors[i] = temp; 5043 BustItemCache(); 5044 } 5045 if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { 5046 ImGui::SameLine(175); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } 5047 ImGui::SameLine(); if (ImGui::Button("Revert")) { 5048 style.Colors[i] = ref->Colors[i]; 5049 BustItemCache(); 5050 } 5051 } 5052 ImGui::PopID(); 5053 } 5054 ImGui::PopItemWidth(); 5055 ImGui::Separator(); 5056 ImGui::Text("Colors that are set to Auto (i.e. IMPLOT_AUTO_COL) will\n" 5057 "be automatically deduced from your ImGui style or the\n" 5058 "current ImPlot Colormap. If you want to style individual\n" 5059 "plot items, use Push/PopStyleColor around its function."); 5060 ImGui::EndTabItem(); 5061 } 5062 if (ImGui::BeginTabItem("Colormaps")) { 5063 static int output_dest = 0; 5064 if (ImGui::Button("Export", ImVec2(75,0))) { 5065 if (output_dest == 0) 5066 ImGui::LogToClipboard(); 5067 else 5068 ImGui::LogToTTY(); 5069 int size = GetColormapSize(); 5070 const char* name = GetColormapName(gp.Style.Colormap); 5071 ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); 5072 for (int i = 0; i < size; ++i) { 5073 ImU32 col = GetColormapColorU32(i,gp.Style.Colormap); 5074 ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); 5075 } 5076 ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); 5077 ImGui::LogFinish(); 5078 } 5079 ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); 5080 ImGui::SameLine(); 5081 static bool edit = false; 5082 ImGui::Checkbox("Edit Mode",&edit); 5083 5084 // built-in/added 5085 ImGui::Separator(); 5086 for (int i = 0; i < gp.ColormapData.Count; ++i) { 5087 ImGui::PushID(i); 5088 int size = gp.ColormapData.GetKeyCount(i); 5089 bool selected = i == gp.Style.Colormap; 5090 5091 const char* name = GetColormapName(i); 5092 if (!selected) 5093 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); 5094 if (ImGui::Button(name, ImVec2(100,0))) { 5095 gp.Style.Colormap = i; 5096 BustItemCache(); 5097 } 5098 if (!selected) 5099 ImGui::PopStyleVar(); 5100 ImGui::SameLine(); 5101 ImGui::BeginGroup(); 5102 if (edit) { 5103 for (int c = 0; c < size; ++c) { 5104 ImGui::PushID(c); 5105 ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); 5106 if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { 5107 ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); 5108 gp.ColormapData.SetKeyColor(i,c,col32); 5109 BustItemCache(); 5110 } 5111 if ((c + 1) % 12 != 0 && c != size -1) 5112 ImGui::SameLine(); 5113 ImGui::PopID(); 5114 } 5115 } 5116 else { 5117 if (ImPlot::ColormapButton("##",ImVec2(-1,0),i)) 5118 edit = true; 5119 } 5120 ImGui::EndGroup(); 5121 ImGui::PopID(); 5122 } 5123 5124 5125 static ImVector<ImVec4> custom; 5126 if (custom.Size == 0) { 5127 custom.push_back(ImVec4(1,0,0,1)); 5128 custom.push_back(ImVec4(0,1,0,1)); 5129 custom.push_back(ImVec4(0,0,1,1)); 5130 } 5131 ImGui::Separator(); 5132 ImGui::BeginGroup(); 5133 static char name[16] = "MyColormap"; 5134 5135 5136 if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0))) 5137 custom.push_back(ImVec4(0,0,0,1)); 5138 ImGui::SameLine(); 5139 if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) 5140 custom.pop_back(); 5141 ImGui::SetNextItemWidth(100); 5142 ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); 5143 static bool qual = true; 5144 ImGui::Checkbox("Qualitative",&qual); 5145 if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1) 5146 AddColormap(name,custom.Data,custom.Size,qual); 5147 5148 ImGui::EndGroup(); 5149 ImGui::SameLine(); 5150 ImGui::BeginGroup(); 5151 for (int c = 0; c < custom.Size; ++c) { 5152 ImGui::PushID(c); 5153 if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) { 5154 5155 } 5156 if ((c + 1) % 12 != 0) 5157 ImGui::SameLine(); 5158 ImGui::PopID(); 5159 } 5160 ImGui::EndGroup(); 5161 5162 5163 ImGui::EndTabItem(); 5164 } 5165 ImGui::EndTabBar(); 5166 } 5167} 5168 5169void ShowUserGuide() { 5170 ImGui::BulletText("Left-click drag within the plot area to pan X and Y axes."); 5171 ImGui::Indent(); 5172 ImGui::BulletText("Left-click drag on axis labels to pan an individual axis."); 5173 ImGui::Unindent(); 5174 ImGui::BulletText("Scroll in the plot area to zoom both X any Y axes."); 5175 ImGui::Indent(); 5176 ImGui::BulletText("Scroll on axis labels to zoom an individual axis."); 5177 ImGui::Unindent(); 5178 ImGui::BulletText("Right-click drag to box select data."); 5179 ImGui::Indent(); 5180 ImGui::BulletText("Hold Alt to expand box selection horizontally."); 5181 ImGui::BulletText("Hold Shift to expand box selection vertically."); 5182 ImGui::BulletText("Left-click while box selecting to cancel the selection."); 5183 ImGui::Unindent(); 5184 ImGui::BulletText("Double left-click to fit all visible data."); 5185 ImGui::Indent(); 5186 ImGui::BulletText("Double left-click axis labels to fit the individual axis."); 5187 ImGui::Unindent(); 5188 ImGui::BulletText("Right-click open the full plot context menu."); 5189 ImGui::Indent(); 5190 ImGui::BulletText("Right-click axis labels to open an individual axis context menu."); 5191 ImGui::Unindent(); 5192 ImGui::BulletText("Click legend label icons to show/hide plot items."); 5193} 5194 5195void ShowTicksMetrics(const ImPlotTicker& ticker) { 5196 ImGui::BulletText("Size: %d", ticker.TickCount()); 5197 ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y); 5198} 5199 5200void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { 5201 ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis)); 5202 ImGui::BulletText("Flags: 0x%08X", axis.Flags); 5203 ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max); 5204 ImGui::BulletText("Pixels: %f", axis.PixelSize()); 5205 ImGui::BulletText("Aspect: %f", axis.GetAspect()); 5206 ImGui::BulletText(axis.OrthoAxis == nullptr ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID); 5207 ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin); 5208 ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax); 5209 ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false"); 5210 ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false"); 5211 ImGui::BulletText("Held: %s", axis.Held ? "true" : "false"); 5212 5213 if (ImGui::TreeNode("Transform")) { 5214 ImGui::BulletText("PixelMin: %f", axis.PixelMin); 5215 ImGui::BulletText("PixelMax: %f", axis.PixelMax); 5216 ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel); 5217 ImGui::BulletText("ScaleMax: %f", axis.ScaleMax); 5218 ImGui::TreePop(); 5219 } 5220 5221 if (ImGui::TreeNode("Ticks")) { 5222 ShowTicksMetrics(axis.Ticker); 5223 ImGui::TreePop(); 5224 } 5225} 5226 5227void ShowMetricsWindow(bool* p_popen) { 5228 5229 static bool show_plot_rects = false; 5230 static bool show_axes_rects = false; 5231 static bool show_axis_rects = false; 5232 static bool show_canvas_rects = false; 5233 static bool show_frame_rects = false; 5234 static bool show_subplot_frame_rects = false; 5235 static bool show_subplot_grid_rects = false; 5236 static bool show_legend_rects = false; 5237 5238 ImDrawList& fg = *ImGui::GetForegroundDrawList(); 5239 5240 ImPlotContext& gp = *GImPlot; 5241 // ImGuiContext& g = *GImGui; 5242 ImGuiIO& io = ImGui::GetIO(); 5243 ImGui::Begin("ImPlot Metrics", p_popen); 5244 ImGui::Text("ImPlot " IMPLOT_VERSION); 5245 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); 5246 ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y); 5247 ImGui::Separator(); 5248 if (ImGui::TreeNode("Tools")) { 5249 if (ImGui::Button("Bust Plot Cache")) 5250 BustPlotCache(); 5251 ImGui::SameLine(); 5252 if (ImGui::Button("Bust Item Cache")) 5253 BustItemCache(); 5254 ImGui::Checkbox("Show Frame Rects", &show_frame_rects); 5255 ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects); 5256 ImGui::Checkbox("Show Plot Rects", &show_plot_rects); 5257 ImGui::Checkbox("Show Axes Rects", &show_axes_rects); 5258 ImGui::Checkbox("Show Axis Rects", &show_axis_rects); 5259 ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); 5260 ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); 5261 ImGui::Checkbox("Show Legend Rects", &show_legend_rects); 5262 ImGui::TreePop(); 5263 } 5264 const int n_plots = gp.Plots.GetBufSize(); 5265 const int n_subplots = gp.Subplots.GetBufSize(); 5266 // render rects 5267 for (int p = 0; p < n_plots; ++p) { 5268 ImPlotPlot* plot = gp.Plots.GetByIndex(p); 5269 if (show_frame_rects) 5270 fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255)); 5271 if (show_canvas_rects) 5272 fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255)); 5273 if (show_plot_rects) 5274 fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); 5275 if (show_axes_rects) 5276 fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255)); 5277 if (show_axis_rects) { 5278 for (int i = 0; i < ImAxis_COUNT; ++i) { 5279 if (plot->Axes[i].Enabled) 5280 fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); 5281 } 5282 } 5283 if (show_legend_rects && plot->Items.GetLegendCount() > 0) { 5284 fg.AddRect(plot->Items.Legend.Rect.Min, plot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255)); 5285 fg.AddRect(plot->Items.Legend.RectClamped.Min, plot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255)); 5286 } 5287 } 5288 for (int p = 0; p < n_subplots; ++p) { 5289 ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); 5290 if (show_subplot_frame_rects) 5291 fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); 5292 if (show_subplot_grid_rects) 5293 fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); 5294 if (show_legend_rects && subplot->Items.GetLegendCount() > 0) { 5295 fg.AddRect(subplot->Items.Legend.Rect.Min, subplot->Items.Legend.Rect.Max, IM_COL32(255,192,0,255)); 5296 fg.AddRect(subplot->Items.Legend.RectClamped.Min, subplot->Items.Legend.RectClamped.Max, IM_COL32(255,128,0,255)); 5297 } 5298 } 5299 if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { 5300 for (int p = 0; p < n_plots; ++p) { 5301 // plot 5302 ImPlotPlot& plot = *gp.Plots.GetByIndex(p); 5303 ImGui::PushID(p); 5304 if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) { 5305 int n_items = plot.Items.GetItemCount(); 5306 if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { 5307 for (int i = 0; i < n_items; ++i) { 5308 ImPlotItem* item = plot.Items.GetItemByIndex(i); 5309 ImGui::PushID(i); 5310 if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { 5311 ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); 5312 ImGui::Bullet(); 5313 ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); 5314 if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) 5315 item->Color = ImGui::ColorConvertFloat4ToU32(temp); 5316 5317 ImGui::BulletText("NameOffset: %d",item->NameOffset); 5318 ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); 5319 ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); 5320 ImGui::TreePop(); 5321 } 5322 ImGui::PopID(); 5323 } 5324 ImGui::TreePop(); 5325 } 5326 char buff[16]; 5327 for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { 5328 ImFormatString(buff,16,"X-Axis %d", i+1); 5329 if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) { 5330 ShowAxisMetrics(plot, plot.XAxis(i)); 5331 ImGui::TreePop(); 5332 } 5333 } 5334 for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { 5335 ImFormatString(buff,16,"Y-Axis %d", i+1); 5336 if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) { 5337 ShowAxisMetrics(plot, plot.YAxis(i)); 5338 ImGui::TreePop(); 5339 } 5340 } 5341 ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none"); 5342 ImGui::BulletText("Flags: 0x%08X", plot.Flags); 5343 ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false"); 5344 ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false"); 5345 ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false"); 5346 ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false"); 5347 ImGui::BulletText("Held: %s", plot.Held ? "true" : "false"); 5348 ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); 5349 ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false"); 5350 ImGui::TreePop(); 5351 } 5352 ImGui::PopID(); 5353 } 5354 ImGui::TreePop(); 5355 } 5356 5357 if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) { 5358 for (int p = 0; p < n_subplots; ++p) { 5359 // plot 5360 ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p); 5361 ImGui::PushID(p); 5362 if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) { 5363 int n_items = plot.Items.GetItemCount(); 5364 if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { 5365 for (int i = 0; i < n_items; ++i) { 5366 ImPlotItem* item = plot.Items.GetItemByIndex(i); 5367 ImGui::PushID(i); 5368 if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { 5369 ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); 5370 ImGui::Bullet(); 5371 ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); 5372 if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) 5373 item->Color = ImGui::ColorConvertFloat4ToU32(temp); 5374 5375 ImGui::BulletText("NameOffset: %d",item->NameOffset); 5376 ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); 5377 ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); 5378 ImGui::TreePop(); 5379 } 5380 ImGui::PopID(); 5381 } 5382 ImGui::TreePop(); 5383 } 5384 ImGui::BulletText("Flags: 0x%08X", plot.Flags); 5385 ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false"); 5386 ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); 5387 ImGui::TreePop(); 5388 } 5389 ImGui::PopID(); 5390 } 5391 ImGui::TreePop(); 5392 } 5393 if (ImGui::TreeNode("Colormaps")) { 5394 ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); 5395 ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4); 5396 if (ImGui::TreeNode("Data")) { 5397 for (int m = 0; m < gp.ColormapData.Count; ++m) { 5398 if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { 5399 int count = gp.ColormapData.GetKeyCount(m); 5400 int size = gp.ColormapData.GetTableSize(m); 5401 bool qual = gp.ColormapData.IsQual(m); 5402 ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); 5403 ImGui::BulletText("Key Count: %d", count); 5404 ImGui::BulletText("Table Size: %d", size); 5405 ImGui::Indent(); 5406 5407 static float t = 0.5; 5408 ImVec4 samp; 5409 float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x; 5410 ImGui::SetNextItemWidth(wid); 5411 ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m); 5412 ImGui::SameLine(); 5413 ImGui::ColorButton("Sampler",samp); 5414 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); 5415 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); 5416 for (int c = 0; c < size; ++c) { 5417 ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c)); 5418 ImGui::PushID(m*1000+c); 5419 ImGui::ColorButton("",col,0,ImVec2(10,10)); 5420 ImGui::PopID(); 5421 if ((c + 1) % 32 != 0 && c != size - 1) 5422 ImGui::SameLine(); 5423 } 5424 ImGui::PopStyleVar(); 5425 ImGui::PopStyleColor(); 5426 ImGui::Unindent(); 5427 ImGui::TreePop(); 5428 } 5429 } 5430 ImGui::TreePop(); 5431 } 5432 ImGui::TreePop(); 5433 } 5434 ImGui::End(); 5435} 5436 5437bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) { 5438 5439 ImGui::PushID(id); 5440 ImGui::BeginGroup(); 5441 5442 ImGuiStyle& style = ImGui::GetStyle(); 5443 ImVec4 col_txt = style.Colors[ImGuiCol_Text]; 5444 ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled]; 5445 ImVec4 col_btn = style.Colors[ImGuiCol_Button]; 5446 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); 5447 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); 5448 5449 const float ht = ImGui::GetFrameHeight(); 5450 ImVec2 cell_size(ht*1.25f,ht); 5451 char buff[32]; 5452 bool clk = false; 5453 tm& Tm = GImPlot->Tm; 5454 5455 const int min_yr = 1970; 5456 const int max_yr = 2999; 5457 5458 // t1 parts 5459 int t1_mo = 0; int t1_md = 0; int t1_yr = 0; 5460 if (t1 != nullptr) { 5461 GetTime(*t1,&Tm); 5462 t1_mo = Tm.tm_mon; 5463 t1_md = Tm.tm_mday; 5464 t1_yr = Tm.tm_year + 1900; 5465 } 5466 5467 // t2 parts 5468 int t2_mo = 0; int t2_md = 0; int t2_yr = 0; 5469 if (t2 != nullptr) { 5470 GetTime(*t2,&Tm); 5471 t2_mo = Tm.tm_mon; 5472 t2_md = Tm.tm_mday; 5473 t2_yr = Tm.tm_year + 1900; 5474 } 5475 5476 // day widget 5477 if (*level == 0) { 5478 *t = FloorTime(*t, ImPlotTimeUnit_Day); 5479 GetTime(*t, &Tm); 5480 const int this_year = Tm.tm_year + 1900; 5481 const int last_year = this_year - 1; 5482 const int next_year = this_year + 1; 5483 const int this_mon = Tm.tm_mon; 5484 const int last_mon = this_mon == 0 ? 11 : this_mon - 1; 5485 const int next_mon = this_mon == 11 ? 0 : this_mon + 1; 5486 const int days_this_mo = GetDaysInMonth(this_year, this_mon); 5487 const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon); 5488 ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo); 5489 GetTime(t_first_mo,&Tm); 5490 const int first_wd = Tm.tm_wday; 5491 // month year 5492 ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); 5493 if (ImGui::Button(buff)) 5494 *level = 1; 5495 ImGui::SameLine(5*cell_size.x); 5496 BeginDisabledControls(this_year <= min_yr && this_mon == 0); 5497 if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) 5498 *t = AddTime(*t, ImPlotTimeUnit_Mo, -1); 5499 EndDisabledControls(this_year <= min_yr && this_mon == 0); 5500 ImGui::SameLine(); 5501 BeginDisabledControls(this_year >= max_yr && this_mon == 11); 5502 if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) 5503 *t = AddTime(*t, ImPlotTimeUnit_Mo, 1); 5504 EndDisabledControls(this_year >= max_yr && this_mon == 11); 5505 // render weekday abbreviations 5506 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 5507 for (int i = 0; i < 7; ++i) { 5508 ImGui::Button(WD_ABRVS[i],cell_size); 5509 if (i != 6) { ImGui::SameLine(); } 5510 } 5511 ImGui::PopItemFlag(); 5512 // 0 = last mo, 1 = this mo, 2 = next mo 5513 int mo = first_wd > 0 ? 0 : 1; 5514 int day = mo == 1 ? 1 : days_last_mo - first_wd + 1; 5515 for (int i = 0; i < 6; ++i) { 5516 for (int j = 0; j < 7; ++j) { 5517 if (mo == 0 && day > days_last_mo) { 5518 mo = 1; 5519 day = 1; 5520 } 5521 else if (mo == 1 && day > days_this_mo) { 5522 mo = 2; 5523 day = 1; 5524 } 5525 const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year); 5526 const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon); 5527 const int now_md = day; 5528 5529 const bool off_mo = mo == 0 || mo == 2; 5530 const bool t1_or_t2 = (t1 != nullptr && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) || 5531 (t2 != nullptr && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md); 5532 5533 if (off_mo) 5534 ImGui::PushStyleColor(ImGuiCol_Text, col_dis); 5535 if (t1_or_t2) { 5536 ImGui::PushStyleColor(ImGuiCol_Button, col_btn); 5537 ImGui::PushStyleColor(ImGuiCol_Text, col_txt); 5538 } 5539 ImGui::PushID(i*7+j); 5540 ImFormatString(buff,32,"%d",day); 5541 if (now_yr == min_yr-1 || now_yr == max_yr+1) { 5542 ImGui::Dummy(cell_size); 5543 } 5544 else if (ImGui::Button(buff,cell_size) && !clk) { 5545 *t = MakeTime(now_yr, now_mo, now_md); 5546 clk = true; 5547 } 5548 ImGui::PopID(); 5549 if (t1_or_t2) 5550 ImGui::PopStyleColor(2); 5551 if (off_mo) 5552 ImGui::PopStyleColor(); 5553 if (j != 6) 5554 ImGui::SameLine(); 5555 day++; 5556 } 5557 } 5558 } 5559 // month widget 5560 else if (*level == 1) { 5561 *t = FloorTime(*t, ImPlotTimeUnit_Mo); 5562 GetTime(*t, &Tm); 5563 int this_yr = Tm.tm_year + 1900; 5564 ImFormatString(buff, 32, "%d", this_yr); 5565 if (ImGui::Button(buff)) 5566 *level = 2; 5567 BeginDisabledControls(this_yr <= min_yr); 5568 ImGui::SameLine(5*cell_size.x); 5569 if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) 5570 *t = AddTime(*t, ImPlotTimeUnit_Yr, -1); 5571 EndDisabledControls(this_yr <= min_yr); 5572 ImGui::SameLine(); 5573 BeginDisabledControls(this_yr >= max_yr); 5574 if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) 5575 *t = AddTime(*t, ImPlotTimeUnit_Yr, 1); 5576 EndDisabledControls(this_yr >= max_yr); 5577 // ImGui::Dummy(cell_size); 5578 cell_size.x *= 7.0f/4.0f; 5579 cell_size.y *= 7.0f/3.0f; 5580 int mo = 0; 5581 for (int i = 0; i < 3; ++i) { 5582 for (int j = 0; j < 4; ++j) { 5583 const bool t1_or_t2 = (t1 != nullptr && t1_yr == this_yr && t1_mo == mo) || 5584 (t2 != nullptr && t2_yr == this_yr && t2_mo == mo); 5585 if (t1_or_t2) 5586 ImGui::PushStyleColor(ImGuiCol_Button, col_btn); 5587 if (ImGui::Button(MONTH_ABRVS[mo],cell_size) && !clk) { 5588 *t = MakeTime(this_yr, mo); 5589 *level = 0; 5590 } 5591 if (t1_or_t2) 5592 ImGui::PopStyleColor(); 5593 if (j != 3) 5594 ImGui::SameLine(); 5595 mo++; 5596 } 5597 } 5598 } 5599 else if (*level == 2) { 5600 *t = FloorTime(*t, ImPlotTimeUnit_Yr); 5601 int this_yr = GetYear(*t); 5602 int yr = this_yr - this_yr % 20; 5603 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); 5604 ImFormatString(buff,32,"%d-%d",yr,yr+19); 5605 ImGui::Button(buff); 5606 ImGui::PopItemFlag(); 5607 ImGui::SameLine(5*cell_size.x); 5608 BeginDisabledControls(yr <= min_yr); 5609 if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size)) 5610 *t = MakeTime(yr-20); 5611 EndDisabledControls(yr <= min_yr); 5612 ImGui::SameLine(); 5613 BeginDisabledControls(yr + 20 >= max_yr); 5614 if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size)) 5615 *t = MakeTime(yr+20); 5616 EndDisabledControls(yr+ 20 >= max_yr); 5617 // ImGui::Dummy(cell_size); 5618 cell_size.x *= 7.0f/4.0f; 5619 cell_size.y *= 7.0f/5.0f; 5620 for (int i = 0; i < 5; ++i) { 5621 for (int j = 0; j < 4; ++j) { 5622 const bool t1_or_t2 = (t1 != nullptr && t1_yr == yr) || (t2 != nullptr && t2_yr == yr); 5623 if (t1_or_t2) 5624 ImGui::PushStyleColor(ImGuiCol_Button, col_btn); 5625 ImFormatString(buff,32,"%d",yr); 5626 if (yr<1970||yr>3000) { 5627 ImGui::Dummy(cell_size); 5628 } 5629 else if (ImGui::Button(buff,cell_size)) { 5630 *t = MakeTime(yr); 5631 *level = 1; 5632 } 5633 if (t1_or_t2) 5634 ImGui::PopStyleColor(); 5635 if (j != 3) 5636 ImGui::SameLine(); 5637 yr++; 5638 } 5639 } 5640 } 5641 ImGui::PopStyleVar(); 5642 ImGui::PopStyleColor(); 5643 ImGui::EndGroup(); 5644 ImGui::PopID(); 5645 return clk; 5646} 5647 5648bool ShowTimePicker(const char* id, ImPlotTime* t) { 5649 ImPlotContext& gp = *GImPlot; 5650 ImGui::PushID(id); 5651 tm& Tm = gp.Tm; 5652 GetTime(*t,&Tm); 5653 5654 static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09", 5655 "10","11","12","13","14","15","16","17","18","19", 5656 "20","21","22","23","24","25","26","27","28","29", 5657 "30","31","32","33","34","35","36","37","38","39", 5658 "40","41","42","43","44","45","46","47","48","49", 5659 "50","51","52","53","54","55","56","57","58","59"}; 5660 5661 static const char* am_pm[] = {"am","pm"}; 5662 5663 bool hour24 = gp.Style.Use24HourClock; 5664 5665 int hr = hour24 ? Tm.tm_hour : ((Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12); 5666 int min = Tm.tm_min; 5667 int sec = Tm.tm_sec; 5668 int ap = Tm.tm_hour < 12 ? 0 : 1; 5669 5670 bool changed = false; 5671 5672 ImVec2 spacing = ImGui::GetStyle().ItemSpacing; 5673 spacing.x = 0; 5674 float width = ImGui::CalcTextSize("888").x; 5675 float height = ImGui::GetFrameHeight(); 5676 5677 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing); 5678 ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f); 5679 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); 5680 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); 5681 ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered)); 5682 5683 ImGui::SetNextItemWidth(width); 5684 if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) { 5685 const int ia = hour24 ? 0 : 1; 5686 const int ib = hour24 ? 24 : 13; 5687 for (int i = ia; i < ib; ++i) { 5688 if (ImGui::Selectable(nums[i],i==hr)) { 5689 hr = i; 5690 changed = true; 5691 } 5692 } 5693 ImGui::EndCombo(); 5694 } 5695 ImGui::SameLine(); 5696 ImGui::Text(":"); 5697 ImGui::SameLine(); 5698 ImGui::SetNextItemWidth(width); 5699 if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) { 5700 for (int i = 0; i < 60; ++i) { 5701 if (ImGui::Selectable(nums[i],i==min)) { 5702 min = i; 5703 changed = true; 5704 } 5705 } 5706 ImGui::EndCombo(); 5707 } 5708 ImGui::SameLine(); 5709 ImGui::Text(":"); 5710 ImGui::SameLine(); 5711 ImGui::SetNextItemWidth(width); 5712 if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) { 5713 for (int i = 0; i < 60; ++i) { 5714 if (ImGui::Selectable(nums[i],i==sec)) { 5715 sec = i; 5716 changed = true; 5717 } 5718 } 5719 ImGui::EndCombo(); 5720 } 5721 if (!hour24) { 5722 ImGui::SameLine(); 5723 if (ImGui::Button(am_pm[ap],ImVec2(0,height))) { 5724 ap = 1 - ap; 5725 changed = true; 5726 } 5727 } 5728 5729 ImGui::PopStyleColor(3); 5730 ImGui::PopStyleVar(2); 5731 ImGui::PopID(); 5732 5733 if (changed) { 5734 if (!hour24) 5735 hr = hr % 12 + ap * 12; 5736 Tm.tm_hour = hr; 5737 Tm.tm_min = min; 5738 Tm.tm_sec = sec; 5739 *t = MkTime(&Tm); 5740 } 5741 5742 return changed; 5743} 5744 5745void StyleColorsAuto(ImPlotStyle* dst) { 5746 ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); 5747 ImVec4* colors = style->Colors; 5748 5749 style->MinorAlpha = 0.25f; 5750 5751 colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; 5752 colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; 5753 colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; 5754 colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; 5755 colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; 5756 colors[ImPlotCol_FrameBg] = IMPLOT_AUTO_COL; 5757 colors[ImPlotCol_PlotBg] = IMPLOT_AUTO_COL; 5758 colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; 5759 colors[ImPlotCol_LegendBg] = IMPLOT_AUTO_COL; 5760 colors[ImPlotCol_LegendBorder] = IMPLOT_AUTO_COL; 5761 colors[ImPlotCol_LegendText] = IMPLOT_AUTO_COL; 5762 colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL; 5763 colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL; 5764 colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; 5765 colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL; 5766 colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL; 5767 colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; 5768 colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; 5769 colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; 5770 colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; 5771 colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL; 5772 colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL; 5773} 5774 5775void StyleColorsClassic(ImPlotStyle* dst) { 5776 ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); 5777 ImVec4* colors = style->Colors; 5778 5779 style->MinorAlpha = 0.5f; 5780 5781 colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; 5782 colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; 5783 colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; 5784 colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; 5785 colors[ImPlotCol_ErrorBar] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 5786 colors[ImPlotCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); 5787 colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); 5788 colors[ImPlotCol_PlotBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); 5789 colors[ImPlotCol_LegendBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); 5790 colors[ImPlotCol_LegendBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); 5791 colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 5792 colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 5793 colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 5794 colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 5795 colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); 5796 colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO 5797 colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO 5798 colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO 5799 colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO 5800 colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f); 5801 colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f); 5802} 5803 5804void StyleColorsDark(ImPlotStyle* dst) { 5805 ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); 5806 ImVec4* colors = style->Colors; 5807 5808 style->MinorAlpha = 0.25f; 5809 5810 colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; 5811 colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; 5812 colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; 5813 colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; 5814 colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; 5815 colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f); 5816 colors[ImPlotCol_PlotBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); 5817 colors[ImPlotCol_PlotBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); 5818 colors[ImPlotCol_LegendBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); 5819 colors[ImPlotCol_LegendBorder] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); 5820 colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5821 colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5822 colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5823 colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5824 colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); 5825 colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO 5826 colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO 5827 colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO 5828 colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO 5829 colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); 5830 colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); 5831} 5832 5833void StyleColorsLight(ImPlotStyle* dst) { 5834 ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); 5835 ImVec4* colors = style->Colors; 5836 5837 style->MinorAlpha = 1.0f; 5838 5839 colors[ImPlotCol_Line] = IMPLOT_AUTO_COL; 5840 colors[ImPlotCol_Fill] = IMPLOT_AUTO_COL; 5841 colors[ImPlotCol_MarkerOutline] = IMPLOT_AUTO_COL; 5842 colors[ImPlotCol_MarkerFill] = IMPLOT_AUTO_COL; 5843 colors[ImPlotCol_ErrorBar] = IMPLOT_AUTO_COL; 5844 colors[ImPlotCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5845 colors[ImPlotCol_PlotBg] = ImVec4(0.42f, 0.57f, 1.00f, 0.13f); 5846 colors[ImPlotCol_PlotBorder] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); 5847 colors[ImPlotCol_LegendBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); 5848 colors[ImPlotCol_LegendBorder] = ImVec4(0.82f, 0.82f, 0.82f, 0.80f); 5849 colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 5850 colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 5851 colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 5852 colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); 5853 colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 5854 colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f); 5855 colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO 5856 colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO 5857 colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO 5858 colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f); 5859 colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); 5860} 5861 5862//----------------------------------------------------------------------------- 5863// [SECTION] Obsolete Functions/Types 5864//----------------------------------------------------------------------------- 5865 5866#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS 5867 5868bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, 5869 ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, 5870 const char* y2_label, const char* y3_label) 5871{ 5872 if (!BeginPlot(title, size, flags)) 5873 return false; 5874 SetupAxis(ImAxis_X1, x_label, x_flags); 5875 SetupAxis(ImAxis_Y1, y1_label, y1_flags); 5876 if (ImHasFlag(flags, ImPlotFlags_YAxis2)) 5877 SetupAxis(ImAxis_Y2, y2_label, y2_flags); 5878 if (ImHasFlag(flags, ImPlotFlags_YAxis3)) 5879 SetupAxis(ImAxis_Y3, y3_label, y3_flags); 5880 return true; 5881} 5882 5883#endif 5884 5885} // namespace ImPlot