implot.cpp 110.4 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
24
25
26
27
28
29
30
31
32
33
// ImPlot v0.2 WIP

/*

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/05/13 (0.2) - ImMarker was change to ImPlotMarker and ImAxisFlags was changed to ImPlotAxisFlags
ozlb's avatar
ozlb 已提交
35
36
- 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:
Evan Pezent's avatar
Evan Pezent 已提交
37
                     - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBar` is now just `ImPlot::Bar`.
ozlb's avatar
ozlb 已提交
38
                       It should be fairly obvious what was what.
Evan Pezent's avatar
Evan Pezent 已提交
39
40
                     - 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'.
41
42
43
- 2020/05/10 (0.2) - The following function/struct names were changes:
                    - ImPlotRange       -> ImPlotLimits
                    - GetPlotRange()    -> GetPlotLimits()
Evan Pezent's avatar
Evan Pezent 已提交
44
                    - SetNextPlotRange  -> SetNextPlotLimits
45
46
47
48
49
50
                    - 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 已提交
51
52
53
54
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif

55
56
#include "implot.h"
#include "imgui_internal.h"
57

58
59
60
#ifdef _MSC_VER
#define sprintf sprintf_s
#endif
Evan Pezent's avatar
Evan Pezent 已提交
61
62
63
64
65
66
67
68
69
70
71
72
73

#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)
74
75
// The maximum number of support y-axes
#define MAX_Y_AXES 3
Evan Pezent's avatar
Evan Pezent 已提交
76
77
78

ImPlotStyle::ImPlotStyle() {
    LineWeight = 1;
79
    Marker = ImPlotMarker_None;
Evan Pezent's avatar
Evan Pezent 已提交
80
81
82
83
    MarkerSize = 5;
    MarkerWeight = 1;
    ErrorBarSize = 5;
    ErrorBarWeight = 1.5;
ozlb's avatar
ozlb 已提交
84
    DigitalBitHeight = 8;
Evan Pezent's avatar
Evan Pezent 已提交
85
86
87
88
89
90
91
92
93
94
95

    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;
96
97
    Colors[ImPlotCol_YAxis2]        = IM_COL_AUTO;
    Colors[ImPlotCol_YAxis3]        = IM_COL_AUTO;
Evan Pezent's avatar
Evan Pezent 已提交
98
    Colors[ImPlotCol_Selection]     = ImVec4(1,1,0,1);
ozlb's avatar
Curors    
ozlb 已提交
99
    Colors[ImPlotCol_Query]         = ImVec4(0,1,0,1);
100
101
}

102
103
104
105
ImPlotRange::ImPlotRange() : Min(NAN), Max(NAN) {}

bool ImPlotRange::Contains(float v) const {
    return v >= Min && v <= Max;
106
107
}

108
109
110
111
112
113
114
115
float ImPlotRange::Size() const {
    return Max - Min;
}

ImPlotLimits::ImPlotLimits() {}

bool ImPlotLimits::Contains(const ImVec2& p) const {
    return X.Contains(p.x) && Y.Contains(p.y);
Evan Pezent's avatar
Evan Pezent 已提交
116
117
}

ozlb's avatar
ozlb 已提交
118
119
120
121
122
ImVec2 ImPlotLimits::Size() const {
    return ImVec2(X.Size(),Y.Size());
}

namespace ImPlot {
Evan Pezent's avatar
Evan Pezent 已提交
123
124
125

namespace {

126
127
128
//-----------------------------------------------------------------------------
// Private Utils
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
129
130
131
132
133
134
135
136

/// Returns true if a flag is set
template <typename TSet, typename TFlag>
inline bool HasFlag(TSet set, TFlag flag) {
    return (set & flag) == flag;
}

/// Flips a flag in a flagset
Evan Pezent's avatar
Evan Pezent 已提交
137
template <typename TSet, typename TFlag>
Evan Pezent's avatar
Evan Pezent 已提交
138
139
140
141
142
143
144
145
146
inline void FlipFlag(TSet& set, TFlag flag) {
    HasFlag(set, flag) ? set &= ~flag : set |= flag;
}

/// Linearly remaps float x from [x0 x1] to [y0 y1].
inline float Remap(float x, float x0, float x1, float y0, float y1) {
    return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}

147
/// Turns NANs to 0s
Evan Pezent's avatar
Evan Pezent 已提交
148
inline float ConstrainNan(float val) {
149
    return isnan(val) ? 0 : val;
Evan Pezent's avatar
Evan Pezent 已提交
150
151
}

152
/// Turns INFINITYs to FLT_MAXs
Evan Pezent's avatar
Evan Pezent 已提交
153
154
155
156
inline float ConstrainInf(float val) {
    return val == INFINITY ? FLT_MAX : val == -INFINITY ? -FLT_MAX : val;
}

157
/// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?)
Evan Pezent's avatar
Evan Pezent 已提交
158
inline float ConstrainLog(float val) {
159
    return val <= 0 ? 0.001f : val;
Evan Pezent's avatar
Evan Pezent 已提交
160
161
}

162
/// Returns true if val is NAN or INFINITY
Evan Pezent's avatar
Evan Pezent 已提交
163
inline bool NanOrInf(float val) {
164
    return val == INFINITY || val == -INFINITY || isnan(val);
Evan Pezent's avatar
Evan Pezent 已提交
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
}

/// 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"
inline double NiceNum(double x, bool round) {
    double f;  /* fractional part of x */
    double nf; /* nice, rounded fraction */
    int expv = (int)floor(log10(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);
}

/// Draws vertical text. The position is the bottom left of the text rect.
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;
200
    for (char c = *text++; c; c = *text++) {
Evan Pezent's avatar
Evan Pezent 已提交
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
        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;
    }
}

/// Calculates the size of vertical text
inline ImVec2 CalcTextSizeVertical(const char *text) {
ozlb's avatar
ozlb 已提交
218
    ImVec2 sz = ImGui::CalcTextSize(text);
Evan Pezent's avatar
Evan Pezent 已提交
219
220
221
    return ImVec2(sz.y, sz.x);
}

222
223
224
225
226
227
228
229
230
} // private namespace

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

ImVec4 NextColor();

//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
231
// Structs
232
233
//-----------------------------------------------------------------------------

Evan Pezent's avatar
Evan Pezent 已提交
234
235
/// Tick mark info
struct ImTick {
Evan Pezent's avatar
Evan Pezent 已提交
236
    ImTick(double value, bool major, bool render_label = true) {
Evan Pezent's avatar
Evan Pezent 已提交
237
238
239
240
241
242
243
        PlotPos = value;
        Major = major;
        RenderLabel = render_label;
    }
    double PlotPos;
    float  PixelPos;
    ImVec2 Size;
244
245
246
    int    TextOffset;    
    bool   Major;
    bool   RenderLabel;    
Evan Pezent's avatar
Evan Pezent 已提交
247
248
249
};

struct ImPlotItem {
250
    ImPlotItem() {
Evan Pezent's avatar
Evan Pezent 已提交
251
        Show = true;
252
        Highlight = false;
Evan Pezent's avatar
Evan Pezent 已提交
253
254
255
        Color = NextColor();
        NameOffset = -1;
        ID = 0;
256
    }
Evan Pezent's avatar
Evan Pezent 已提交
257
258
    ~ImPlotItem() { ID = 0; }
    bool Show;
259
    bool Highlight;
Evan Pezent's avatar
Evan Pezent 已提交
260
261
262
263
264
265
266
    ImVec4 Color;
    int NameOffset;
    ImGuiID ID;
};

/// Plot axis structure. You shouldn't need to construct this!
struct ImPlotAxis {
Evan Pezent's avatar
Evan Pezent 已提交
267
    ImPlotAxis() {
Evan Pezent's avatar
Evan Pezent 已提交
268
        Dragging = false;
269
270
        Range.Min = 0;
        Range.Max = 1;
Evan Pezent's avatar
Evan Pezent 已提交
271
272
273
        Divisions = 3;
        Subdivisions = 10;
        Flags = PreviousFlags = ImPlotAxisFlags_Default;
Evan Pezent's avatar
Evan Pezent 已提交
274
275
    }
    bool Dragging;
276
    ImPlotRange Range;
Evan Pezent's avatar
Evan Pezent 已提交
277
278
    int Divisions;
    int Subdivisions;
279
    ImPlotAxisFlags Flags, PreviousFlags;
Evan Pezent's avatar
Evan Pezent 已提交
280
281
282
};

/// Holds Plot state information that must persist between frames
ozlb's avatar
ozlb 已提交
283
284
struct ImPlotState {
    ImPlotState() {
285
        Selecting = Querying = Queried = DraggingQuery = false;
Evan Pezent's avatar
Evan Pezent 已提交
286
        SelectStart =  QueryStart = ImVec2(0,0);
287
        Flags = PreviousFlags = ImPlotFlags_Default;
Evan Pezent's avatar
Evan Pezent 已提交
288
        ColorIdx = 0;
289
        CurrentYAxis = 0;
Evan Pezent's avatar
Evan Pezent 已提交
290
291
292
293
294
    }
    ImPool<ImPlotItem> Items;

    ImRect BB_Legend;
    ImVec2 SelectStart;
295
    bool Selecting;
Evan Pezent's avatar
Evan Pezent 已提交
296
297
    bool Querying;
    bool Queried;
298
    bool DraggingQuery;
Evan Pezent's avatar
Evan Pezent 已提交
299
    ImVec2 QueryStart;
300
    ImRect QueryRect; // relative to BB_grid!!
301

Evan Pezent's avatar
Evan Pezent 已提交
302
    ImPlotAxis XAxis;
303
304
305
    ImPlotAxis YAxis[MAX_Y_AXES];

    ImPlotFlags Flags, PreviousFlags;
Evan Pezent's avatar
Evan Pezent 已提交
306
    int ColorIdx;
307
    int CurrentYAxis;
Evan Pezent's avatar
Evan Pezent 已提交
308
309
310
311
};

struct ImNextPlotData {
    ImGuiCond XRangeCond;
312
    ImGuiCond YRangeCond[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
313
    bool HasXRange;
314
315
316
    bool HasYRange[MAX_Y_AXES];
    ImPlotRange X;
    ImPlotRange Y[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
317
318
319
320
};

/// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot()
struct ImPlotContext {
321
    ImPlotContext() : RenderX(), RenderY() {
Evan Pezent's avatar
Evan Pezent 已提交
322
        CurrentPlot = NULL;
323
        FitThisFrame = FitX = false;
ozlb's avatar
ozlb 已提交
324
        RestorePalette();
Evan Pezent's avatar
Evan Pezent 已提交
325
    }
326

Evan Pezent's avatar
Evan Pezent 已提交
327
    /// ALl Plots
ozlb's avatar
ozlb 已提交
328
    ImPool<ImPlotState> Plots;
Evan Pezent's avatar
Evan Pezent 已提交
329
    /// Current Plot
ozlb's avatar
ozlb 已提交
330
    ImPlotState* CurrentPlot;
Evan Pezent's avatar
Evan Pezent 已提交
331
    // Legend
Evan Pezent's avatar
Evan Pezent 已提交
332
    ImVector<int> LegendIndices;
333
    ImGuiTextBuffer LegendLabels;
Evan Pezent's avatar
Evan Pezent 已提交
334
    // Bounding regions
Evan Pezent's avatar
Evan Pezent 已提交
335
336
337
    ImRect BB_Frame;
    ImRect BB_Canvas;
    ImRect BB_Grid;
338
    // Cached Colors
Evan Pezent's avatar
Evan Pezent 已提交
339
340
    ImU32 Col_Frame, Col_Bg, Col_Border,
          Col_Txt, Col_TxtDis,
Evan Pezent's avatar
Evan Pezent 已提交
341
          Col_SlctBg, Col_SlctBd,
ozlb's avatar
ozlb 已提交
342
          Col_QryBg, Col_QryBd;
343
344
345
346
347
348
349
350
351
352
    struct AxisColor {
        AxisColor() : Major(), Minor(), Txt() {}
        ImU32 Major, Minor, Txt;
    };
    AxisColor Col_X;
    AxisColor Col_Y[MAX_Y_AXES];
    // Tick marks
    ImVector<ImTick> XTicks,  YTicks[MAX_Y_AXES];
    ImGuiTextBuffer XTickLabels, YTickLabels[MAX_Y_AXES];
    float AxisLabelReference[MAX_Y_AXES];
353
    // Transformation cache
354
355
356
357
358
359
360
    ImRect PixelRange[MAX_Y_AXES];
    // linear scale (slope)
    float Mx;
    float My[MAX_Y_AXES];
    // log scale denominator
    float LogDenX;
    float LogDenY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
361
    // Data extents
362
363
    ImPlotRange ExtentsX;
    ImPlotRange ExtentsY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
364
    int VisibleItemCount;
365
366
    bool FitThisFrame; bool FitX;
    bool FitY[MAX_Y_AXES] = {};
367
368
    // Hover states
    bool Hov_Frame;
Evan Pezent's avatar
Evan Pezent 已提交
369
    bool Hov_Grid;    
Evan Pezent's avatar
Evan Pezent 已提交
370
    // Render flags
371
    bool RenderX, RenderY[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
372
    // Mouse pos
373
    ImVec2 LastMousePos[MAX_Y_AXES];
Evan Pezent's avatar
Evan Pezent 已提交
374
375
376
377
378
    // Style
    ImVector<ImVec4> ColorMap;
    ImPlotStyle Style;
    ImVector<ImGuiColorMod> ColorModifiers;  // Stack for PushStyleColor()/PopStyleColor()
    ImVector<ImGuiStyleMod> StyleModifiers;  // Stack for PushStyleVar()/PopStyleVar()
Evan Pezent's avatar
Evan Pezent 已提交
379
    ImNextPlotData NextPlotData;
ozlb's avatar
ozlb 已提交
380
381
    // Digital plot item count
    int DigitalPlotItemCnt;
ozlb's avatar
ozlb 已提交
382
    int DigitalPlotOffset;
Evan Pezent's avatar
Evan Pezent 已提交
383
384
385
386
387
};

/// Global plot context
static ImPlotContext gp;

388
389
390
391
392
393
//-----------------------------------------------------------------------------
// Utils
//-----------------------------------------------------------------------------

/// Returns the next unused default plot color
ImVec4 NextColor() {
394
    ImVec4 col  = gp.ColorMap[gp.CurrentPlot->ColorIdx % gp.ColorMap.size()];
395
396
    gp.CurrentPlot->ColorIdx++;
    return col;
397
}
Evan Pezent's avatar
Evan Pezent 已提交
398

399
inline void FitPoint(const ImVec2& p) {
400
401
    ImPlotRange* extents_x = &gp.ExtentsX;
    ImPlotRange* extents_y = &gp.ExtentsY[gp.CurrentPlot->CurrentYAxis];
402
    if (!NanOrInf(p.x)) {
403
404
        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;
405
406
    }
    if (!NanOrInf(p.y)) {
407
408
        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;
409
410
411
412
413
414
415
416
417
418
    }
}

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

inline void UpdateTransformCache() {
    // get pixels for transforms

419
    for (int i = 0; i < MAX_Y_AXES; i++) {
420
421
422
423
        gp.PixelRange[i] = ImRect(HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Grid.Max.x : gp.BB_Grid.Min.x,
                                  HasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Grid.Min.y : gp.BB_Grid.Max.y,
                                  HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Grid.Min.x : gp.BB_Grid.Max.x,
                                  HasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Grid.Max.y : gp.BB_Grid.Min.y);
424
425
426
427
428
429
430
431

        gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size();
    }
    gp.LogDenX = log10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min);
    for (int i = 0; i < MAX_Y_AXES; i++) {
        gp.LogDenY[i] = log10(gp.CurrentPlot->YAxis[i].Range.Max / gp.CurrentPlot->YAxis[i].Range.Min);
    }
    gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / gp.CurrentPlot->XAxis.Range.Size();
432
}
Evan Pezent's avatar
Evan Pezent 已提交
433

434
inline ImVec2 PixelsToPlot(float x, float y, int y_axis_in = -1) {
435
    IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() Needs to be called between BeginPlot() and EndPlot()!");
436
    const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
437
    ImVec2 plt;
438
439
    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;
440
    if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
441
442
        float t = (plt.x - gp.CurrentPlot->XAxis.Range.Min) / gp.CurrentPlot->XAxis.Range.Size();
        plt.x = pow(10.0f, t * gp.LogDenX) * gp.CurrentPlot->XAxis.Range.Min;
443
    }
444
    if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
445
446
        float t = (plt.y - gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.CurrentPlot->YAxis[y_axis].Range.Size();
        plt.y = pow(10.0f, t * gp.LogDenY[y_axis]) * gp.CurrentPlot->YAxis[y_axis].Range.Min;
447
448
449
450
    }
    return plt;
}

Evan Pezent's avatar
Evan Pezent 已提交
451
452
453
454
455
ImVec2 PixelsToPlot(const ImVec2& pix, int y_axis) {
    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.
456
inline ImVec2 PlotToPixels(float x, float y, int y_axis_in = -1) {
457
    IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() Needs to be called between BeginPlot() and EndPlot()!");
458
    const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
459
    ImVec2 pix;
460
    if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
461
462
        float t = log10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
        x       = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, t);
Evan Pezent's avatar
Evan Pezent 已提交
463
    }
464
    if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
465
466
        float t = log10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis];
        y       = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, t);
467
    }
468
469
    pix.x = gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min);
    pix.y = gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min);
470
471
472
    return pix;
}

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

Evan Pezent's avatar
Evan Pezent 已提交
478
479
// Transformer structs

480
struct Plt2PixLinLin {
481
    Plt2PixLinLin(int y_axis_in) : y_axis(y_axis_in) {}
482

483
484
    ImVec2 operator()(const ImVec2& plt) { return (*this)(plt.x, plt.y); }
    ImVec2 operator()(float x, float y) {
ozlb's avatar
ozlb 已提交
485
486
        return ImVec2( gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min),
                 gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min) );
487
488
    }

489
490
    int y_axis;
};
491
492

struct Plt2PixLogLin {
493
494
495
496
497
498
    Plt2PixLogLin(int y_axis_in) : y_axis(y_axis_in) {}

    ImVec2 operator()(const ImVec2& plt) { return (*this)(plt.x, plt.y); }
    ImVec2 operator()(float x, float y) {
        float t = log10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
        x       = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, t);
ozlb's avatar
ozlb 已提交
499
500
        return ImVec2( gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min),
                 gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min) );
501
    }
502
503

    int y_axis;
504
505
506
};

struct Plt2PixLinLog {
507
508
509
510
511
512
    Plt2PixLinLog(int y_axis_in) : y_axis(y_axis_in) {}

    ImVec2 operator()(const ImVec2& plt) { return (*this)(plt.x, plt.y); }
    ImVec2 operator()(float x, float y) {
        float t = log10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis];
        y       = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, t);
ozlb's avatar
ozlb 已提交
513
514
        return ImVec2( gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min),
                 gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min) );
515
    }
516
517

    int y_axis;
518
519
520
};

struct Plt2PixLogLog {
521
522
523
524
525
526
527
528
    Plt2PixLogLog(int y_axis_in) : y_axis(y_axis_in) {}

    ImVec2 operator()(const ImVec2& plt) { return (*this)(plt.x, plt.y); }
    ImVec2 operator()(float x, float y) {
        float t = log10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
        x       = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, t);
        t       = log10(y / gp.CurrentPlot->YAxis[y_axis].Range.Min) / gp.LogDenY[y_axis];
        y       = ImLerp(gp.CurrentPlot->YAxis[y_axis].Range.Min, gp.CurrentPlot->YAxis[y_axis].Range.Max, t);
ozlb's avatar
ozlb 已提交
529
530
        return ImVec2( gp.PixelRange[y_axis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min),
                 gp.PixelRange[y_axis].Min.y + gp.My[y_axis] * (y - gp.CurrentPlot->YAxis[y_axis].Range.Min) );
531
    }
532
533

    int y_axis;
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
};

//-----------------------------------------------------------------------------
// 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 已提交
568
// Tick Utils
569
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
570

571
inline void GetTicks(const ImPlotRange& scale, int nMajor, int nMinor, bool logscale, ImVector<ImTick> &out) {
Evan Pezent's avatar
Evan Pezent 已提交
572
573
    out.shrink(0);
    if (logscale) {
574
        if (scale.Min <= 0 || scale.Max <= 0)
Evan Pezent's avatar
Evan Pezent 已提交
575
            return;
576
577
        int exp_min = (int)(ImFloor(log10(scale.Min)));
        int exp_max = (int)(ImCeil(log10(scale.Max)));
Evan Pezent's avatar
Evan Pezent 已提交
578
579
580
581
        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;
582
            if (major1 >= (scale.Min - FLT_EPSILON) && major1 <= (scale.Max + FLT_EPSILON))
Evan Pezent's avatar
Evan Pezent 已提交
583
584
585
                out.push_back(ImTick(major1, true));
            for (int i = 1; i < 9; ++i) {
                double minor = major1 + i * interval;
586
                if (minor >= (scale.Min - FLT_EPSILON) && minor <= (scale.Max + FLT_EPSILON))
Evan Pezent's avatar
Evan Pezent 已提交
587
588
589
590
591
                    out.push_back(ImTick(minor, false, false));
            }
        }
    }
    else {
592
        const double range    = NiceNum(scale.Max - scale.Min, 0);
Evan Pezent's avatar
Evan Pezent 已提交
593
        const double interval = NiceNum(range / (nMajor - 1), 1);
594
595
        const double graphmin = floor(scale.Min / interval) * interval;
        const double graphmax = ceil(scale.Max / interval) * interval;
Evan Pezent's avatar
Evan Pezent 已提交
596
        for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {
597
            if (major >= scale.Min && major <= scale.Max)
Evan Pezent's avatar
Evan Pezent 已提交
598
599
600
                out.push_back(ImTick(major, true));
            for (int i = 1; i < nMinor; ++i) {
                double minor = major + i * interval / nMinor;
601
                if (minor >= scale.Min && minor <= scale.Max)
Evan Pezent's avatar
Evan Pezent 已提交
602
603
604
605
606
607
608
609
610
                    out.push_back(ImTick(minor, false));
            }
        }
    }
}

inline void LabelTicks(ImVector<ImTick> &ticks, bool scientific, ImGuiTextBuffer& buffer) {
    buffer.Buf.resize(0);
    char temp[32];
ozlb's avatar
ozlb 已提交
611
612
613
614
    for (int t = 0; t < ticks.Size; t++) {
        ImTick *tk = &ticks[t];
        if (tk->RenderLabel) {
            tk->TextOffset = buffer.size();
Evan Pezent's avatar
Evan Pezent 已提交
615
            if (scientific)
ozlb's avatar
ozlb 已提交
616
                sprintf(temp, "%.0e", tk->PlotPos);
Evan Pezent's avatar
Evan Pezent 已提交
617
            else
ozlb's avatar
ozlb 已提交
618
                sprintf(temp, "%g", tk->PlotPos);
Evan Pezent's avatar
Evan Pezent 已提交
619
            buffer.append(temp, temp + strlen(temp) + 1);
ozlb's avatar
ozlb 已提交
620
            tk->Size = ImGui::CalcTextSize(buffer.Buf.Data + tk->TextOffset);
Evan Pezent's avatar
Evan Pezent 已提交
621
622
623
624
        }
    }
}

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
namespace {
struct AxisState {
    ImPlotAxis* axis;
    bool has_range;
    ImGuiCond range_cond;
    bool present;
    int present_so_far;
    bool flip;
    bool lock_min;
    bool lock_max;
    bool lock;

    AxisState(ImPlotAxis& axis_in, bool has_range_in, ImGuiCond range_cond_in,
              bool present_in, int previous_present)
            : axis(&axis_in),
              has_range(has_range_in),
              range_cond(range_cond_in),
              present(present_in),
              present_so_far(previous_present + (present ? 1 : 0)),
644
645
646
              flip(HasFlag(axis->Flags, ImPlotAxisFlags_Invert)),
              lock_min(HasFlag(axis->Flags, ImPlotAxisFlags_LockMin)),
              lock_max(HasFlag(axis->Flags, ImPlotAxisFlags_LockMax)),
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
              lock(present && ((lock_min && lock_max) || (has_range && range_cond == ImGuiCond_Always))) {}

    AxisState()
            : axis(),
              has_range(),
              range_cond(),
              present(),
              present_so_far(),
              flip(),
              lock_min(),
              lock_max(),
              lock() {}
};

void UpdateAxisColor(int axis_flag, ImPlotContext::AxisColor* 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];
ozlb's avatar
ozlb 已提交
663
664
    col->Major = ImGui::GetColorU32(col_Axis);
    col->Minor = ImGui::GetColorU32(col_Axis * ImVec4(1, 1, 1, 0.25f));
ozlb's avatar
ozlb 已提交
665
    col->Txt   = ImGui::GetColorU32(ImVec4(col_Axis.x, col_Axis.y, col_Axis.z, 1));
666
667
668
669
670
671
672
673
674
675
676
677
678
679
}

ImRect GetAxisScale(int y_axis, float tx, float ty, float zoom_rate) {
    return ImRect(
            PixelsToPlot(gp.BB_Grid.Min - gp.BB_Grid.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), y_axis),
            PixelsToPlot(gp.BB_Grid.Max + gp.BB_Grid.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), y_axis));
}

class YPadCalculator {
  public:
    YPadCalculator(const AxisState* axis_states, const float* max_label_widths, float txt_off)
            : AxisStates(axis_states), MaxLabelWidths(max_label_widths), TxtOff(txt_off) {}

    float operator()(int y_axis) {
ozlb's avatar
ozlb 已提交
680
        ImPlotState& plot = *gp.CurrentPlot;
681
682
683
684
685
686
687
        if (!AxisStates[y_axis].present) { return 0; }
        // 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;
        if (AxisStates[y_axis].present_so_far >= 3) {
            pad_result += 6.0f;
        }
688
        if (!HasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_TickLabels)) {
689
690
691
692
693
694
695
696
697
698
699
700
701
            return pad_result;
        }
        pad_result += MaxLabelWidths[y_axis] + TxtOff;
        return pad_result;
    }

  private:
    const AxisState* const AxisStates;
    const float* const MaxLabelWidths;
    const float TxtOff;
};
}  // namespace

702
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
703
// BeginPlot()
704
//-----------------------------------------------------------------------------
Evan Pezent's avatar
Evan Pezent 已提交
705

706
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 已提交
707
708
709
710
711
712
713
714
715
716
717
718
719
720

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

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

    ImGuiContext &G      = *GImGui;
    ImGuiWindow * Window = G.CurrentWindow;
    if (Window->SkipItems) {
        gp.NextPlotData = ImNextPlotData();
        return false;
    }

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

Evan Pezent's avatar
Evan Pezent 已提交
723
    bool just_created = gp.Plots.GetByKey(ID) == NULL;
Evan Pezent's avatar
Evan Pezent 已提交
724
    gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID);
ozlb's avatar
ozlb 已提交
725
    ImPlotState &plot = *gp.CurrentPlot;
Evan Pezent's avatar
Evan Pezent 已提交
726

727
728
    plot.CurrentYAxis = 0;

Evan Pezent's avatar
Evan Pezent 已提交
729
    if (just_created) {
730
731
732
733
734
        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 已提交
735
    }
736
    else {
Evan Pezent's avatar
Evan Pezent 已提交
737
        // TODO: Check which individual flags changed, and only reset those!
738
        // There's probably an easy bit mask trick I'm not aware of.
Evan Pezent's avatar
Evan Pezent 已提交
739
740
        if (flags != plot.PreviousFlags)
            plot.Flags = flags;
741
742
743
744
745
746
747
748
749
750
751
752
753
        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 已提交
754

755
756
757
758
759
760
761
762
763
    // capture scroll with a child region
    if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) {
        ImGui::BeginChild(title, size);
        Window = ImGui::GetCurrentWindow();
        Window->ScrollMax.y = 1.0f;
    }

    ImDrawList &DrawList = *Window->DrawList;

Evan Pezent's avatar
Evan Pezent 已提交
764
765
766
767
768
    // NextPlotData -----------------------------------------------------------

    if (gp.NextPlotData.HasXRange) {
        if (just_created || gp.NextPlotData.XRangeCond == ImGuiCond_Always)
        {
769
            plot.XAxis.Range = gp.NextPlotData.X;
Evan Pezent's avatar
Evan Pezent 已提交
770
771
772
        }
    }

773
774
775
776
777
778
    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 已提交
779
780
781
782
        }
    }

    // AXIS STATES ------------------------------------------------------------
783
784
785
786
787
788
789
    AxisState x(plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true, 0);
    AxisState y[MAX_Y_AXES];
    y[0] = AxisState(plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true, 0);
    y[1] = AxisState(plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1],
                     HasFlag(plot.Flags, ImPlotFlags_YAxis2), y[0].present_so_far);
    y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2],
                     HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far);
Evan Pezent's avatar
Evan Pezent 已提交
790

791
    const bool lock_plot  = x.lock && y[0].lock && y[1].lock && y[2].lock;
Evan Pezent's avatar
Evan Pezent 已提交
792
793
794

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

795
796
797
798
799
800
    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 已提交
801

802
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
803
        plot.XAxis.Range.Min = ConstrainLog(plot.XAxis.Range.Min);
804
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
805
806
        plot.XAxis.Range.Max = ConstrainLog(plot.XAxis.Range.Max);
    for (int i = 0; i < MAX_Y_AXES; i++) {
807
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
808
            plot.YAxis[i].Range.Min = ConstrainLog(plot.YAxis[i].Range.Min);
809
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
810
811
            plot.YAxis[i].Range.Max = ConstrainLog(plot.YAxis[i].Range.Max);
    }
Evan Pezent's avatar
Evan Pezent 已提交
812

813
814
815
816
817
818
    if (plot.XAxis.Range.Max <= plot.XAxis.Range.Min)
        plot.XAxis.Range.Max = plot.XAxis.Range.Min + FLT_EPSILON;
    for (int i = 0; i < MAX_Y_AXES; i++) {
        if (plot.YAxis[i].Range.Max <= plot.YAxis[i].Range.Min)
            plot.YAxis[i].Range.Max = plot.YAxis[i].Range.Min + FLT_EPSILON;
    }
Evan Pezent's avatar
Evan Pezent 已提交
819
820

    // adaptive divisions
821
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Adaptive)) {
Evan Pezent's avatar
Evan Pezent 已提交
822
823
        plot.XAxis.Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth());
        if (plot.XAxis.Divisions < 2)
Evan Pezent's avatar
Evan Pezent 已提交
824
            plot.XAxis.Divisions = 2;
Evan Pezent's avatar
Evan Pezent 已提交
825
    }
826
    for (int i = 0; i < MAX_Y_AXES; i++) {
827
        if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Adaptive)) {
828
829
830
831
            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 已提交
832
833
834
835
    }

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

ozlb's avatar
ozlb 已提交
836
837
838
    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 已提交
839

840
841
842
843
    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 已提交
844

ozlb's avatar
ozlb 已提交
845
846
847
848
849
850
    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 已提交
851
852
853
854

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

    // frame
ozlb's avatar
ozlb 已提交
855
    const ImVec2 frame_size = ImGui::CalcItemSize(size, 100, 100);
Evan Pezent's avatar
Evan Pezent 已提交
856
    gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
ozlb's avatar
ozlb 已提交
857
858
    ImGui::ItemSize(gp.BB_Frame);
    if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) {
Evan Pezent's avatar
Evan Pezent 已提交
859
860
        gp.NextPlotData = ImNextPlotData();
        gp.CurrentPlot = NULL;
861
862
        if (!HasFlag(plot.Flags, ImPlotFlags_NoChild))
            ImGui::EndChild();
Evan Pezent's avatar
Evan Pezent 已提交
863
864
        return false;
    }
ozlb's avatar
ozlb 已提交
865
866
    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 已提交
867
868

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

871
872
873
    gp.RenderX = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines) ||
                    HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks) ||
                    HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) &&  plot.XAxis.Divisions > 1;
874
875
876
    for (int i = 0; i < MAX_Y_AXES; i++) {
        gp.RenderY[i] =
                y[i].present &&
Evan Pezent's avatar
Evan Pezent 已提交
877
878
                (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) ||
                 HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) ||
879
                 HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) &&  plot.YAxis[i].Divisions > 1;
880
    }
Evan Pezent's avatar
Evan Pezent 已提交
881
882
883

    // get ticks
    if (gp.RenderX)
884
        GetTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
885
886
    for (int i = 0; i < MAX_Y_AXES; i++) {
        if (gp.RenderY[i]) {
887
            GetTicks(plot.YAxis[i].Range, plot.YAxis[i].Divisions, plot.YAxis[i].Subdivisions, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]);
888
889
        }
    }
Evan Pezent's avatar
Evan Pezent 已提交
890
891

    // label ticks
892
893
    if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels))
        LabelTicks(gp.XTicks, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Scientific), gp.XTickLabels);
Evan Pezent's avatar
Evan Pezent 已提交
894

895
896
    float max_label_width[MAX_Y_AXES] = {};
    for (int i = 0; i < MAX_Y_AXES; i++) {
897
898
        if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) {
            LabelTicks(gp.YTicks[i], HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific), gp.YTickLabels[i]);
ozlb's avatar
ozlb 已提交
899
900
901
            for (int t = 0; t < gp.YTicks[i].Size; t++) {
                ImTick *yt = &gp.YTicks[i][t];
                max_label_width[i] = yt->Size.x > max_label_width[i] ? yt->Size.x : max_label_width[i];
902
903
            }
        }
Evan Pezent's avatar
Evan Pezent 已提交
904
905
906
    }

    // grid bb
ozlb's avatar
ozlb 已提交
907
    const ImVec2 title_size = ImGui::CalcTextSize(title, NULL, true);
Evan Pezent's avatar
Evan Pezent 已提交
908
    const float txt_off     = 5;
ozlb's avatar
ozlb 已提交
909
    const float txt_height  = ImGui::GetTextLineHeight();
Evan Pezent's avatar
Evan Pezent 已提交
910
    const float pad_top     = title_size.x > 0.0f ? txt_height + txt_off : 0;
911
    const float pad_bot     = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels) ? txt_height + txt_off : 0) + (x_label ? txt_height + txt_off : 0);
912
913
914
    YPadCalculator y_axis_pad(y, max_label_width, txt_off);
    const float pad_left    = y_axis_pad(0) + (y_label ? txt_height + txt_off : 0);
    const float pad_right   = y_axis_pad(1) + y_axis_pad(2);
ozlb's avatar
ozlb 已提交
915
916
    gp.BB_Grid              = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot));
    gp.Hov_Grid             = gp.BB_Grid.Contains(IO.MousePos);
Evan Pezent's avatar
Evan Pezent 已提交
917
918

    // axis region bbs
919
    const ImRect xAxisRegion_bb(gp.BB_Grid.Min + ImVec2(10, 0), ImVec2(gp.BB_Grid.Max.x, gp.BB_Frame.Max.y) - ImVec2(10, 0));
Evan Pezent's avatar
Evan Pezent 已提交
920
    const bool   hov_x_axis_region = xAxisRegion_bb.Contains(IO.MousePos);
921
922
923
924
925
926
927
928
929
930
931
932
933
934

    // The left labels are referenced to the left of the bounding box.
    gp.AxisLabelReference[0] = gp.BB_Grid.Min.x;
    // If Y axis 1 is present, its labels will be referenced to the
    // right of the bounding box.
    gp.AxisLabelReference[1] = gp.BB_Grid.Max.x;
    // The third axis may be either referenced to the right of the
    // bounding box, or 6 pixels further past the end of the 2nd axis.
    gp.AxisLabelReference[2] =
            !y[1].present ?
            gp.BB_Grid.Max.x :
            (gp.AxisLabelReference[1] + y_axis_pad(1) + 6);

    ImRect yAxisRegion_bb[MAX_Y_AXES];
ozlb's avatar
ozlb 已提交
935
    yAxisRegion_bb[0] = ImRect(ImVec2(gp.BB_Frame.Min.x, gp.BB_Grid.Min.y), ImVec2(gp.BB_Grid.Min.x + 6, gp.BB_Grid.Max.y - 10));
936
    // The auxiliary y axes are off to the right of the BB grid.
ozlb's avatar
ozlb 已提交
937
    yAxisRegion_bb[1] = ImRect(ImVec2(gp.BB_Grid.Max.x - 6, gp.BB_Grid.Min.y),
938
                               gp.BB_Grid.Max + ImVec2(y_axis_pad(1), 0));
ozlb's avatar
ozlb 已提交
939
    yAxisRegion_bb[2] = ImRect(ImVec2(gp.AxisLabelReference[2] - 6, gp.BB_Grid.Min.y),
940
941
                               yAxisRegion_bb[1].Max + ImVec2(y_axis_pad(2), 0));

ozlb's avatar
ozlb 已提交
942
943
    ImRect centralRegion(ImVec2(gp.BB_Grid.Min.x + 6, gp.BB_Grid.Min.y),
                         ImVec2(gp.BB_Grid.Max.x - 6, gp.BB_Grid.Max.y));
Evan Pezent's avatar
Evan Pezent 已提交
944

945
946
947
948
949
950
    const bool hov_y_axis_region[MAX_Y_AXES] = {
        y[0].present && (yAxisRegion_bb[0].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)),
        y[1].present && (yAxisRegion_bb[1].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)),
        y[2].present && (yAxisRegion_bb[2].Contains(IO.MousePos) || centralRegion.Contains(IO.MousePos)),
    };
    const bool any_hov_y_axis_region = hov_y_axis_region[0] || hov_y_axis_region[1] || hov_y_axis_region[2];
Evan Pezent's avatar
Evan Pezent 已提交
951
952

    // legend hovered from last frame
Evan Pezent's avatar
Evan Pezent 已提交
953
    const bool hov_legend = HasFlag(plot.Flags, ImPlotFlags_Legend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
954
955

    bool hov_query = false;
956
957
958
959
960
961
    if (gp.Hov_Frame && gp.Hov_Grid && plot.Queried && !plot.Querying) {
        ImRect bb_query = plot.QueryRect;

        bb_query.Min += gp.BB_Grid.Min;
        bb_query.Max += gp.BB_Grid.Min;

962
963
964
965
966
967
968
        hov_query = bb_query.Contains(IO.MousePos);
    }

    // QUERY DRAG -------------------------------------------------------------
    if (plot.DraggingQuery && (IO.MouseReleased[0] || !IO.MouseDown[0])) {
        plot.DraggingQuery = false;
    }
Evan Pezent's avatar
Evan Pezent 已提交
969
    if (plot.DraggingQuery) {
ozlb's avatar
ozlb 已提交
970
        ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
971
972
        plot.QueryRect.Min += IO.MouseDelta;
        plot.QueryRect.Max += IO.MouseDelta;
973
    }
974
    if (gp.Hov_Frame && gp.Hov_Grid && hov_query && !plot.DraggingQuery && !plot.Selecting && !hov_legend) {
ozlb's avatar
ozlb 已提交
975
        ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
976
977
        const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging;
        if (IO.MouseDown[0] && !plot.XAxis.Dragging && !any_y_dragging) {
978
            plot.DraggingQuery = true;
Evan Pezent's avatar
Evan Pezent 已提交
979
980
        }
    }
Evan Pezent's avatar
Evan Pezent 已提交
981
982
983

    // DRAG INPUT -------------------------------------------------------------

984

Evan Pezent's avatar
Evan Pezent 已提交
985
986
987
    // end drags
    if (plot.XAxis.Dragging && (IO.MouseReleased[0] || !IO.MouseDown[0])) {
        plot.XAxis.Dragging             = false;
Evan Pezent's avatar
Evan Pezent 已提交
988
        G.IO.MouseDragMaxDistanceSqr[0] = 0;
Evan Pezent's avatar
Evan Pezent 已提交
989
    }
990
991
992
993
994
    for (int i = 0; i < MAX_Y_AXES; i++) {
        if (plot.YAxis[i].Dragging && (IO.MouseReleased[0] || !IO.MouseDown[0])) {
            plot.YAxis[i].Dragging             = false;
            G.IO.MouseDragMaxDistanceSqr[0] = 0;
        }
Evan Pezent's avatar
Evan Pezent 已提交
995
    }
996
    const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging;
ozlb's avatar
ozlb 已提交
997
    bool drag_in_progress = plot.XAxis.Dragging || any_y_dragging;
Evan Pezent's avatar
Evan Pezent 已提交
998
    // do drag
999
    if (drag_in_progress) {
1000
        UpdateTransformCache();
为了加快浏览速度,不会显示所有历史记录。 查看完整的 blame