implot.cpp 267.3 KB
Newer Older
Evan Pezent's avatar
Evan Pezent 已提交
1
2
// MIT License

3
// Copyright (c) 2023 Evan Pezent
Evan Pezent's avatar
Evan Pezent 已提交
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

23
// ImPlot v0.17
24
25
26
27
28
29
30
31
32
33

/*

API BREAKING CHANGES
====================
Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
Below 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.
When 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.
You can read releases logs https://github.com/epezent/implot/releases for more details.

ocornut's avatar
ocornut 已提交
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.
ocornut's avatar
ocornut 已提交
35
36
- 2023/06/26 (0.15) - Various build fixes related to updates in Dear ImGui internals.
- 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
41
42
43
44
45
46
47
48
49
50
51
                      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.
                    - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead
                    - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal
                    - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags
                    - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags
                    - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical`
                    - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal )
                    - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks
                    - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags
                    - ImPlotFormatters should now return an int indicating the size written
                    - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks
- 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272
Evan Pezent's avatar
Evan Pezent 已提交
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
                    - TRIVIAL RENAME:
                      - ImPlotLimits                              -> ImPlotRect
                      - ImPlotYAxis_                              -> ImAxis_
                      - SetPlotYAxis                              -> SetAxis
                      - BeginDragDropTarget                       -> BeginDragDropTargetPlot
                      - BeginDragDropSource                       -> BeginDragDropSourcePlot
                      - ImPlotFlags_NoMousePos                    -> ImPlotFlags_NoMouseText
                      - SetNextPlotLimits                         -> SetNextAxesLimits
                      - SetMouseTextLocation                      -> SetupMouseText
                    - SIGNATURE MODIFIED:
                      - PixelsToPlot/PlotToPixels                 -> added optional X-Axis arg
                      - GetPlotMousePos                           -> added optional X-Axis arg
                      - GetPlotLimits                             -> added optional X-Axis arg
                      - GetPlotSelection                          -> added optional X-Axis arg
                      - DragLineX/Y/DragPoint                     -> now takes int id; removed labels (render with Annotation/Tag instead)
                    - REPLACED:
                      - IsPlotXAxisHovered/IsPlotXYAxisHovered    -> IsAxisHovered(ImAxis)
                      - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis)
                      - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis)
                      - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc.   -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes)
                      - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes)
                      - SetNextPlotLimitsX/Y                      -> SetNextAxisLimits(ImAxis)
                      - LinkNextPlotLimits                        -> SetNextAxisLinks(ImAxis)
                      - FitNextPlotAxes                           -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit
                      - SetLegendLocation                         -> SetupLegend
                      - ImPlotFlags_NoHighlight                   -> ImPlotLegendFlags_NoHighlight
                      - ImPlotOrientation                         -> ImPlotLegendFlags_Horizontal
                      - Annotate                                  -> Annotation
                    - REMOVED:
                      - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect
                      - SetNextPlotTicksX, SetNextPlotTicksY      -> use SetupAxisTicks
                      - SetNextPlotFormatX, SetNextPlotFormatY    -> use SetupAxisFormat
                      - AnnotateClamped                           -> use Annotation(bool clamp = true)
                    - OBSOLETED:
                      - 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.
Evan Pezent's avatar
Evan Pezent 已提交
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
- 2021/03/08 (0.9)  - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap.
                      ShowColormapScale was changed to ColormapScale and requires additional arguments.
- 2021/03/07 (0.9)  - The signature of ShowColormapScale was modified to accept a ImVec2 size.
- 2021/02/28 (0.9)  - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements.
- 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
                      to implot_internal.h due to its immaturity.
- 2020/10/16 (0.8)  - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding
- 2020/09/10 (0.8)  - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0.
- 2020/09/07 (0.8)  - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG)
- 2020/09/06 (0.7)  - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset
                      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).
- 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.
- 2020/08/25 (0.5)  - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation.
- 2020/08/17 (0.5)  - PlotText was changed so that text is centered horizontally and vertically about the desired point.
- 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.
- 2020/06/13 (0.4)  - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default.
- 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.
- 2020/06/01 (0.3)  - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`.
- 2020/05/31 (0.3)  - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead.
- 2020/05/29 (0.3)  - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2
- 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`
                      and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate
                      that multiple bars will be plotted.
- 2020/05/13 (0.2)  - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`.
- 2020/05/11 (0.2)  - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect`
- 2020/05/11 (0.2)  - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made:
                      - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`.
                        It should be fairly obvious what was what.
                      - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent
                        style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'.
- 2020/05/10 (0.2)  - The following function/struct names were changes:
                     - ImPlotRange       -> ImPlotLimits
                     - GetPlotRange()    -> GetPlotLimits()
                     - SetNextPlotRange  -> SetNextPlotLimits
                     - SetNextPlotRangeX -> SetNextPlotLimitsX
                     - SetNextPlotRangeY -> SetNextPlotLimitsY
- 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
131
#include "implot_internal.h"

Evan Pezent's avatar
Evan Pezent 已提交
132
133
#include <stdlib.h>

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
136
137
138
#if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll)
#define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All
#endif

139
140
141
142
143
// Support for pre-1.89.7 versions.
#if (IMGUI_VERSION_NUM < 18966)
#define ImGuiButtonFlags_AllowOverlap ImGuiButtonFlags_AllowItemOverlap
#endif

Evan Pezent's avatar
Evan Pezent 已提交
144
145
// Visual Studio warnings
#ifdef _MSC_VER
146
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
Evan Pezent's avatar
Evan Pezent 已提交
147
148
149
150
151
152
153
154
155
#endif

// Clang/GCC warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wformat-nonliteral"  // warning: format string is not a string literal
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wformat-nonliteral"    // warning: format not a string literal, format string not checked
#endif

156
// Global plot context
157
#ifndef GImPlot
158
ImPlotContext* GImPlot = nullptr;
159
#endif
Evan Pezent's avatar
spaces    
Evan Pezent 已提交
160

161
162
163
//-----------------------------------------------------------------------------
// Struct Implementations
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
164

epezent's avatar
epezent 已提交
165
ImPlotInputMap::ImPlotInputMap() {
Evan Pezent's avatar
Evan Pezent 已提交
166
    ImPlot::MapInputDefault(this);
epezent's avatar
epezent 已提交
167
168
}

Evan Pezent's avatar
Evan Pezent 已提交
169
ImPlotStyle::ImPlotStyle() {
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    LineWeight         = 1;
    Marker             = ImPlotMarker_None;
    MarkerSize         = 4;
    MarkerWeight       = 1;
    FillAlpha          = 1;
    ErrorBarSize       = 5;
    ErrorBarWeight     = 1.5f;
    DigitalBitHeight   = 8;
    DigitalBitGap      = 4;

    PlotBorderSize     = 1;
    MinorAlpha         = 0.25f;
    MajorTickLen       = ImVec2(10,10);
    MinorTickLen       = ImVec2(5,5);
    MajorTickSize      = ImVec2(1,1);
    MinorTickSize      = ImVec2(1,1);
    MajorGridSize      = ImVec2(1,1);
    MinorGridSize      = ImVec2(1,1);
    PlotPadding        = ImVec2(10,10);
    LabelPadding       = ImVec2(5,5);
    LegendPadding      = ImVec2(10,10);
    LegendInnerPadding = ImVec2(5,5);
193
    LegendSpacing      = ImVec2(5,0);
194
195
    MousePosPadding    = ImVec2(10,10);
    AnnotationPadding  = ImVec2(2,2);
196
    FitPadding         = ImVec2(0,0);
197
    PlotDefaultSize    = ImVec2(400,300);
Evan Pezent's avatar
Evan Pezent 已提交
198
    PlotMinSize        = ImVec2(200,150);
Evan Pezent's avatar
Evan Pezent 已提交
199

epezent's avatar
epezent 已提交
200
    ImPlot::StyleColorsAuto(this);
epezent's avatar
epezent 已提交
201

202
203
    Colormap = ImPlotColormap_Deep;

epezent's avatar
epezent 已提交
204
    UseLocalTime     = false;
epezent's avatar
epezent 已提交
205
    Use24HourClock   = false;
206
    UseISO8601       = false;
207
208
}

209
210
211
212
//-----------------------------------------------------------------------------
// Style
//-----------------------------------------------------------------------------

epezent's avatar
epezent 已提交
213
214
215
namespace ImPlot {

const char* GetStyleColorName(ImPlotCol col) {
Evan Pezent's avatar
Evan Pezent 已提交
216
    static const char* col_names[ImPlotCol_COUNT] = {
epezent's avatar
epezent 已提交
217
218
219
220
221
222
223
224
225
226
227
228
229
        "Line",
        "Fill",
        "MarkerOutline",
        "MarkerFill",
        "ErrorBar",
        "FrameBg",
        "PlotBg",
        "PlotBorder",
        "LegendBg",
        "LegendBorder",
        "LegendText",
        "TitleText",
        "InlayText",
Evan Pezent's avatar
Evan Pezent 已提交
230
231
        "AxisText",
        "AxisGrid",
Evan Pezent's avatar
Evan Pezent 已提交
232
        "AxisTick",
Evan Pezent's avatar
Evan Pezent 已提交
233
234
235
        "AxisBg",
        "AxisBgHovered",
        "AxisBgActive",
epezent's avatar
epezent 已提交
236
237
238
239
240
241
        "Selection",
        "Crosshairs"
    };
    return col_names[col];
}

242
243
244
245
246
const char* GetMarkerName(ImPlotMarker marker) {
    switch (marker) {
        case ImPlotMarker_None:     return "None";
        case ImPlotMarker_Circle:   return "Circle";
        case ImPlotMarker_Square:   return "Square";
247
248
249
250
251
252
253
254
        case ImPlotMarker_Diamond:  return "Diamond";
        case ImPlotMarker_Up:       return "Up";
        case ImPlotMarker_Down:     return "Down";
        case ImPlotMarker_Left:     return "Left";
        case ImPlotMarker_Right:    return "Right";
        case ImPlotMarker_Cross:    return "Cross";
        case ImPlotMarker_Plus:     return "Plus";
        case ImPlotMarker_Asterisk: return "Asterisk";
255
256
257
258
        default:                    return "";
    }
}

epezent's avatar
epezent 已提交
259
260
261
262
263
264
265
266
267
268
ImVec4 GetAutoColor(ImPlotCol idx) {
    ImVec4 col(0,0,0,1);
    switch(idx) {
        case ImPlotCol_Line:          return col; // these are plot dependent!
        case ImPlotCol_Fill:          return col; // these are plot dependent!
        case ImPlotCol_MarkerOutline: return col; // these are plot dependent!
        case ImPlotCol_MarkerFill:    return col; // these are plot dependent!
        case ImPlotCol_ErrorBar:      return ImGui::GetStyleColorVec4(ImGuiCol_Text);
        case ImPlotCol_FrameBg:       return ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
        case ImPlotCol_PlotBg:        return ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
epezent's avatar
epezent 已提交
269
        case ImPlotCol_PlotBorder:    return ImGui::GetStyleColorVec4(ImGuiCol_Border);
epezent's avatar
epezent 已提交
270
271
272
273
274
        case ImPlotCol_LegendBg:      return ImGui::GetStyleColorVec4(ImGuiCol_PopupBg);
        case ImPlotCol_LegendBorder:  return GetStyleColorVec4(ImPlotCol_PlotBorder);
        case ImPlotCol_LegendText:    return GetStyleColorVec4(ImPlotCol_InlayText);
        case ImPlotCol_TitleText:     return ImGui::GetStyleColorVec4(ImGuiCol_Text);
        case ImPlotCol_InlayText:     return ImGui::GetStyleColorVec4(ImGuiCol_Text);
Evan Pezent's avatar
Evan Pezent 已提交
275
276
        case ImPlotCol_AxisText:      return ImGui::GetStyleColorVec4(ImGuiCol_Text);
        case ImPlotCol_AxisGrid:      return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f);
Evan Pezent's avatar
Evan Pezent 已提交
277
        case ImPlotCol_AxisTick:      return GetStyleColorVec4(ImPlotCol_AxisGrid);
Evan Pezent's avatar
Evan Pezent 已提交
278
279
280
        case ImPlotCol_AxisBg:        return ImVec4(0,0,0,0);
        case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
        case ImPlotCol_AxisBgActive:  return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
epezent's avatar
epezent 已提交
281
282
283
284
        case ImPlotCol_Selection:     return ImVec4(1,1,0,1);
        case ImPlotCol_Crosshairs:    return GetStyleColorVec4(ImPlotCol_PlotBorder);
        default: return col;
    }
285
286
}

epezent's avatar
epezent 已提交
287
288
289
290
291
292
293
294
295
struct ImPlotStyleVarInfo {
    ImGuiDataType   Type;
    ImU32           Count;
    ImU32           Offset;
    void*           GetVarPtr(ImPlotStyle* style) const { return (void*)((unsigned char*)style + Offset); }
};

static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
{
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, LineWeight)         }, // ImPlotStyleVar_LineWeight
    { ImGuiDataType_S32,   1, (ImU32)offsetof(ImPlotStyle, Marker)             }, // ImPlotStyleVar_Marker
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerSize)         }, // ImPlotStyleVar_MarkerSize
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MarkerWeight)       }, // ImPlotStyleVar_MarkerWeight
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, FillAlpha)          }, // ImPlotStyleVar_FillAlpha
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarSize)       }, // ImPlotStyleVar_ErrorBarSize
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, ErrorBarWeight)     }, // ImPlotStyleVar_ErrorBarWeight
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitHeight)   }, // ImPlotStyleVar_DigitalBitHeight
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, DigitalBitGap)      }, // ImPlotStyleVar_DigitalBitGap

    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, PlotBorderSize)     }, // ImPlotStyleVar_PlotBorderSize
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlotStyle, MinorAlpha)         }, // ImPlotStyleVar_MinorAlpha
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickLen)       }, // ImPlotStyleVar_MajorTickLen
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickLen)       }, // ImPlotStyleVar_MinorTickLen
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorTickSize)      }, // ImPlotStyleVar_MajorTickSize
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorTickSize)      }, // ImPlotStyleVar_MinorTickSize
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MajorGridSize)      }, // ImPlotStyleVar_MajorGridSize
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MinorGridSize)      }, // ImPlotStyleVar_MinorGridSize
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotPadding)        }, // ImPlotStyleVar_PlotPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LabelPadding)       }, // ImPlotStyleVar_LabelPaddine
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendPadding)      }, // ImPlotStyleVar_LegendPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, LegendSpacing)      }, // ImPlotStyleVar_LegendSpacing

    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, MousePosPadding)    }, // ImPlotStyleVar_MousePosPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, AnnotationPadding)  }, // ImPlotStyleVar_AnnotationPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, FitPadding)         }, // ImPlotStyleVar_FitPadding
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotDefaultSize)    }, // ImPlotStyleVar_PlotDefaultSize
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlotStyle, PlotMinSize)        }  // ImPlotStyleVar_PlotMinSize
epezent's avatar
epezent 已提交
325
326
327
328
329
330
331
};

static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
    IM_ASSERT(idx >= 0 && idx < ImPlotStyleVar_COUNT);
    IM_ASSERT(IM_ARRAYSIZE(GPlotStyleVarInfo) == ImPlotStyleVar_COUNT);
    return &GPlotStyleVarInfo[idx];
}
Evan Pezent's avatar
Evan Pezent 已提交
332

333
//-----------------------------------------------------------------------------
334
// Generic Helpers
335
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
336

337
void 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
340
341
342
    if (!text_end)
        text_end = text_begin + strlen(text_begin);
    ImGuiContext& g = *GImGui;
    ImFont* font = g.Font;
343
    // Align to be pixel perfect
344
345
    pos.x = ImFloor(pos.x);
    pos.y = ImFloor(pos.y);
346
    const float scale = g.FontSize / font->FontSize;
347
348
349
350
351
352
    const char* s = text_begin;
    int chars_exp = (int)(text_end - s);
    int chars_rnd = 0;
    const int vtx_count_max = chars_exp * 4;
    const int idx_count_max = chars_exp * 6;
    DrawList->PrimReserve(idx_count_max, vtx_count_max);
353
354
355
356
357
358
359
360
361
362
363
    while (s < text_end) {
        unsigned int c = (unsigned int)*s;
        if (c < 0x80) {
            s += 1;
        }
        else {
            s += ImTextCharFromUtf8(&c, s, text_end);
            if (c == 0) // Malformed UTF-8?
                break;
        }
        const ImFontGlyph * glyph = font->FindGlyph((ImWchar)c);
364
        if (glyph == nullptr) {
Evan Pezent's avatar
Evan Pezent 已提交
365
            continue;
366
        }
367
368
369
370
371
372
        DrawList->PrimQuadUV(pos + ImVec2(glyph->Y0, -glyph->X0) * scale, pos + ImVec2(glyph->Y0, -glyph->X1) * scale,
                             pos + ImVec2(glyph->Y1, -glyph->X1) * scale, pos + ImVec2(glyph->Y1, -glyph->X0) * scale,
                             ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0),
                             ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1),
                             col);
        pos.y -= glyph->AdvanceX * scale;
373
        chars_rnd++;
Evan Pezent's avatar
Evan Pezent 已提交
374
    }
375
376
377
    // Give back unused vertices
    int chars_skp = chars_exp-chars_rnd;
    DrawList->PrimUnreserve(chars_skp*6, chars_skp*4);
Evan Pezent's avatar
Evan Pezent 已提交
378
379
}

Evan Pezent's avatar
Evan Pezent 已提交
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) {
    float txt_ht = ImGui::GetTextLineHeight();
    const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end);
    ImVec2 text_size;
    float  y = 0;
    while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) {
        text_size = ImGui::CalcTextSize(text_begin,tmp,true);
        DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp);
        text_begin = tmp + 1;
        y += txt_ht;
    }
    text_size = ImGui::CalcTextSize(text_begin,title_end,true);
    DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end);
}

395
double NiceNum(double x, bool round) {
Evan Pezent's avatar
Evan Pezent 已提交
396
397
    double f;
    double nf;
398
    int expv = (int)floor(ImLog10(x));
Evan Pezent's avatar
Evan Pezent 已提交
399
    f = x / ImPow(10.0, (double)expv);
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
    if (round)
        if (f < 1.5)
            nf = 1;
        else if (f < 3)
            nf = 2;
        else if (f < 7)
            nf = 5;
        else
            nf = 10;
    else if (f <= 1)
        nf = 1;
    else if (f <= 2)
        nf = 2;
    else if (f <= 5)
        nf = 5;
    else
        nf = 10;
    return nf * ImPow(10.0, expv);
Evan Pezent's avatar
Evan Pezent 已提交
418
419
}

420
//-----------------------------------------------------------------------------
421
// Context Utils
422
423
//-----------------------------------------------------------------------------

424
425
426
427
void SetImGuiContext(ImGuiContext* ctx) {
    ImGui::SetCurrentContext(ctx);
}

428
429
ImPlotContext* CreateContext() {
    ImPlotContext* ctx = IM_NEW(ImPlotContext)();
430
    Initialize(ctx);
431
    if (GImPlot == nullptr)
432
433
434
435
436
        SetCurrentContext(ctx);
    return ctx;
}

void DestroyContext(ImPlotContext* ctx) {
437
    if (ctx == nullptr)
438
439
        ctx = GImPlot;
    if (GImPlot == ctx)
440
        SetCurrentContext(nullptr);
441
    IM_DELETE(ctx);
442
}
443
444
445
446
447
448
449
450

ImPlotContext* GetCurrentContext() {
    return GImPlot;
}

void SetCurrentContext(ImPlotContext* ctx) {
    GImPlot = ctx;
}
Evan Pezent's avatar
Evan Pezent 已提交
451

452
453
454
#define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual)
#define IM_RGB(r,g,b) IM_COL32(r,g,b,255)

455
void Initialize(ImPlotContext* ctx) {
Evan Pezent's avatar
Evan Pezent 已提交
456
457
458
    ResetCtxForNextPlot(ctx);
    ResetCtxForNextAlignedPlots(ctx);
    ResetCtxForNextSubplot(ctx);
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492

    const ImU32 Deep[]     = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396                        };
    const ImU32 Dark[]     = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409                                    };
    const ImU32 Pastel[]   = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986                                    };
    const ImU32 Paired[]   = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481};
    const ImU32 Viridis[]  = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301            };
    const ImU32 Plasma[]   = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752            };
    const ImU32 Hot[]      = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295            };
    const ImU32 Cool[]     = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015            };
    const ImU32 Pink[]     = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295            };
    const ImU32 Jet[]      = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335            };
    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)};
    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)};
    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)};
    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)};
    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)};
    const ImU32 Greys[]    = {IM_COL32_WHITE, IM_COL32_BLACK                                                                                                                };

    IMPLOT_APPEND_CMAP(Deep, true);
    IMPLOT_APPEND_CMAP(Dark, true);
    IMPLOT_APPEND_CMAP(Pastel, true);
    IMPLOT_APPEND_CMAP(Paired, true);
    IMPLOT_APPEND_CMAP(Viridis, false);
    IMPLOT_APPEND_CMAP(Plasma, false);
    IMPLOT_APPEND_CMAP(Hot, false);
    IMPLOT_APPEND_CMAP(Cool, false);
    IMPLOT_APPEND_CMAP(Pink, false);
    IMPLOT_APPEND_CMAP(Jet, false);
    IMPLOT_APPEND_CMAP(Twilight, false);
    IMPLOT_APPEND_CMAP(RdBu, false);
    IMPLOT_APPEND_CMAP(BrBG, false);
    IMPLOT_APPEND_CMAP(PiYG, false);
    IMPLOT_APPEND_CMAP(Spectral, false);
    IMPLOT_APPEND_CMAP(Greys, false);
493
494
}

Evan Pezent's avatar
Evan Pezent 已提交
495
void ResetCtxForNextPlot(ImPlotContext* ctx) {
epezent's avatar
epezent 已提交
496
    // reset the next plot/item data
497
498
    ctx->NextPlotData.Reset();
    ctx->NextItemData.Reset();
epezent's avatar
epezent 已提交
499
500
    // reset labels
    ctx->Annotations.Reset();
Evan Pezent's avatar
Evan Pezent 已提交
501
    ctx->Tags.Reset();
epezent's avatar
epezent 已提交
502
    // reset extents/fit
Evan Pezent's avatar
Evan Pezent 已提交
503
    ctx->OpenContextThisFrame = false;
504
505
506
507
    // reset digital plot items count
    ctx->DigitalPlotItemCnt = 0;
    ctx->DigitalPlotOffset = 0;
    // nullify plot
508
509
510
    ctx->CurrentPlot  = nullptr;
    ctx->CurrentItem  = nullptr;
    ctx->PreviousItem = nullptr;
511
512
}

Evan Pezent's avatar
Evan Pezent 已提交
513
void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) {
514
515
    ctx->CurrentAlignmentH = nullptr;
    ctx->CurrentAlignmentV = nullptr;
Evan Pezent's avatar
Evan Pezent 已提交
516
517
518
}

void ResetCtxForNextSubplot(ImPlotContext* ctx) {
519
520
521
    ctx->CurrentSubplot      = nullptr;
    ctx->CurrentAlignmentH   = nullptr;
    ctx->CurrentAlignmentV   = nullptr;
Evan Pezent's avatar
Evan Pezent 已提交
522
523
}

524
//-----------------------------------------------------------------------------
525
// Plot Utils
526
527
//-----------------------------------------------------------------------------

528
ImPlotPlot* GetPlot(const char* title) {
529
530
531
532
533
    ImGuiWindow*   Window = GImGui->CurrentWindow;
    const ImGuiID  ID     = Window->GetID(title);
    return GImPlot->Plots.GetByKey(ID);
}

534
ImPlotPlot* GetCurrentPlot() {
535
    return GImPlot->CurrentPlot;
536
537
}

epezent's avatar
epezent 已提交
538
void BustPlotCache() {
539
540
541
    ImPlotContext& gp = *GImPlot;
    gp.Plots.Clear();
    gp.Subplots.Clear();
epezent's avatar
epezent 已提交
542
543
}

544
545
546
547
//-----------------------------------------------------------------------------
// Legend Utils
//-----------------------------------------------------------------------------

548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) {
    ImVec2 pos;
    if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East))
        pos.x = outer_rect.Min.x + pad.x;
    else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East))
        pos.x = outer_rect.Max.x - pad.x - inner_size.x;
    else
        pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f;
    // legend reference point y
    if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South))
        pos.y = outer_rect.Min.y + pad.y;
    else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South))
        pos.y = outer_rect.Max.y - pad.y - inner_size.y;
    else
        pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f;
    pos.x = IM_ROUND(pos.x);
    pos.y = IM_ROUND(pos.y);
    return pos;
}

Evan Pezent's avatar
Evan Pezent 已提交
568
ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) {
569
    // vars
Evan Pezent's avatar
Evan Pezent 已提交
570
    const int   nItems      = items.GetLegendCount();
571
572
573
574
575
576
    const float txt_ht      = ImGui::GetTextLineHeight();
    const float icon_size   = txt_ht;
    // get label max width
    float max_label_width = 0;
    float sum_label_width = 0;
    for (int i = 0; i < nItems; ++i) {
Evan Pezent's avatar
Evan Pezent 已提交
577
        const char* label       = items.GetLegendLabel(i);
578
        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;
579
580
581
582
        max_label_width         = label_width > max_label_width ? label_width : max_label_width;
        sum_label_width        += label_width;
    }
    // calc legend size
Evan Pezent's avatar
Evan Pezent 已提交
583
    const ImVec2 legend_size = vertical ?
584
585
586
587
588
                               ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) :
                               ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht);
    return legend_size;
}

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
bool ClampLegendRect(ImRect& legend_rect, const ImRect& outer_rect, const ImVec2& pad) {
    bool clamped = false;
    ImRect outer_rect_pad(outer_rect.Min + pad, outer_rect.Max - pad);
    if (legend_rect.Min.x < outer_rect_pad.Min.x) {
        legend_rect.Min.x = outer_rect_pad.Min.x;
        clamped = true;
    }
    if (legend_rect.Min.y < outer_rect_pad.Min.y) {
        legend_rect.Min.y = outer_rect_pad.Min.y;
        clamped = true;
    }
    if (legend_rect.Max.x > outer_rect_pad.Max.x) {
        legend_rect.Max.x = outer_rect_pad.Max.x;
        clamped = true;
    }
    if (legend_rect.Max.y > outer_rect_pad.Max.y) {
        legend_rect.Max.y = outer_rect_pad.Max.y;
        clamped = true;
ocornut's avatar
ocornut 已提交
607
    }
608
609
    return clamped;
}
ocornut's avatar
ocornut 已提交
610

611
612
int LegendSortingComp(const void* _a, const void* _b) {
    ImPlotItemGroup* items = GImPlot->SortItems;
Evan Pezent's avatar
Evan Pezent 已提交
613
614
615
616
617
618
619
620
    const int a = *(const int*)_a;
    const int b = *(const int*)_b;
    const char* label_a = items->GetLegendLabel(a);
    const char* label_b = items->GetLegendLabel(b);
    return strcmp(label_a,label_b);
}

bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) {
621
622
623
624
    // vars
    const float txt_ht      = ImGui::GetTextLineHeight();
    const float icon_size   = txt_ht;
    const float icon_shrink = 2;
625
    ImU32 col_txt           = GetStyleColorU32(ImPlotCol_LegendText);
626
    ImU32 col_txt_dis       = ImAlphaU32(col_txt, 0.25f);
627
628
    // render each legend item
    float sum_label_width = 0;
Evan Pezent's avatar
Evan Pezent 已提交
629
    bool any_item_hovered = false;
Evan Pezent's avatar
Evan Pezent 已提交
630
631
632
633

    const int num_items = items.GetLegendCount();
    if (num_items < 1)
        return hovered;
634
    // build render order
635
636
    ImPlotContext& gp = *GImPlot;
    ImVector<int>& indices = gp.TempInt1;
637
638
639
640
    indices.resize(num_items);
    for (int i = 0; i < num_items; ++i)
        indices[i] = i;
    if (ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_Sort) && num_items > 1) {
641
        gp.SortItems = &items;
642
643
644
        qsort(indices.Data, num_items, sizeof(int), LegendSortingComp);
    }
    // render
Evan Pezent's avatar
Evan Pezent 已提交
645
    for (int i = 0; i < num_items; ++i) {
646
        const int idx           = indices[i];
Evan Pezent's avatar
Evan Pezent 已提交
647
648
        ImPlotItem* item        = items.GetLegendItem(idx);
        const char* label       = items.GetLegendLabel(idx);
649
        const float label_width = ImGui::CalcTextSize(label, nullptr, true).x;
Evan Pezent's avatar
Evan Pezent 已提交
650
651
652
        const ImVec2 top_left   = vertical ?
                                  legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) :
                                  legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0);
653
654
655
656
657
658
659
        sum_label_width        += label_width;
        ImRect icon_bb;
        icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink);
        icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink);
        ImRect label_bb;
        label_bb.Min = top_left;
        label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size);
Evan Pezent's avatar
Evan Pezent 已提交
660
        ImU32 col_txt_hl;
661
        ImU32 col_item = ImAlphaU32(item->Color,1);
Evan Pezent's avatar
Evan Pezent 已提交
662

Evan Pezent's avatar
Evan Pezent 已提交
663
        ImRect button_bb(icon_bb.Min, label_bb.Max);
Evan Pezent's avatar
Evan Pezent 已提交
664

665
        ImGui::KeepAliveID(item->ID);
Evan Pezent's avatar
Evan Pezent 已提交
666
667
668
669
670
671
672
673

        bool item_hov = false;
        bool item_hld = false;
        bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons)
                      ? false
                      : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld);

        if (item_clk)
Evan Pezent's avatar
Evan Pezent 已提交
674
675
            item->Show = !item->Show;

Evan Pezent's avatar
Evan Pezent 已提交
676
677
678
679
680
681
682
683

        const bool can_hover = (item_hov)
                             && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem)
                             || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis));

        if (can_hover) {
            item->LegendHoverRect.Min = icon_bb.Min;
            item->LegendHoverRect.Max = label_bb.Max;
684
            item->LegendHovered = true;
Evan Pezent's avatar
Evan Pezent 已提交
685
686
            col_txt_hl = ImMixU32(col_txt, col_item, 64);
            any_item_hovered = true;
687
688
        }
        else {
Evan Pezent's avatar
Evan Pezent 已提交
689
            col_txt_hl = ImGui::GetColorU32(col_txt);
690
        }
Evan Pezent's avatar
Evan Pezent 已提交
691
        ImU32 col_icon;
Evan Pezent's avatar
Evan Pezent 已提交
692
        if (item_hld)
Evan Pezent's avatar
Evan Pezent 已提交
693
            col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);
Evan Pezent's avatar
Evan Pezent 已提交
694
        else if (item_hov)
Evan Pezent's avatar
Evan Pezent 已提交
695
696
697
698
            col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f);
        else
            col_icon = item->Show ? col_item : col_txt_dis;

Evan Pezent's avatar
Evan Pezent 已提交
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)
Evan Pezent's avatar
Evan Pezent 已提交
702
            DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl  : col_txt_dis, label, text_display_end);
703
    }
Evan Pezent's avatar
Evan Pezent 已提交
704
    return hovered && !any_item_hovered;
705
706
707
}

//-----------------------------------------------------------------------------
708
// Locators
709
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
710

711
712
713
static const float TICK_FILL_X = 0.8f;
static const float TICK_FILL_Y = 1.0f;

714
715
716
void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
    if (range.Min == range.Max)
        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);
epezent's avatar
epezent 已提交
720
    const double interval   = NiceNum(nice_range / (nMajor - 1), true);
721
722
    const double graphmin   = floor(range.Min / interval) * interval;
    const double graphmax   = ceil(range.Max / interval) * interval;
723
724
    bool first_major_set    = false;
    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
729
730
731
732
        // is this zero? combat zero formatting issues
        if (major-interval < 0 && major+interval > 0)
            major = 0;
        if (range.Contains(major)) {
            if (!first_major_set) {
733
                first_major_idx = ticker.TickCount();
734
735
                first_major_set = true;
            }
736
            total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize;
737
        }
738
739
        for (int i = 1; i < nMinor; ++i) {
            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
        }
epezent's avatar
epezent 已提交
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
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
            ticker.Ticks[i].ShowLabel = false;
        for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2)
            ticker.Ticks[i].ShowLabel = false;
    }
}

bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) {
    if (range.Min * range.Max > 0) {
        const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers
        double log_min = ImLog10(ImAbs(range.Min));
        double log_max = ImLog10(ImAbs(range.Max));
        double log_a = ImMin(log_min,log_max);
        double log_b = ImMax(log_min,log_max);
        exp_step  = ImMax(1,(int)(log_b - log_a) / nMajor);
        exp_min   = (int)log_a;
        exp_max   = (int)log_b;
        if (exp_step != 1) {
            while(exp_step % 3 != 0)       exp_step++; // make step size multiple of three
            while(exp_min % exp_step != 0) exp_min--;  // decrease exp_min until exp_min + N * exp_step will be 0
        }
        return true;
769
    }
770
    return false;
771
772
}

773
774
void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {
    const double sign = ImSign(range.Max);
775
    for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) {
776
777
        double major1 = sign*ImPow(10, (double)(e));
        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
783
            major1 = sign*ImPow(10, (double)(e+j));
            major2 = sign*ImPow(10, (double)(e+j+1));
784
785
            interval = (major2 - major1) / 9;
            for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) {
Evan Pezent's avatar
Evan Pezent 已提交
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);
epezent's avatar
epezent 已提交
789
            }
Evan Pezent's avatar
Evan Pezent 已提交
790
791
792
793
        }
    }
}

794
795
796
797
798
799
800
801
void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
    int exp_min, exp_max, exp_step;
    if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step))
        AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data);
}

float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) {
    double scaleToPixels = pixels / range.Size();
802
803
804
    double scaleMin      = TransformForward_SymLog(range.Min,nullptr);
    double scaleMax      = TransformForward_SymLog(range.Max,nullptr);
    double s             = TransformForward_SymLog(plt, nullptr);
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
    double t             = (s - scaleMin) / (scaleMax - scaleMin);
    plt                  = range.Min + range.Size() * t;

    return (float)(0 + scaleToPixels * (plt - range.Min));
}

void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) {
    if (range.Min >= -1 && range.Max <= 1) {
        Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data);
    }
    else if (range.Min * range.Max < 0) { // cross zero
        const float pix_min = 0;
        const float pix_max = pixels;
        const float pix_p1  = CalcSymLogPixel(1, range, pixels);
        const float pix_n1  = CalcSymLogPixel(-1, range, pixels);
        int exp_min_p, exp_max_p, exp_step_p;
        int exp_min_n, exp_max_n, exp_step_n;
        CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p);
        CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n);
        int exp_step = ImMax(exp_step_n, exp_step_p);
        ticker.AddTick(0,true,0,true,formatter,formatter_data);
        AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data);
        AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data);
    }
    else {
        Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data);
    }
}

void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) {
epezent's avatar
epezent 已提交
835
    for (int i = 0; i < n; ++i) {
836
        if (labels != nullptr)
837
838
839
            ticker.AddTick(values[i], false, 0, true, labels[i]);
        else
            ticker.AddTick(values[i], false, 0, true, formatter, data);
epezent's avatar
epezent 已提交
840
841
842
843
844
845
846
847
848
    }
}

//-----------------------------------------------------------------------------
// Time Ticks and Utils
//-----------------------------------------------------------------------------

// this may not be thread safe?
static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {
epezent's avatar
epezent 已提交
849
850
851
852
853
854
855
    0.000001,
    0.001,
    1,
    60,
    3600,
    86400,
    2629800,
epezent's avatar
epezent 已提交
856
857
858
859
860
861
862
863
864
865
866
867
    31557600
};

inline ImPlotTimeUnit GetUnitForRange(double range) {
    static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME};
    for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {
        if (range <= cutoffs[i])
            return (ImPlotTimeUnit)i;
    }
    return ImPlotTimeUnit_Yr;
}

868
869
870
871
872
873
874
875
876
877
878
879
inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) {
    if (max_divs < divs[0])
        return 0;
    for (int i = 1; i < size; ++i) {
        if (max_divs < divs[i])
            return step[i-1];
    }
    return step[size-1];
}

inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
    if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) {
epezent's avatar
epezent 已提交
880
881
882
        static const int step[] = {500,250,200,100,50,25,20,10,5,2,1};
        static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000};
        return LowerBoundStep(max_divs, divs, step, 11);
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
    }
    if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) {
        static const int step[] = {30,15,10,5,1};
        static const int divs[] = {2,4,6,12,60};
        return LowerBoundStep(max_divs, divs, step, 5);
    }
    else if (unit == ImPlotTimeUnit_Hr) {
        static const int step[] = {12,6,3,2,1};
        static const int divs[] = {2,4,8,12,24};
        return LowerBoundStep(max_divs, divs, step, 5);
    }
    else if (unit == ImPlotTimeUnit_Day) {
        static const int step[] = {14,7,2,1};
        static const int divs[] = {2,4,14,28};
        return LowerBoundStep(max_divs, divs, step, 4);
    }
    else if (unit == ImPlotTimeUnit_Mo) {
        static const int step[] = {6,3,2,1};
        static const int divs[] = {2,4,6,12};
        return LowerBoundStep(max_divs, divs, step, 4);
    }
    return 0;
}

epezent's avatar
epezent 已提交
907
ImPlotTime MkGmtTime(struct tm *ptm) {
908
909
910
911
912
913
    ImPlotTime t;
#ifdef _WIN32
    t.S = _mkgmtime(ptm);
#else
    t.S = timegm(ptm);
#endif
epezent's avatar
epezent 已提交
914
915
    if (t.S < 0)
        t.S = 0;
916
    return t;
epezent's avatar
epezent 已提交
917
}
918

epezent's avatar
epezent 已提交
919
tm* GetGmtTime(const ImPlotTime& t, tm* ptm)
epezent's avatar
epezent 已提交
920
921
{
#ifdef _WIN32
epezent's avatar
epezent 已提交
922
923
924
  if (gmtime_s(ptm, &t.S) == 0)
    return ptm;
  else
925
    return nullptr;
epezent's avatar
epezent 已提交
926
927
928
929
930
931
932
933
#else
  return gmtime_r(&t.S, ptm);
#endif
}

ImPlotTime MkLocTime(struct tm *ptm) {
    ImPlotTime t;
    t.S = mktime(ptm);
epezent's avatar
epezent 已提交
934
935
    if (t.S < 0)
        t.S = 0;
epezent's avatar
epezent 已提交
936
937
938
939
940
941
942
    return t;
}

tm* GetLocTime(const ImPlotTime& t, tm* ptm) {
#ifdef _WIN32
  if (localtime_s(ptm, &t.S) == 0)
    return ptm;
epezent's avatar
epezent 已提交
943
  else
944
    return nullptr;
epezent's avatar
epezent 已提交
945
#else
epezent's avatar
epezent 已提交
946
    return localtime_r(&t.S, ptm);
epezent's avatar
epezent 已提交
947
948
949
#endif
}

epezent's avatar
epezent 已提交
950
951
952
953
954
955
956
957
958
959
960
961
962
963
inline ImPlotTime MkTime(struct tm *ptm) {
    if (GetStyle().UseLocalTime)
        return MkLocTime(ptm);
    else
        return MkGmtTime(ptm);
}

inline tm* GetTime(const ImPlotTime& t, tm* ptm) {
    if (GetStyle().UseLocalTime)
        return GetLocTime(t,ptm);
    else
        return GetGmtTime(t,ptm);
}

964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) {
    tm& Tm = GImPlot->Tm;

    int yr = year - 1900;
    if (yr < 0)
        yr = 0;

    sec  = sec + us / 1000000;
    us   = us % 1000000;

    Tm.tm_sec  = sec;
    Tm.tm_min  = min;
    Tm.tm_hour = hour;
    Tm.tm_mday = day;
    Tm.tm_mon  = month;
    Tm.tm_year = yr;

    ImPlotTime t = MkTime(&Tm);

    t.Us = us;
    return t;
}

int GetYear(const ImPlotTime& t) {
    tm& Tm = GImPlot->Tm;
    GetTime(t, &Tm);
    return Tm.tm_year + 1900;
}

epezent's avatar
epezent 已提交
993
ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
epezent's avatar
epezent 已提交
994
    tm& Tm = GImPlot->Tm;
epezent's avatar
epezent 已提交
995
    ImPlotTime t_out = t;
epezent's avatar
epezent 已提交
996
    switch(unit) {
epezent's avatar
epezent 已提交
997
998
        case ImPlotTimeUnit_Us:  t_out.Us += count;         break;
        case ImPlotTimeUnit_Ms:  t_out.Us += count * 1000;  break;
epezent's avatar
epezent 已提交
999
1000
        case ImPlotTimeUnit_S:   t_out.S  += count;         break;
        case ImPlotTimeUnit_Min: t_out.S  += count * 60;    break;
为了加快浏览速度,不会显示所有历史记录。 查看完整的 blame