implot.cpp 147.7 KB
Newer Older
Evan Pezent's avatar
Evan Pezent 已提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// MIT License

// Copyright (c) 2020 Evan Pezent

// 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.3 WIP
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.

34
- 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.
Evan Pezent's avatar
Evan Pezent 已提交
35
- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`.
36
- 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead.
37
- 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2
38
- 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`
39
40
                     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.
41
42
- 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`
ozlb's avatar
ozlb 已提交
43
- 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made:
44
                     - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`.
ozlb's avatar
ozlb 已提交
45
                       It should be fairly obvious what was what.
Evan Pezent's avatar
Evan Pezent 已提交
46
47
                     - 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'.
48
49
50
- 2020/05/10 (0.2) - The following function/struct names were changes:
                    - ImPlotRange       -> ImPlotLimits
                    - GetPlotRange()    -> GetPlotLimits()
Evan Pezent's avatar
Evan Pezent 已提交
51
                    - SetNextPlotRange  -> SetNextPlotLimits
52
53
54
55
56
57
                    - 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.

*/

Evan Pezent's avatar
Evan Pezent 已提交
58
59
60
61
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif

62
63
64
65
66
#include "implot.h"
#include "imgui_internal.h"
#ifdef _MSC_VER
#define sprintf sprintf_s
#endif
Evan Pezent's avatar
Evan Pezent 已提交
67
68
69
70
71
72
73
74
75
76
77
78
79

#define IM_NORMALIZE2F_OVER_ZERO(VX, VY)                                                           \
    {                                                                                              \
        float d2 = VX * VX + VY * VY;                                                              \
        if (d2 > 0.0f) {                                                                           \
            float inv_len = 1.0f / ImSqrt(d2);                                                     \
            VX *= inv_len;                                                                         \
            VY *= inv_len;                                                                         \
        }                                                                                          \
    }

// Special Color used to specific that a plot item color should set determined automatically.
#define IM_COL_AUTO ImVec4(0,0,0,-1)
80
81
// The maximum number of support y-axes
#define MAX_Y_AXES 3
Evan Pezent's avatar
Evan Pezent 已提交
82

83
84
85
// static inline float  ImLog10(float x)  { return log10f(x); }
static inline double ImLog10(double x) { return log10(x); }

Evan Pezent's avatar
Evan Pezent 已提交
86
87
ImPlotStyle::ImPlotStyle() {
    LineWeight = 1;
88
    Marker = ImPlotMarker_None;
89
    MarkerSize = 4;
Evan Pezent's avatar
Evan Pezent 已提交
90
91
92
    MarkerWeight = 1;
    ErrorBarSize = 5;
    ErrorBarWeight = 1.5;
ozlb's avatar
ozlb 已提交
93
    DigitalBitHeight = 8;
94
    DigitalBitGap = 4;
Evan Pezent's avatar
Evan Pezent 已提交
95
96
97
98
99
100
101
102
103
104
105

    Colors[ImPlotCol_Line]          = IM_COL_AUTO;
    Colors[ImPlotCol_Fill]          = IM_COL_AUTO;
    Colors[ImPlotCol_MarkerOutline] = IM_COL_AUTO;
    Colors[ImPlotCol_MarkerFill]    = IM_COL_AUTO;
    Colors[ImPlotCol_ErrorBar]      = IM_COL_AUTO;
    Colors[ImPlotCol_FrameBg]       = IM_COL_AUTO;
    Colors[ImPlotCol_PlotBg]        = IM_COL_AUTO;
    Colors[ImPlotCol_PlotBorder]    = IM_COL_AUTO;
    Colors[ImPlotCol_XAxis]         = IM_COL_AUTO;
    Colors[ImPlotCol_YAxis]         = IM_COL_AUTO;
106
107
    Colors[ImPlotCol_YAxis2]        = IM_COL_AUTO;
    Colors[ImPlotCol_YAxis3]        = IM_COL_AUTO;
Evan Pezent's avatar
Evan Pezent 已提交
108
    Colors[ImPlotCol_Selection]     = ImVec4(1,1,0,1);
ozlb's avatar
Curors    
ozlb 已提交
109
    Colors[ImPlotCol_Query]         = ImVec4(0,1,0,1);
110
111
}

Evan Pezent's avatar
Evan Pezent 已提交
112
113
114
115
ImPlotRange::ImPlotRange() {
    Min = NAN;
    Max = NAN;
}
116

117
bool ImPlotRange::Contains(double v) const {
118
    return v >= Min && v <= Max;
119
120
}

121
double ImPlotRange::Size() const {
122
123
124
125
126
    return Max - Min;
}

ImPlotLimits::ImPlotLimits() {}

Evan Pezent's avatar
Evan Pezent 已提交
127
128
129
130
bool ImPlotLimits::Contains(const ImPlotPoint& p) const {
    return Contains(p.x, p.y);
}

131
132
bool ImPlotLimits::Contains(double x, double y) const {
    return X.Contains(x) && Y.Contains(y);
Evan Pezent's avatar
Evan Pezent 已提交
133
134
}

ozlb's avatar
ozlb 已提交
135
namespace ImPlot {
Evan Pezent's avatar
Evan Pezent 已提交
136
137
138

namespace {

139
140
141
//-----------------------------------------------------------------------------
// Private Utils
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
142

Evan Pezent's avatar
Evan Pezent 已提交
143
144
145
146
147
148
149
150
151
152
template <int Count>
struct OffsetCalculator {
    OffsetCalculator(int* sizes) {
        Offsets[0] = 0;
        for (int i = 1; i < Count; ++i)
            Offsets[i] = Offsets[i-1] + sizes[i-1];
    }
    int Offsets[Count];
};

Evan Pezent's avatar
Evan Pezent 已提交
153
154
155
156
157
158
159
160
161
template <typename T>
void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) {
    buffer.resize(n);
    T step = (vmax - vmin) / (n - 1);
    for (int i = 0; i < n; ++i) {
        buffer[i] = vmin + i * step;
    }
}

162
// Returns true if a flag is set
Evan Pezent's avatar
Evan Pezent 已提交
163
164
165
166
167
template <typename TSet, typename TFlag>
inline bool HasFlag(TSet set, TFlag flag) {
    return (set & flag) == flag;
}

168
// Flips a flag in a flagset
Evan Pezent's avatar
Evan Pezent 已提交
169
template <typename TSet, typename TFlag>
Evan Pezent's avatar
Evan Pezent 已提交
170
171
172
173
inline void FlipFlag(TSet& set, TFlag flag) {
    HasFlag(set, flag) ? set &= ~flag : set |= flag;
}

174
// Linearly remaps x from [x0 x1] to [y0 y1].
175
176
template <typename T>
inline T Remap(T x, T x0, T x1, T y0, T y1) {
Evan Pezent's avatar
Evan Pezent 已提交
177
178
179
    return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}

180
// Turns NANs to 0s
181
inline double ConstrainNan(double val) {
182
    return isnan(val) ? 0 : val;
Evan Pezent's avatar
Evan Pezent 已提交
183
184
}

185
// Turns infinity to floating point maximums
186
187
inline double ConstrainInf(double val) {
    return val == HUGE_VAL ?  DBL_MAX : val == -HUGE_VAL ? - DBL_MAX : val;
Evan Pezent's avatar
Evan Pezent 已提交
188
189
}

190
// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?)
191
inline double ConstrainLog(double val) {
192
    return val <= 0 ? 0.001f : val;
Evan Pezent's avatar
Evan Pezent 已提交
193
194
}

195
// Returns true if val is NAN or INFINITY
196
197
inline bool NanOrInf(double val) {
    return val == HUGE_VAL || val == -HUGE_VAL || isnan(val);
Evan Pezent's avatar
Evan Pezent 已提交
198
199
}

200
201
202
203
204
205
206
207
208
209
210
// Computes order of magnitude of double.
// inline int OrderOfMagnitude(double val) {
//     return val == 0 ? 0 : (int)(floor(log10(fabs(val))));
// }

// Returns the precision required for a order of magnitude.
// inline int OrderToPrecision(int order) {
//     return order > 0 ? 0 : 1 - order;
// }

// Draws vertical text. The position is the bottom left of the text rect.
Evan Pezent's avatar
Evan Pezent 已提交
211
212
213
214
215
inline void AddTextVertical(ImDrawList *DrawList, const char *text, ImVec2 pos, ImU32 text_color) {
    pos.x                   = IM_ROUND(pos.x);
    pos.y                   = IM_ROUND(pos.y);
    ImFont *           font = GImGui->Font;
    const ImFontGlyph *glyph;
216
    for (char c = *text++; c; c = *text++) {
Evan Pezent's avatar
Evan Pezent 已提交
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
        glyph = font->FindGlyph(c);
        if (!glyph)
            continue;

        DrawList->PrimReserve(6, 4);
        DrawList->PrimQuadUV(
            pos + ImVec2(glyph->Y0, -glyph->X0), pos + ImVec2(glyph->Y0, -glyph->X1),
            pos + ImVec2(glyph->Y1, -glyph->X1), pos + ImVec2(glyph->Y1, -glyph->X0),

            ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V0),
            ImVec2(glyph->U1, glyph->V1), ImVec2(glyph->U0, glyph->V1), text_color);
        pos.y -= glyph->AdvanceX;
    }
}

232
// Calculates the size of vertical text
Evan Pezent's avatar
Evan Pezent 已提交
233
inline ImVec2 CalcTextSizeVertical(const char *text) {
ozlb's avatar
ozlb 已提交
234
    ImVec2 sz = ImGui::CalcTextSize(text);
Evan Pezent's avatar
Evan Pezent 已提交
235
236
237
    return ImVec2(sz.y, sz.x);
}

238
239
240
241
242
243
244
245
246
} // private namespace

//-----------------------------------------------------------------------------
// Forwards
//-----------------------------------------------------------------------------

ImVec4 NextColor();

//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
247
// Structs
248
249
//-----------------------------------------------------------------------------

250
// Tick mark info
251
252
struct ImPlotTick {
    ImPlotTick(double value, bool major, bool render_label = true) {
Evan Pezent's avatar
Evan Pezent 已提交
253
254
255
        PlotPos = value;
        Major = major;
        RenderLabel = render_label;
Evan Pezent's avatar
Evan Pezent 已提交
256
        Labeled = false;
Evan Pezent's avatar
Evan Pezent 已提交
257
    }
258
    double PlotPos;
Evan Pezent's avatar
Evan Pezent 已提交
259
260
    float  PixelPos;
    ImVec2 Size;
261
    int    TextOffset;
262
    bool   Major;
263
    bool   RenderLabel;
Evan Pezent's avatar
Evan Pezent 已提交
264
    bool   Labeled;
Evan Pezent's avatar
Evan Pezent 已提交
265
266
};

267
// Axis state information that must persist after EndPlot
Evan Pezent's avatar
Evan Pezent 已提交
268
struct ImPlotAxis {
Evan Pezent's avatar
Evan Pezent 已提交
269
    ImPlotAxis() {
Evan Pezent's avatar
Evan Pezent 已提交
270
        Dragging = false;
271
272
        Range.Min = 0;
        Range.Max = 1;
Evan Pezent's avatar
Evan Pezent 已提交
273
274
275
        Divisions = 3;
        Subdivisions = 10;
        Flags = PreviousFlags = ImPlotAxisFlags_Default;
Evan Pezent's avatar
Evan Pezent 已提交
276
277
    }
    bool Dragging;
278
    ImPlotRange Range;
Evan Pezent's avatar
Evan Pezent 已提交
279
280
    int Divisions;
    int Subdivisions;
281
    ImPlotAxisFlags Flags, PreviousFlags;
Evan Pezent's avatar
Evan Pezent 已提交
282
283
};

284
// Axis state information only needed between BeginPlot/EndPlot
285
struct ImPlotAxisState {
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
    ImPlotAxis* Axis;
    bool HasRange;
    ImGuiCond RangeCond;
    bool Present;
    int PresentSoFar;
    bool Invert;
    bool LockMin;
    bool LockMax;
    bool Lock;
    ImPlotAxisState(ImPlotAxis& axis, bool has_range, ImGuiCond range_cond, bool present, int previous_present) :
        Axis(&axis),
        HasRange(has_range),
        RangeCond(range_cond),
        Present(present),
        PresentSoFar(previous_present + (Present ? 1 : 0)),
        Invert(HasFlag(Axis->Flags, ImPlotAxisFlags_Invert)),
        LockMin(HasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always)),
        LockMax(HasFlag(Axis->Flags, ImPlotAxisFlags_LockMax) || (HasRange && RangeCond == ImGuiCond_Always)),
        Lock(!Present || ((LockMin && LockMax) || (HasRange && RangeCond == ImGuiCond_Always)))
    {}

    ImPlotAxisState() :
        Axis(), HasRange(), RangeCond(), Present(), PresentSoFar(),Invert(),LockMin(), LockMax(), Lock()
    {}
310
311
312
313
314
315
316
};

struct ImPlotAxisColor {
    ImPlotAxisColor() : Major(), Minor(), Txt() {}
    ImU32 Major, Minor, Txt;
};

317
// State information for Plot items
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
struct ImPlotItem {
    ImPlotItem() {
        Show = true;
        Highlight = false;
        Color = NextColor();
        NameOffset = -1;
        ID = 0;
    }
    ~ImPlotItem() { ID = 0; }
    bool Show;
    bool Highlight;
    ImVec4 Color;
    int NameOffset;
    ImGuiID ID;
};

334
// Holds Plot state information that must persist after EndPlot
ozlb's avatar
ozlb 已提交
335
336
struct ImPlotState {
    ImPlotState() {
337
        Selecting = Querying = Queried = DraggingQuery = false;
Evan Pezent's avatar
Evan Pezent 已提交
338
        SelectStart =  QueryStart = ImVec2(0,0);
339
        Flags = PreviousFlags = ImPlotFlags_Default;
Evan Pezent's avatar
Evan Pezent 已提交
340
        ColorIdx = 0;
341
        CurrentYAxis = 0;
Evan Pezent's avatar
Evan Pezent 已提交
342
343
344
345
346
    }
    ImPool<ImPlotItem> Items;

    ImRect BB_Legend;
    ImVec2 SelectStart;
347
    bool Selecting;
Evan Pezent's avatar
Evan Pezent 已提交
348
349
    bool Querying;
    bool Queried;
350
    bool DraggingQuery;
Evan Pezent's avatar
Evan Pezent 已提交
351
    ImVec2 QueryStart;
352
    ImRect QueryRect; // relative to BB_Plot!!
353

Evan Pezent's avatar
Evan Pezent 已提交
354
    ImPlotAxis XAxis;
355
356
357
    ImPlotAxis YAxis[MAX_Y_AXES];

    ImPlotFlags Flags, PreviousFlags;
Evan Pezent's avatar
Evan Pezent 已提交
358
    int ColorIdx;
359
    int CurrentYAxis;
Evan Pezent's avatar
Evan Pezent 已提交
360
361
};

362
363
364
struct ImPlotNextPlotData {
    ImPlotNextPlotData() {
        HasXRange = false;
Evan Pezent's avatar
Evan Pezent 已提交
365
        ShowDefaultTicksX = true;
366
367
        for (int i = 0; i < MAX_Y_AXES; ++i) {
            HasYRange[i] = false;
Evan Pezent's avatar
Evan Pezent 已提交
368
            ShowDefaultTicksY[i] = true;
369
370
        }
    }
Evan Pezent's avatar
Evan Pezent 已提交
371
    ImGuiCond XRangeCond;
372
    ImGuiCond YRangeCond[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
373
    bool HasXRange;
374
375
376
    bool HasYRange[MAX_Y_AXES];
    ImPlotRange X;
    ImPlotRange Y[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
377
378
    bool ShowDefaultTicksX;
    bool ShowDefaultTicksY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
379
380
};

381
// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot()
Evan Pezent's avatar
Evan Pezent 已提交
382
struct ImPlotContext {
383
    ImPlotContext() : RenderX(), RenderY() {
Evan Pezent's avatar
Evan Pezent 已提交
384
385
        ChildWindowMade = false;
        Reset();
Evan Pezent's avatar
Evan Pezent 已提交
386
        SetColormap(ImPlotColormap_Default);
Evan Pezent's avatar
Evan Pezent 已提交
387
    }
388

Evan Pezent's avatar
Evan Pezent 已提交
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
    void Reset() {
        // end child window if it was made
        if (ChildWindowMade)
            ImGui::EndChild();
        ChildWindowMade = false;
        // reset the next plot data
        NextPlotData = ImPlotNextPlotData();
        // reset items count
        VisibleItemCount = 0;
        // reset legend items
        LegendIndices.shrink(0);
        LegendLabels.Buf.shrink(0);
        // reset ticks/labels
        XTicks.shrink(0);
        XTickLabels.Buf.shrink(0);
        for (int i = 0; i < 3; ++i) {
            YTicks[i].shrink(0);
            YTickLabels[i].Buf.shrink(0);
        }
        // reset extents
        FitX = false;
        ExtentsX.Min = HUGE_VAL;
        ExtentsX.Max = -HUGE_VAL;
        for (int i = 0; i < MAX_Y_AXES; i++) {
            ExtentsY[i].Min = HUGE_VAL;
            ExtentsY[i].Max = -HUGE_VAL;
            FitY[i] = false;
        }
        // reset digital plot items count
        DigitalPlotItemCnt = 0;
        DigitalPlotOffset = 0;
        // nullify plot
Evan Pezent's avatar
Evan Pezent 已提交
421
422
        CurrentPlot = NULL;
    }
423

424
    // ALl Plots
ozlb's avatar
ozlb 已提交
425
    ImPool<ImPlotState> Plots;
426
    // Current Plot
ozlb's avatar
ozlb 已提交
427
    ImPlotState* CurrentPlot;
Evan Pezent's avatar
Evan Pezent 已提交
428
    // Legend
Evan Pezent's avatar
Evan Pezent 已提交
429
    ImVector<int> LegendIndices;
430
    ImGuiTextBuffer LegendLabels;
Evan Pezent's avatar
Evan Pezent 已提交
431
    // Bounding regions
Evan Pezent's avatar
Evan Pezent 已提交
432
433
    ImRect BB_Frame;
    ImRect BB_Canvas;
434
    ImRect BB_Plot;
435
    // Cached Colors
Evan Pezent's avatar
Evan Pezent 已提交
436
437
    ImU32 Col_Frame, Col_Bg, Col_Border,
          Col_Txt, Col_TxtDis,
Evan Pezent's avatar
Evan Pezent 已提交
438
          Col_SlctBg, Col_SlctBd,
ozlb's avatar
ozlb 已提交
439
          Col_QryBg, Col_QryBd;
440
441
442
443
    ImPlotAxisColor Col_X;
    ImPlotAxisColor Col_Y[MAX_Y_AXES];
    ImPlotAxisState X;
    ImPlotAxisState Y[MAX_Y_AXES];
444
    // Tick marks
445
    ImVector<ImPlotTick> XTicks,  YTicks[MAX_Y_AXES];
446
447
    ImGuiTextBuffer XTickLabels, YTickLabels[MAX_Y_AXES];
    float AxisLabelReference[MAX_Y_AXES];
448
    // Transformation cache
449
450
    ImRect PixelRange[MAX_Y_AXES];
    // linear scale (slope)
451
452
    double Mx;
    double My[MAX_Y_AXES];
453
    // log scale denominator
454
455
    double LogDenX;
    double LogDenY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
456
    // Data extents
457
458
    ImPlotRange ExtentsX;
    ImPlotRange ExtentsY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
459
    int VisibleItemCount;
460
    bool FitThisFrame; bool FitX;
Evan Pezent's avatar
Evan Pezent 已提交
461
    bool FitY[MAX_Y_AXES];
462
463
    // Hover states
    bool Hov_Frame;
464
    bool Hov_Plot;
Evan Pezent's avatar
Evan Pezent 已提交
465
    // Render flags
466
    bool RenderX, RenderY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
467
468
469
    // Lock info
    bool LockPlot;
    bool ChildWindowMade;
Evan Pezent's avatar
Evan Pezent 已提交
470
    // Mouse pos
Evan Pezent's avatar
Evan Pezent 已提交
471
    ImPlotPoint LastMousePos[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
472
    // Style
Evan Pezent's avatar
Evan Pezent 已提交
473
474
    ImVec4* Colormap;
    int     ColormapSize;
Evan Pezent's avatar
Evan Pezent 已提交
475
476
477
    ImPlotStyle Style;
    ImVector<ImGuiColorMod> ColorModifiers;  // Stack for PushStyleColor()/PopStyleColor()
    ImVector<ImGuiStyleMod> StyleModifiers;  // Stack for PushStyleVar()/PopStyleVar()
478
    ImPlotNextPlotData NextPlotData;
ozlb's avatar
ozlb 已提交
479
480
    // Digital plot item count
    int DigitalPlotItemCnt;
ozlb's avatar
ozlb 已提交
481
    int DigitalPlotOffset;
Evan Pezent's avatar
Evan Pezent 已提交
482
483
};

484
// Global plot context
Evan Pezent's avatar
Evan Pezent 已提交
485
486
static ImPlotContext gp;

487
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
488
// Context Utils
489
490
//-----------------------------------------------------------------------------

491
// Returns the next unused default plot color
492
ImVec4 NextColor() {
Evan Pezent's avatar
Evan Pezent 已提交
493
    ImVec4 col  = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.ColormapSize];
494
495
    gp.CurrentPlot->ColorIdx++;
    return col;
496
}
Evan Pezent's avatar
Evan Pezent 已提交
497

Evan Pezent's avatar
Evan Pezent 已提交
498
inline void FitPoint(const ImPlotPoint& p) {
499
500
    ImPlotRange* extents_x = &gp.ExtentsX;
    ImPlotRange* extents_y = &gp.ExtentsY[gp.CurrentPlot->CurrentYAxis];
501
    if (!NanOrInf(p.x)) {
502
503
        extents_x->Min = p.x < extents_x->Min ? p.x : extents_x->Min;
        extents_x->Max = p.x > extents_x->Max ? p.x : extents_x->Max;
504
505
    }
    if (!NanOrInf(p.y)) {
506
507
        extents_y->Min = p.y < extents_y->Min ? p.y : extents_y->Min;
        extents_y->Max = p.y > extents_y->Max ? p.y : extents_y->Max;
508
509
510
511
512
513
514
515
516
    }
}

//-----------------------------------------------------------------------------
// Coordinate Transforms
//-----------------------------------------------------------------------------

inline void UpdateTransformCache() {
    // get pixels for transforms
517
    for (int i = 0; i < MAX_Y_AXES; i++) {
518
519
520
521
        gp.PixelRange[i] = ImRect(HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.x : gp.BB_Plot.Min.x,
                                  HasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.y : gp.BB_Plot.Max.y,
                                  HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.x : gp.BB_Plot.Max.x,
                                  HasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.y : gp.BB_Plot.Min.y);
522
523
524

        gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size();
    }
525
    gp.LogDenX = ImLog10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min);
526
    for (int i = 0; i < MAX_Y_AXES; i++) {
527
        gp.LogDenY[i] = ImLog10(gp.CurrentPlot->YAxis[i].Range.Max / gp.CurrentPlot->YAxis[i].Range.Min);
528
529
    }
    gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / gp.CurrentPlot->XAxis.Range.Size();
530
}
Evan Pezent's avatar
Evan Pezent 已提交
531

Evan Pezent's avatar
Evan Pezent 已提交
532
inline ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in = -1) {
Evan Pezent's avatar
Evan Pezent 已提交
533
    IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
534
    const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
Evan Pezent's avatar
Evan Pezent 已提交
535
    ImPlotPoint plt;
536
537
    plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min;
    plt.y = (y - gp.PixelRange[y_axis].Min.y) / gp.My[y_axis] + gp.CurrentPlot->YAxis[y_axis].Range.Min;
538
    if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
539
540
        double t = (plt.x - gp.CurrentPlot->XAxis.Range.Min) / gp.CurrentPlot->XAxis.Range.Size();
        plt.x = ImPow(10, t * gp.LogDenX) * gp.CurrentPlot->XAxis.Range.Min;
541
    }
542
    if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
543
544
        double t = (plt.y - gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.CurrentPlot->YAxis[y_axis].Range.Size();
        plt.y = ImPow(10, t * gp.LogDenY[y_axis]) * gp.CurrentPlot->YAxis[y_axis].Range.Min;
545
546
547
548
    }
    return plt;
}

Evan Pezent's avatar
Evan Pezent 已提交
549
ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis) {
Evan Pezent's avatar
Evan Pezent 已提交
550
551
552
553
    return PixelsToPlot(pix.x, pix.y, y_axis);
}

// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
554
inline ImVec2 PlotToPixels(double x, double y, int y_axis_in = -1) {
Evan Pezent's avatar
Evan Pezent 已提交
555
    IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
556
    const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
557
    ImVec2 pix;
558
    if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
559
        double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
Evan Pezent's avatar
Evan Pezent 已提交
560
        x       = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
Evan Pezent's avatar
Evan Pezent 已提交
561
    }
562
    if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
563
        double t = ImLog10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis];
Evan Pezent's avatar
Evan Pezent 已提交
564
        y       = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, (float)t);
565
    }
Evan Pezent's avatar
Evan Pezent 已提交
566
567
    pix.x = (float)(gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min));
    pix.y = (float)(gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min));
568
569
570
    return pix;
}

Evan Pezent's avatar
Evan Pezent 已提交
571
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
Evan Pezent's avatar
Evan Pezent 已提交
572
ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) {
573
    return PlotToPixels(plt.x, plt.y, y_axis);
574
575
}

576
// Transformer functors
Evan Pezent's avatar
Evan Pezent 已提交
577

578
struct TransformerLinLin {
579
    TransformerLinLin(int y_axis) : YAxis(y_axis) {}
580

581
    inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
582
    inline ImVec2 operator()(double x, double y) {
583
584
        return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
                       (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
585
586
    }

587
    int YAxis;
588
};
589

590
struct TransformerLogLin {
591
    TransformerLogLin(int y_axis) : YAxis(y_axis) {}
592

593
    inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
594
595
    inline ImVec2 operator()(double x, double y) {
        double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
Evan Pezent's avatar
Evan Pezent 已提交
596
        x        = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
597
598
        return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
                       (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
599
    }
600

601
    int YAxis;
602
603
};

604
struct TransformerLinLog {
605
    TransformerLinLog(int y_axis) : YAxis(y_axis) {}
606

607
    inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
608
    inline ImVec2 operator()(double x, double y) {
609
610
611
612
        double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis];
        y        = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t);
        return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
                       (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
613
    }
614
    int YAxis;
615
616
};

617
struct TransformerLogLog {
618
    TransformerLogLog(int y_axis) : YAxis(y_axis) {}
619
620

    inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
621
622
623
    inline ImVec2 operator()(double x, double y) {
        double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
        x        = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t);
624
625
626
627
        t        = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis];
        y        = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t);
        return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)),
                       (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) );
628
    }
629

630
    int YAxis;
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
};

//-----------------------------------------------------------------------------
// Legend Utils
//-----------------------------------------------------------------------------

ImPlotItem* RegisterItem(const char* label_id) {
    ImGuiID id = ImGui::GetID(label_id);
    ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id);
    int idx = gp.CurrentPlot->Items.GetIndex(item);
    item->ID = id;
    gp.LegendIndices.push_back(idx);
    item->NameOffset = gp.LegendLabels.size();
    gp.LegendLabels.append(label_id, label_id + strlen(label_id) + 1);
    if (item->Show)
        gp.VisibleItemCount++;
    return item;
}

int GetLegendCount() {
    return gp.LegendIndices.size();
}

ImPlotItem* GetLegendItem(int i) {
    return gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]);
}

const char* GetLegendLabel(int i) {
    ImPlotItem* item  = gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]);
    IM_ASSERT(item->NameOffset != -1 && item->NameOffset < gp.LegendLabels.Buf.Size);
    return gp.LegendLabels.Buf.Data + item->NameOffset;
}

//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
665
// Tick Utils
666
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
667

668
669
// Utility function to that rounds x to powers of 2,5 and 10 for generating axis labels
// Taken from Graphics Gems 1 Chapter 11.2, "Nice Numbers for Graph Labels"
Evan Pezent's avatar
Evan Pezent 已提交
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
inline double NiceNum(double x, bool round) {
    double f;  /* fractional part of x */
    double nf; /* nice, rounded fraction */
    int expv = (int)floor(ImLog10(x));
    f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */
    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 已提交
695
inline void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector<ImPlotTick> &out) {
Evan Pezent's avatar
Evan Pezent 已提交
696
    if (logscale) {
Evan Pezent's avatar
Evan Pezent 已提交
697
        if (range.Min <= 0 || range.Max <= 0)
Evan Pezent's avatar
Evan Pezent 已提交
698
            return;
699
700
        int exp_min = (int)ImLog10(range.Min);
        int exp_max = (int)(ceil(ImLog10(range.Max)));
Evan Pezent's avatar
Evan Pezent 已提交
701
702
703
704
        for (int e = exp_min - 1; e < exp_max + 1; ++e) {
            double major1 = ImPow(10, (double)(e));
            double major2 = ImPow(10, (double)(e + 1));
            double interval = (major2 - major1) / 9;
705
            if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON))
706
                out.push_back(ImPlotTick(major1, true));
Evan Pezent's avatar
Evan Pezent 已提交
707
708
            for (int i = 1; i < 9; ++i) {
                double minor = major1 + i * interval;
709
                if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON))
710
                    out.push_back(ImPlotTick(minor, false, false));
Evan Pezent's avatar
Evan Pezent 已提交
711
712
713
714
            }
        }
    }
    else {
Evan Pezent's avatar
Evan Pezent 已提交
715
716
717
718
        const double nice_range    = NiceNum(range.Size() * 0.99, 0);
        const double interval      = NiceNum(nice_range / (nMajor - 1), 1);
        const double graphmin      = floor(range.Min / interval) * interval;
        const double graphmax      = ceil(range.Max / interval) * interval;
Evan Pezent's avatar
Evan Pezent 已提交
719
        for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {
Evan Pezent's avatar
Evan Pezent 已提交
720
            if (major >= range.Min && major <= range.Max)
721
                out.push_back(ImPlotTick(major, true));
Evan Pezent's avatar
Evan Pezent 已提交
722
723
            for (int i = 1; i < nMinor; ++i) {
                double minor = major + i * interval / nMinor;
Evan Pezent's avatar
Evan Pezent 已提交
724
                if (minor >= range.Min && minor <= range.Max)
725
                    out.push_back(ImPlotTick(minor, false));
Evan Pezent's avatar
Evan Pezent 已提交
726
727
728
729
730
            }
        }
    }
}

731
732
733
734
735
736
737
738
739
740
741
742
743
inline void AddCustomTicks(const double* values, const char** labels, int n, ImVector<ImPlotTick>& ticks, ImGuiTextBuffer& buffer) {
    for (int i = 0; i < n; ++i) {
        ImPlotTick tick(values[i],false);
        tick.TextOffset = buffer.size();
        if (labels != NULL) {
            buffer.append(labels[i], labels[i] + strlen(labels[i]) + 1);
            tick.Size = ImGui::CalcTextSize(labels[i]);
            tick.Labeled = true;
        }
        ticks.push_back(tick);
    }
}

744
inline void LabelTicks(ImVector<ImPlotTick> &ticks, bool scientific, ImGuiTextBuffer& buffer) {
Evan Pezent's avatar
Evan Pezent 已提交
745
    char temp[32];
ozlb's avatar
ozlb 已提交
746
    for (int t = 0; t < ticks.Size; t++) {
747
        ImPlotTick *tk = &ticks[t];
Evan Pezent's avatar
Evan Pezent 已提交
748
        if (tk->RenderLabel && !tk->Labeled) {
ozlb's avatar
ozlb 已提交
749
            tk->TextOffset = buffer.size();
Evan Pezent's avatar
Evan Pezent 已提交
750
            if (scientific)
ozlb's avatar
ozlb 已提交
751
                sprintf(temp, "%.0e", tk->PlotPos);
Evan Pezent's avatar
Evan Pezent 已提交
752
            else
753
                sprintf(temp, "%.10g", tk->PlotPos);
Evan Pezent's avatar
Evan Pezent 已提交
754
            buffer.append(temp, temp + strlen(temp) + 1);
ozlb's avatar
ozlb 已提交
755
            tk->Size = ImGui::CalcTextSize(buffer.Buf.Data + tk->TextOffset);
Evan Pezent's avatar
Evan Pezent 已提交
756
            tk->Labeled = true;
Evan Pezent's avatar
Evan Pezent 已提交
757
758
759
760
        }
    }
}

761
762
763
764
765
inline float MaxTickLabelWidth(ImVector<ImPlotTick>& ticks) {
    float w = 0;
    for (int i = 0; i < ticks.Size; ++i)
        w = ticks[i].Size.x > w ? ticks[i].Size.x : w;
    return w;
766
767
768
769
}

class YPadCalculator {
  public:
770
771
    YPadCalculator(const ImPlotAxisState* axis_states, const float* max_label_widths, float txt_off)
            : ImPlotAxisStates(axis_states), MaxLabelWidths(max_label_widths), TxtOff(txt_off) {}
772
773

    float operator()(int y_axis) {
ozlb's avatar
ozlb 已提交
774
        ImPlotState& plot = *gp.CurrentPlot;
775
        if (!ImPlotAxisStates[y_axis].Present) { return 0; }
776
777
778
        // If we have more than 1 axis present before us, then we need
        // extra space to account for our tick bar.
        float pad_result = 0;
779
        if (ImPlotAxisStates[y_axis].PresentSoFar >= 3) {
780
781
            pad_result += 6.0f;
        }
782
        if (!HasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_TickLabels)) {
783
784
785
786
787
788
789
            return pad_result;
        }
        pad_result += MaxLabelWidths[y_axis] + TxtOff;
        return pad_result;
    }

  private:
790
    const ImPlotAxisState* const ImPlotAxisStates;
791
792
793
    const float* const MaxLabelWidths;
    const float TxtOff;
};
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812

//-----------------------------------------------------------------------------
// Axis Utils
//-----------------------------------------------------------------------------

void UpdateAxisColor(int axis_flag, ImPlotAxisColor* col) {
    const ImVec4 col_Axis = gp.Style.Colors[axis_flag].w == -1 ? ImGui::GetStyle().Colors[ImGuiCol_Text] * ImVec4(1, 1, 1, 0.25f) : gp.Style.Colors[axis_flag];
    col->Major = ImGui::GetColorU32(col_Axis);
    col->Minor = ImGui::GetColorU32(col_Axis * ImVec4(1, 1, 1, 0.25f));
    col->Txt   = ImGui::GetColorU32(ImVec4(col_Axis.x, col_Axis.y, col_Axis.z, 1));
}

struct ImPlotAxisScale {
    ImPlotAxisScale(int y_axis, float tx, float ty, float zoom_rate) {
        Min = PixelsToPlot(gp.BB_Plot.Min - gp.BB_Plot.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), y_axis);
        Max = PixelsToPlot(gp.BB_Plot.Max + gp.BB_Plot.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), y_axis);
    }
    ImPlotPoint Min, Max;
};
813

814
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
815
// BeginPlot()
816
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
817

818
bool BeginPlot(const char* title, const char* x_label, const char* y_label, const ImVec2& size, ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags) {
Evan Pezent's avatar
Evan Pezent 已提交
819
820
821
822
823
824
825
826

    IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!");

    // FRONT MATTER  -----------------------------------------------------------

    ImGuiContext &G      = *GImGui;
    ImGuiWindow * Window = G.CurrentWindow;
    if (Window->SkipItems) {
Evan Pezent's avatar
Evan Pezent 已提交
827
        gp.Reset();
Evan Pezent's avatar
Evan Pezent 已提交
828
829
830
831
832
        return false;
    }

    const ImGuiID     ID       = Window->GetID(title);
    const ImGuiStyle &Style    = G.Style;
Evan Pezent's avatar
Evan Pezent 已提交
833
    const ImGuiIO &   IO       = ImGui::GetIO();
Evan Pezent's avatar
Evan Pezent 已提交
834

Evan Pezent's avatar
Evan Pezent 已提交
835
    bool just_created = gp.Plots.GetByKey(ID) == NULL;
Evan Pezent's avatar
Evan Pezent 已提交
836
    gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID);
ozlb's avatar
ozlb 已提交
837
    ImPlotState &plot = *gp.CurrentPlot;
Evan Pezent's avatar
Evan Pezent 已提交
838

839
840
    plot.CurrentYAxis = 0;

Evan Pezent's avatar
Evan Pezent 已提交
841
    if (just_created) {
842
843
844
845
846
        plot.Flags          = flags;
        plot.XAxis.Flags    = x_flags;
        plot.YAxis[0].Flags = y_flags;
        plot.YAxis[1].Flags = y2_flags;
        plot.YAxis[2].Flags = y3_flags;
Evan Pezent's avatar
Evan Pezent 已提交
847
    }
848
    else {
Evan Pezent's avatar
Evan Pezent 已提交
849
        // TODO: Check which individual flags changed, and only reset those!
850
        // There's probably an easy bit mask trick I'm not aware of.
Evan Pezent's avatar
Evan Pezent 已提交
851
852
        if (flags != plot.PreviousFlags)
            plot.Flags = flags;
853
854
855
856
857
858
859
860
861
862
863
864
865
        if (y_flags != plot.YAxis[0].PreviousFlags)
            plot.YAxis[0].PreviousFlags = y_flags;
        if (y2_flags != plot.YAxis[1].PreviousFlags)
            plot.YAxis[1].PreviousFlags = y2_flags;
        if (y3_flags != plot.YAxis[2].PreviousFlags)
            plot.YAxis[2].PreviousFlags = y3_flags;
    }

    plot.PreviousFlags          = flags;
    plot.XAxis.PreviousFlags    = x_flags;
    plot.YAxis[0].PreviousFlags = y_flags;
    plot.YAxis[1].PreviousFlags = y2_flags;
    plot.YAxis[2].PreviousFlags = y3_flags;
Evan Pezent's avatar
Evan Pezent 已提交
866

867
    // capture scroll with a child region
868
869
    const float default_w = 400;
    const float default_h = 300;
870
    if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) {
871
        ImGui::BeginChild(title, ImVec2(size.x == 0 ? default_w : size.x, size.y == 0 ? default_h : size.y));
872
873
        Window = ImGui::GetCurrentWindow();
        Window->ScrollMax.y = 1.0f;
Evan Pezent's avatar
Evan Pezent 已提交
874
875
876
877
        gp.ChildWindowMade = true;
    }
    else {
        gp.ChildWindowMade = false;
878
879
880
881
    }

    ImDrawList &DrawList = *Window->DrawList;

Evan Pezent's avatar
Evan Pezent 已提交
882
883
884
885
886
    // NextPlotData -----------------------------------------------------------

    if (gp.NextPlotData.HasXRange) {
        if (just_created || gp.NextPlotData.XRangeCond == ImGuiCond_Always)
        {
887
            plot.XAxis.Range = gp.NextPlotData.X;
Evan Pezent's avatar
Evan Pezent 已提交
888
889
890
        }
    }

891
892
893
894
895
896
    for (int i = 0; i < MAX_Y_AXES; i++) {
        if (gp.NextPlotData.HasYRange[i]) {
            if (just_created || gp.NextPlotData.YRangeCond[i] == ImGuiCond_Always)
            {
                plot.YAxis[i].Range = gp.NextPlotData.Y[i];
            }
Evan Pezent's avatar
Evan Pezent 已提交
897
898
899
900
        }
    }

    // AXIS STATES ------------------------------------------------------------
901
902
903
    gp.X    = ImPlotAxisState(plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true, 0);
    gp.Y[0] = ImPlotAxisState(plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true, 0);
    gp.Y[1] = ImPlotAxisState(plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1],
904
                                   HasFlag(plot.Flags, ImPlotFlags_YAxis2), gp.Y[0].PresentSoFar);
905
    gp.Y[2] = ImPlotAxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2],
906
                                   HasFlag(plot.Flags, ImPlotFlags_YAxis3), gp.Y[1].PresentSoFar);
907

908
    gp.LockPlot = gp.X.Lock && gp.Y[0].Lock && gp.Y[1].Lock && gp.Y[2].Lock;
Evan Pezent's avatar
Evan Pezent 已提交
909
910
911

    // CONSTRAINTS ------------------------------------------------------------

912
913
914
915
916
917
    plot.XAxis.Range.Min = ConstrainNan(ConstrainInf(plot.XAxis.Range.Min));
    plot.XAxis.Range.Max = ConstrainNan(ConstrainInf(plot.XAxis.Range.Max));
    for (int i = 0; i < MAX_Y_AXES; i++) {
        plot.YAxis[i].Range.Min = ConstrainNan(ConstrainInf(plot.YAxis[i].Range.Min));
        plot.YAxis[i].Range.Max = ConstrainNan(ConstrainInf(plot.YAxis[i].Range.Max));
    }
Evan Pezent's avatar
Evan Pezent 已提交
918

919
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
920
        plot.XAxis.Range.Min = ConstrainLog(plot.XAxis.Range.Min);
921
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
922
923
        plot.XAxis.Range.Max = ConstrainLog(plot.XAxis.Range.Max);
    for (int i = 0; i < MAX_Y_AXES; i++) {
924
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
925
            plot.YAxis[i].Range.Min = ConstrainLog(plot.YAxis[i].Range.Min);
926
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
927
928
            plot.YAxis[i].Range.Max = ConstrainLog(plot.YAxis[i].Range.Max);
    }
Evan Pezent's avatar
Evan Pezent 已提交
929

930
    if (plot.XAxis.Range.Max <= plot.XAxis.Range.Min)
931
        plot.XAxis.Range.Max = plot.XAxis.Range.Min + DBL_EPSILON;
932
933
    for (int i = 0; i < MAX_Y_AXES; i++) {
        if (plot.YAxis[i].Range.Max <= plot.YAxis[i].Range.Min)
934
            plot.YAxis[i].Range.Max = plot.YAxis[i].Range.Min + DBL_EPSILON;
935
    }
Evan Pezent's avatar
Evan Pezent 已提交
936
937

    // adaptive divisions
938
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Adaptive)) {
Evan Pezent's avatar
Evan Pezent 已提交
939
940
        plot.XAxis.Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth());
        if (plot.XAxis.Divisions < 2)
Evan Pezent's avatar
Evan Pezent 已提交
941
            plot.XAxis.Divisions = 2;
Evan Pezent's avatar
Evan Pezent 已提交
942
    }
943
    for (int i = 0; i < MAX_Y_AXES; i++) {
944
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Adaptive)) {
945
946
947
948
            plot.YAxis[i].Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight());
            if (plot.YAxis[i].Divisions < 2)
                plot.YAxis[i].Divisions = 2;
        }
Evan Pezent's avatar
Evan Pezent 已提交
949
950
951
952
    }

    // COLORS -----------------------------------------------------------------

ozlb's avatar
ozlb 已提交
953
954
955
    gp.Col_Frame  = gp.Style.Colors[ImPlotCol_FrameBg].w     == -1 ? ImGui::GetColorU32(ImGuiCol_FrameBg)    : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_FrameBg]);
    gp.Col_Bg     = gp.Style.Colors[ImPlotCol_PlotBg].w      == -1 ? ImGui::GetColorU32(ImGuiCol_WindowBg)   : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_PlotBg]);
    gp.Col_Border = gp.Style.Colors[ImPlotCol_PlotBorder].w  == -1 ? ImGui::GetColorU32(ImGuiCol_Text, 0.5f) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_PlotBorder]);
Evan Pezent's avatar
Evan Pezent 已提交
956

957
958
959
960
    UpdateAxisColor(ImPlotCol_XAxis, &gp.Col_X);
    UpdateAxisColor(ImPlotCol_YAxis, &gp.Col_Y[0]);
    UpdateAxisColor(ImPlotCol_YAxis2, &gp.Col_Y[1]);
    UpdateAxisColor(ImPlotCol_YAxis3, &gp.Col_Y[2]);
Evan Pezent's avatar
Evan Pezent 已提交
961

ozlb's avatar
ozlb 已提交
962
963
964
965
966
967
    gp.Col_Txt    = ImGui::GetColorU32(ImGuiCol_Text);
    gp.Col_TxtDis = ImGui::GetColorU32(ImGuiCol_TextDisabled);
    gp.Col_SlctBg = ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Selection] * ImVec4(1,1,1,0.25f));
    gp.Col_SlctBd = ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Selection]);
    gp.Col_QryBg =  ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Query] * ImVec4(1,1,1,0.25f));
    gp.Col_QryBd =  ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Query]);
Evan Pezent's avatar
Evan Pezent 已提交
968
969
970
971

    // BB AND HOVER -----------------------------------------------------------

    // frame
972
    const ImVec2 frame_size = ImGui::CalcItemSize(size, default_w, default_h);
Evan Pezent's avatar
Evan Pezent 已提交
973
    gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
ozlb's avatar
ozlb 已提交
974
975
    ImGui::ItemSize(gp.BB_Frame);
    if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) {
Evan Pezent's avatar
Evan Pezent 已提交
976
        gp.Reset();
Evan Pezent's avatar
Evan Pezent 已提交
977
978
        return false;
    }
ozlb's avatar
ozlb 已提交
979
980
    gp.Hov_Frame = ImGui::ItemHoverable(gp.BB_Frame, ID);
    ImGui::RenderFrame(gp.BB_Frame.Min, gp.BB_Frame.Max, gp.Col_Frame, true, Style.FrameRounding);
Evan Pezent's avatar
Evan Pezent 已提交
981
982

    // canvas bb
Evan Pezent's avatar
Evan Pezent 已提交
983
    gp.BB_Canvas = ImRect(gp.BB_Frame.Min + Style.WindowPadding, gp.BB_Frame.Max - Style.WindowPadding);
Evan Pezent's avatar
Evan Pezent 已提交
984

985
986
987
    gp.RenderX = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines) ||
                    HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks) ||
                    HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) &&  plot.XAxis.Divisions > 1;
988
989
    for (int i = 0; i < MAX_Y_AXES; i++) {
        gp.RenderY[i] =
990
                gp.Y[i].Present &&
Evan Pezent's avatar
Evan Pezent 已提交
991
992
                (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) ||
                 HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) ||
993
                 HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) &&  plot.YAxis[i].Divisions > 1;
994
    }
Evan Pezent's avatar
Evan Pezent 已提交
995
996

    // get ticks
Evan Pezent's avatar
Evan Pezent 已提交
997
998
    if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX)
        AddDefaultTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
999
    for (int i = 0; i < MAX_Y_AXES; i++) {
Evan Pezent's avatar
Evan Pezent 已提交
1000
        if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) {
为了加快浏览速度,不会显示所有历史记录。 查看完整的 blame