diff --git a/README.md b/README.md
index 5ea1fc0..4b8da34 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,17 @@
# ImPlot
-ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ocornut/imgui). It aims to provide a first-class API that will make ImGui users feel right at home. ImPlot is well suited for visualizing program data in real-time and requires minimal code to integrate. Just like ImGui, it does not burden the end user with GUI state management, avoids STL containers and C++ headers, and has no external dependencies except for ImGui itself.
+ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ocornut/imgui). It aims to provide a first-class API that will make ImGui users feel right at home. ImPlot is well suited for visualizing program data in real-time and requires minimal code to integrate. Just like ImGui, it does not burden the end user with GUI state management, avoids STL containers and C++ headers, and has no external dependencies except for ImGui itself.
-
-
+
+
-
-
+
+
## Features
-- multiple plot types:
+- multiple plot types:
- line plots
- shaded plots
- scatter plots
@@ -32,7 +32,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/
- optional plot titles, axis labels, and grid labels
- optional legend with toggle buttons to quickly show/hide items
- size-aware grid with smart labels that are always power-of-ten multiples of 1, 2, and 5
-- default styling based on current ImGui theme, but most elements can be customized independently
+- default styling based on current ImGui theme, but most elements can be customized independently
- mouse cursor location display and optional crosshairs cursor
- customizable data getters and data striding (just like ImGui:PlotLine)
- relatively good performance for high density plots
@@ -40,7 +40,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/
## Usage
-The API is used just like any other ImGui `BeginX`/`EndX` pair. First, start a plotting context with `ImPlot::BeginPlot()`. Next, plot as many items as you want with the provided `PlotX` functions (e.g. `PlotLine()`, `PlotBars()`, `PlotErrorBars()`, etc). Finally, wrap things up with a call to `ImPlot::EndPlot()`. That's it!
+The API is used just like any other ImGui `BeginX`/`EndX` pair. First, start a new plot with `ImPlot::BeginPlot()`. Next, plot as many items as you want with the provided `PlotX` functions (e.g. `PlotLine()`, `PlotBars()`, `PlotErrorBars()`, etc). Finally, wrap things up with a call to `ImPlot::EndPlot()`. That's it!
```cpp
if (ImPlot::BeginPlot("My Plot")) {
@@ -51,22 +51,33 @@ if (ImPlot::BeginPlot("My Plot")) {
}
```
-Consult `implot_demo.cpp` for a comprehensive example of ImPlot's features.
+Consult `implot_demo.cpp` for a comprehensive example of ImPlot's features.
## Integration
-Just add `implot.h`, `implot.cpp`, and optionally `implot_demo.cpp` to your sources. This assumes you already have an ImGui-ready environment. If not, consider trying [mahi-gui](https://github.com/mahilab/mahi-gui), which bundles ImGui, ImPlot, and several other packages for you.
+1) Add `implot.h`, `implot_internal.h`, `implot.cpp`, `implot_items.cpp` and optionally `implot_demo.cpp` to your sources.
+2) Create and destroy an `ImPlotContext` wherever you create your `ImGuiContext`:
+
+```cpp
+ImGui::CreateContext();
+ImPlot::CreateContext();
+...
+ImPlot::DestroyContext();
+ImGui::DestroyContext();
+```
+
+Of course, this assumes you already have an ImGui-ready environment. If not, consider trying [mahi-gui](https://github.com/mahilab/mahi-gui), which bundles ImGui, ImPlot, and several other packages for you.
## Special Notes
-- If you experience data truncation and/or visual glitches, it is **HIGHLY** recommended that you either:
+- If you experience data truncation and/or visual glitches, it is **HIGHLY** recommended that you EITHER:
1) Handle the `ImGuiBackendFlags_RendererHasVtxOffset` flag in your renderer when using 16-bit indices (the official OpenGL3 renderer supports this) and use an ImGui version with patch [imgui@f6120f8](https://github.com/ocornut/imgui/commit/f6120f8e16eefcdb37b63974e6915a3dd35414be).
2) Enable 32-bit indices by uncommenting `#define ImDrawIdx unsigned int` in your `imconfig.h` file.
- By default, no anti-aliasing is done on line plots for performance reasons. If you use 4x MSAA, then you likely won't even notice. However, you can re-enable AA with the `ImPlotFlags_AntiAliased` flag.
## Interactive Demo
-An online version of the demo is hosted [here](https://traineq.org/implot_demo/src/implot_demo.html). You can view the plots and the source code that generated them. Note that this demo may not always be up to date and is not as performant as a desktop implemention, but it should give you a general taste of what's possible with ImPlot. Special thanks to [pthom](https://github.com/pthom) for creating and hosting this!
+An online version of the demo is hosted [here](https://traineq.org/implot_demo/src/implot_demo.html). You can view the plots and the source code that generated them. Note that this demo may not always be up to date and is not as performant as a desktop implemention, but it should give you a general taste of what's possible with ImPlot. Special thanks to [pthom](https://github.com/pthom) for creating and hosting this!
## FAQ
@@ -76,7 +87,7 @@ A: ImGui is an incredibly powerful tool for rapid prototyping and development, b
**Q: Is ImPlot suitable for real-time plotting?**
-A: Yes, within reason. You can plot tens to hundreds of thousands of points without issue, but don't expect plotting over a million to be a buttery smooth experience. We do our best to keep it fast and avoid memory allocations.
+A: Yes, within reason. You can plot tens to hundreds of thousands of points without issue, but don't expect plotting over a million to be a buttery smooth experience. We do our best to keep it fast and avoid memory allocations.
**Q: Can plot styles be modified?**
@@ -104,7 +115,7 @@ A: See the note about anti-aliasing under **Special Notes** above.
**Q: Does ImPlot provide analytic tools?**
-A: Not exactly, but it does give you the ability to query plot sub-ranges, with which you can process your data however you like.
+A: Not exactly, but it does give you the ability to query plot sub-ranges, with which you can process your data however you like.
**Q: Can plots be exported/saved to image?**
@@ -116,6 +127,6 @@ A: Yes, ImPlot accepts both `float` and `double` for all of its plotting functio
**Q: Can ImPlot be used with other languages/bindings?**
-A: Yes, you can use the C binding, [cimplot](https://github.com/cimgui/cimplot) with most high level languages.
+A: Yes, you can use the C binding, [cimplot](https://github.com/cimgui/cimplot) with most high level languages.
diff --git a/implot.cpp b/implot.cpp
index 778605a..eac3d09 100644
--- a/implot.cpp
+++ b/implot.cpp
@@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the
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.
+- 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file.
- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default.
- 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well.
- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`.
@@ -56,43 +57,36 @@ You can read releases logs https://github.com/epezent/implot/releases for more d
*/
-#ifndef IMGUI_DEFINE_MATH_OPERATORS
-#define IMGUI_DEFINE_MATH_OPERATORS
-#endif
-
#include "implot.h"
-#include "imgui_internal.h"
+#include "implot_internal.h"
+
#ifdef _MSC_VER
#define sprintf sprintf_s
#endif
-#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; \
- } \
- }
+// Global plot context
+ImPlotContext* GImPlot = NULL;
+//-----------------------------------------------------------------------------
+// Struct Implementations
+//-----------------------------------------------------------------------------
-// The maximum number of support y-axes
-#define MAX_Y_AXES 3
-
-// static inline float ImLog10(float x) { return log10f(x); }
-static inline double ImLog10(double x) { return log10(x); }
+ImPlotRange::ImPlotRange() {
+ Min = NAN;
+ Max = NAN;
+}
ImPlotStyle::ImPlotStyle() {
- LineWeight = 1;
- Marker = ImPlotMarker_None;
- MarkerSize = 4;
- MarkerWeight = 1;
- FillAlpha = 1;
- ErrorBarSize = 5;
- ErrorBarWeight = 1.5;
+
+ LineWeight = 1;
+ Marker = ImPlotMarker_None;
+ MarkerSize = 4;
+ MarkerWeight = 1;
+ FillAlpha = 1;
+ ErrorBarSize = 5;
+ ErrorBarWeight = 1.5f;
DigitalBitHeight = 8;
- DigitalBitGap = 4;
+ DigitalBitGap = 4;
Colors[ImPlotCol_Line] = IMPLOT_COL_AUTO;
Colors[ImPlotCol_Fill] = IMPLOT_COL_AUTO;
@@ -110,552 +104,52 @@ ImPlotStyle::ImPlotStyle() {
Colors[ImPlotCol_Query] = ImVec4(0,1,0,1);
}
-ImPlotRange::ImPlotRange() {
- Min = NAN;
- Max = NAN;
-}
-
-bool ImPlotRange::Contains(double v) const {
- return v >= Min && v <= Max;
-}
-
-double ImPlotRange::Size() const {
- return Max - Min;
-}
-
-ImPlotLimits::ImPlotLimits() {}
-
-bool ImPlotLimits::Contains(const ImPlotPoint& p) const {
- return Contains(p.x, p.y);
-}
-
-bool ImPlotLimits::Contains(double x, double y) const {
- return X.Contains(x) && Y.Contains(y);
-}
-
ImPlotInputMap::ImPlotInputMap() {
- PanButton = ImGuiMouseButton_Left;
- PanMod = ImGuiKeyModFlags_None;
- FitButton = ImGuiMouseButton_Left;
- ContextMenuButton = ImGuiMouseButton_Right;
- BoxSelectButton = ImGuiMouseButton_Right;
- BoxSelectMod = ImGuiKeyModFlags_None;
+ PanButton = ImGuiMouseButton_Left;
+ PanMod = ImGuiKeyModFlags_None;
+ FitButton = ImGuiMouseButton_Left;
+ ContextMenuButton = ImGuiMouseButton_Right;
+ BoxSelectButton = ImGuiMouseButton_Right;
+ BoxSelectMod = ImGuiKeyModFlags_None;
BoxSelectCancelButton = ImGuiMouseButton_Left;
- QueryButton = ImGuiMouseButton_Middle;
- QueryMod = ImGuiKeyModFlags_None;
- QueryToggleMod = ImGuiKeyModFlags_Ctrl;
- HorizontalMod = ImGuiKeyModFlags_Alt;
- VerticalMod = ImGuiKeyModFlags_Shift;
+ QueryButton = ImGuiMouseButton_Middle;
+ QueryMod = ImGuiKeyModFlags_None;
+ QueryToggleMod = ImGuiKeyModFlags_Ctrl;
+ HorizontalMod = ImGuiKeyModFlags_Alt;
+ VerticalMod = ImGuiKeyModFlags_Shift;
}
namespace ImPlot {
-namespace {
-
//-----------------------------------------------------------------------------
-// Private Utils
+// Generic Helpers
//-----------------------------------------------------------------------------
-template
-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];
-};
-
-template
-void FillRange(ImVector& 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;
- }
-}
-
-// Returns true if a flag is set
-template
-inline bool HasFlag(TSet set, TFlag flag) {
- return (set & flag) == flag;
-}
-
-// Flips a flag in a flagset
-template
-inline void FlipFlag(TSet& set, TFlag flag) {
- HasFlag(set, flag) ? set &= ~flag : set |= flag;
-}
-
-// Linearly remaps x from [x0 x1] to [y0 y1].
-template
-inline T Remap(T x, T x0, T x1, T y0, T y1) {
- return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
-}
-
-// Returns always positive modulo (r != 0)
-inline int PosMod(int l, int r) {
- return (l % r + r) % r;
-}
-
-// Returns the intersection point of two lines A and B (assumes they are not parallel!)
-inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) {
- float v1 = (a1.x * a2.y - a1.y * a2.x);
- float v2 = (b1.x * b2.y - b1.y * b2.x);
- float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x));
- return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3,
- (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3);
-}
-
-// Turns NANs to 0s
-inline double ConstrainNan(double val) {
- return isnan(val) ? 0 : val;
-}
-
-// Turns infinity to floating point maximums
-inline double ConstrainInf(double val) {
- return val == HUGE_VAL ? DBL_MAX : val == -HUGE_VAL ? - DBL_MAX : val;
-}
-
-// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?)
-inline double ConstrainLog(double val) {
- return val <= 0 ? 0.001f : val;
-}
-
-// Returns true if val is NAN or INFINITY
-inline bool NanOrInf(double val) {
- return val == HUGE_VAL || val == -HUGE_VAL || isnan(val);
-}
-
-// 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;
-}
-
-// Returns a floating point precision to use given a value
-inline int Precision(double val) {
- return OrderToPrecision(OrderOfMagnitude(val));
-}
-
-// 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;
+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;
for (char c = *text++; c; c = *text++) {
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) {
+ImVec2 CalcTextSizeVertical(const char *text) {
ImVec2 sz = ImGui::CalcTextSize(text);
return ImVec2(sz.y, sz.x);
}
-/// Returns white or black text given background color
-inline ImU32 CalcTextColor(const ImVec4& bg) {
- return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.729 ? IM_COL32_BLACK : IM_COL32_WHITE;
-}
-
-} // private namespace
-
-//-----------------------------------------------------------------------------
-// Structs
-//-----------------------------------------------------------------------------
-
-// Tick mark info
-struct ImPlotTick {
- ImPlotTick(double value, bool major, bool render_label = true) {
- PlotPos = value;
- Major = major;
- RenderLabel = render_label;
- Labeled = false;
- }
- double PlotPos;
- float PixelPos;
- ImVec2 Size;
- int TextOffset;
- bool Major;
- bool RenderLabel;
- bool Labeled;
-};
-
-// Axis state information that must persist after EndPlot
-struct ImPlotAxis {
- ImPlotAxis() {
- Dragging = false;
- Hovered = false;
- Range.Min = 0;
- Range.Max = 1;
- Flags = PreviousFlags = ImPlotAxisFlags_Default;
- }
- bool Dragging;
- bool Hovered;
- ImPlotRange Range;
- ImPlotAxisFlags Flags, PreviousFlags;
-};
-
-// Axis state information only needed between BeginPlot/EndPlot
-struct ImPlotAxisState {
- 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()
- {}
-};
-
-struct ImPlotAxisColor {
- ImPlotAxisColor() : Major(), Minor(), Txt() {}
- ImU32 Major, Minor, Txt;
-};
-
-// State information for Plot items
-struct ImPlotItem {
- ImPlotItem() {
- Show = true;
- SeenThisFrame = false;
- Highlight = false;
- Color = ImPlot::NextColormapColor();
- NameOffset = -1;
- ID = 0;
- }
- ~ImPlotItem() { ID = 0; }
- bool Show;
- bool SeenThisFrame;
- bool Highlight;
- ImVec4 Color;
- int NameOffset;
- ImGuiID ID;
-};
-
-// Holds Plot state information that must persist after EndPlot
-struct ImPlotState {
- ImPlotState() {
- Selecting = Querying = Queried = DraggingQuery = false;
- SelectStart = QueryStart = ImVec2(0,0);
- Flags = PreviousFlags = ImPlotFlags_Default;
- ColorIdx = 0;
- CurrentYAxis = 0;
- }
- ImPool Items;
-
- ImRect BB_Legend;
- ImVec2 SelectStart;
- bool Selecting;
- bool Querying;
- bool Queried;
- bool DraggingQuery;
- ImVec2 QueryStart;
- ImRect QueryRect; // relative to BB_Plot!!
-
- ImPlotAxis XAxis;
- ImPlotAxis YAxis[MAX_Y_AXES];
-
- ImPlotFlags Flags, PreviousFlags;
- int ColorIdx;
- int CurrentYAxis;
-};
-
-struct ImPlotNextPlotData {
- ImPlotNextPlotData() {
- HasXRange = false;
- ShowDefaultTicksX = true;
- for (int i = 0; i < MAX_Y_AXES; ++i) {
- HasYRange[i] = false;
- ShowDefaultTicksY[i] = true;
- }
- }
- ImGuiCond XRangeCond;
- ImGuiCond YRangeCond[MAX_Y_AXES];
- bool HasXRange;
- bool HasYRange[MAX_Y_AXES];
- ImPlotRange X;
- ImPlotRange Y[MAX_Y_AXES];
- bool ShowDefaultTicksX;
- bool ShowDefaultTicksY[MAX_Y_AXES];
-};
-
-// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot()
-struct ImPlotContext {
- ImPlotContext() : RenderX(), RenderY() {
- ChildWindowMade = false;
- Reset();
- SetColormap(ImPlotColormap_Default);
- }
-
- 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
- CurrentPlot = NULL;
- }
-
- // ALl Plots
- ImPool Plots;
- // Current Plot
- ImPlotState* CurrentPlot;
- // Legend
- ImVector LegendIndices;
- ImGuiTextBuffer LegendLabels;
- // Bounding regions
- ImRect BB_Frame;
- ImRect BB_Canvas;
- ImRect BB_Plot;
- // Cached Colors
- ImU32 Col_Frame, Col_Bg, Col_Border,
- Col_Txt, Col_TxtDis,
- Col_SlctBg, Col_SlctBd,
- Col_QryBg, Col_QryBd;
- ImPlotAxisColor Col_X;
- ImPlotAxisColor Col_Y[MAX_Y_AXES];
- ImPlotAxisState X;
- ImPlotAxisState Y[MAX_Y_AXES];
- // Tick marks
- ImVector XTicks, YTicks[MAX_Y_AXES];
- ImGuiTextBuffer XTickLabels, YTickLabels[MAX_Y_AXES];
- float AxisLabelReference[MAX_Y_AXES];
- // Transformation cache
- ImRect PixelRange[MAX_Y_AXES];
- // linear scale (slope)
- double Mx;
- double My[MAX_Y_AXES];
- // log scale denominator
- double LogDenX;
- double LogDenY[MAX_Y_AXES];
- // Data extents
- ImPlotRange ExtentsX;
- ImPlotRange ExtentsY[MAX_Y_AXES];
- int VisibleItemCount;
- bool FitThisFrame; bool FitX;
- bool FitY[MAX_Y_AXES];
- // Hover states
- bool Hov_Frame;
- bool Hov_Plot;
- // Render flags
- bool RenderX, RenderY[MAX_Y_AXES];
- // Lock info
- bool LockPlot;
- bool ChildWindowMade;
- // Mouse pos
- ImPlotPoint LastMousePos[MAX_Y_AXES];
- // Style
- ImVec4* Colormap;
- int ColormapSize;
- ImPlotStyle Style;
- ImPlotInputMap InputMap;
- ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
- ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
- ImPlotNextPlotData NextPlotData;
- // Digital plot item count
- int DigitalPlotItemCnt;
- int DigitalPlotOffset;
-};
-
-// Global plot context
-static ImPlotContext gp;
-
-//-----------------------------------------------------------------------------
-// Context Utils
-//-----------------------------------------------------------------------------
-
-ImPlotInputMap& GetInputMap() {
- return gp.InputMap;
-}
-
-inline void FitPoint(const ImPlotPoint& p) {
- ImPlotRange* extents_x = &gp.ExtentsX;
- ImPlotRange* extents_y = &gp.ExtentsY[gp.CurrentPlot->CurrentYAxis];
- if (!NanOrInf(p.x)) {
- 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;
- }
- if (!NanOrInf(p.y)) {
- 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;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Coordinate Transforms
-//-----------------------------------------------------------------------------
-
-inline void UpdateTransformCache() {
- // get pixels for transforms
- for (int i = 0; i < MAX_Y_AXES; i++) {
- 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);
-
- gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size();
- }
- gp.LogDenX = ImLog10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min);
- for (int i = 0; i < MAX_Y_AXES; i++) {
- gp.LogDenY[i] = ImLog10(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();
-}
-
-inline ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in = -1) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
- const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
- ImPlotPoint plt;
- 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;
- if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
- 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;
- }
- if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
- 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;
- }
- return plt;
-}
-
-ImPlotPoint 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.
-inline ImVec2 PlotToPixels(double x, double y, int y_axis_in = -1) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
- const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
- ImVec2 pix;
- if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
- 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);
- }
- if (HasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
- double t = ImLog10(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, (float)t);
- }
- 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));
- return pix;
-}
-
-// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
-ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) {
- return PlotToPixels(plt.x, plt.y, y_axis);
-}
-
-//-----------------------------------------------------------------------------
-// Legend Utils
-//-----------------------------------------------------------------------------
-
-ImPlotItem* RegisterItem(const char* label_id) {
- ImGuiID id = ImGui::GetID(label_id);
- ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id);
- if (item->SeenThisFrame)
- return item;
- item->SeenThisFrame = true;
- int idx = gp.CurrentPlot->Items.GetIndex(item);
- item->ID = id;
- if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) {
- gp.LegendIndices.push_back(idx);
- item->NameOffset = gp.LegendLabels.size();
- gp.LegendLabels.append(label_id, label_id + strlen(label_id) + 1);
- }
- else {
- item->Show = true;
- }
- 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]);
-}
-
-ImPlotItem* GetLegendItem(const char* label_id) {
- ImGuiID id = ImGui::GetID(label_id);
- return gp.CurrentPlot->Items.GetByKey(id);
-}
-
-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;
-}
-
-//-----------------------------------------------------------------------------
-// Tick Utils
-//-----------------------------------------------------------------------------
-
-// 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 NiceNum(double x, bool round) {
double f; /* fractional part of x */
double nf; /* nice, rounded fraction */
int expv = (int)floor(ImLog10(x));
@@ -680,7 +174,237 @@ inline double NiceNum(double x, bool round) {
return nf * ImPow(10.0, expv);
}
-inline void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) {
+//-----------------------------------------------------------------------------
+// Context Utils
+//-----------------------------------------------------------------------------
+
+ImPlotContext* CreateContext() {
+ ImPlotContext* ctx = IM_NEW(ImPlotContext)();
+ Initialize(ctx);
+ if (GImPlot == NULL)
+ SetCurrentContext(ctx);
+ return ctx;
+}
+
+void DestroyContext(ImPlotContext* ctx) {
+ if (ctx == NULL)
+ ctx = GImPlot;
+ if (GImPlot == ctx)
+ SetCurrentContext(NULL);
+ IM_DELETE(ctx);
+}
+
+ImPlotContext* GetCurrentContext() {
+ return GImPlot;
+}
+
+void SetCurrentContext(ImPlotContext* ctx) {
+ GImPlot = ctx;
+}
+
+void Initialize(ImPlotContext* ctx) {
+ Reset(ctx);
+ SetColormapEx(ImPlotColormap_Default, 0, ctx);
+}
+
+void Reset(ImPlotContext* ctx) {
+ // end child window if it was made
+ if (ctx->ChildWindowMade)
+ ImGui::EndChild();
+ ctx->ChildWindowMade = false;
+ // reset the next plot data
+ ctx->NextPlotData = ImPlotNextPlotData();
+ // reset items count
+ ctx->VisibleItemCount = 0;
+ // reset legend items
+ ctx->LegendIndices.shrink(0);
+ ctx->LegendLabels.Buf.shrink(0);
+ // reset ticks/labels
+ ctx->XTicks.shrink(0);
+ ctx->XTickLabels.Buf.shrink(0);
+ for (int i = 0; i < 3; ++i) {
+ ctx->YTicks[i].shrink(0);
+ ctx->YTickLabels[i].Buf.shrink(0);
+ }
+ // reset extents
+ ctx->FitX = false;
+ ctx->ExtentsX.Min = HUGE_VAL;
+ ctx->ExtentsX.Max = -HUGE_VAL;
+ for (int i = 0; i < MAX_Y_AXES; i++) {
+ ctx->ExtentsY[i].Min = HUGE_VAL;
+ ctx->ExtentsY[i].Max = -HUGE_VAL;
+ ctx->FitY[i] = false;
+ }
+ // reset digital plot items count
+ ctx->DigitalPlotItemCnt = 0;
+ ctx->DigitalPlotOffset = 0;
+ // nullify plot
+ ctx->CurrentPlot = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Plot Utils
+//-----------------------------------------------------------------------------
+
+ImPlotState* GetPlot(const char* title) {
+ ImGuiWindow* Window = GImGui->CurrentWindow;
+ const ImGuiID ID = Window->GetID(title);
+ return GImPlot->Plots.GetByKey(ID);
+}
+
+ImPlotState* GetCurrentPlot() {
+ return GImPlot->CurrentPlot;
+}
+
+void FitPoint(const ImPlotPoint& p) {
+ ImPlotContext& gp = *GImPlot;
+ ImPlotRange* extents_x = &gp.ExtentsX;
+ ImPlotRange* extents_y = &gp.ExtentsY[gp.CurrentPlot->CurrentYAxis];
+ if (!NanOrInf(p.x)) {
+ 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;
+ }
+ if (!NanOrInf(p.y)) {
+ 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;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Coordinate Utils
+//-----------------------------------------------------------------------------
+
+void UpdateTransformCache() {
+ ImPlotContext& gp = *GImPlot;
+ // get pixels for transforms
+ for (int i = 0; i < MAX_Y_AXES; i++) {
+ gp.PixelRange[i] = ImRect(ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.x : gp.BB_Plot.Min.x,
+ ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.y : gp.BB_Plot.Max.y,
+ ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.x : gp.BB_Plot.Max.x,
+ ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.y : gp.BB_Plot.Min.y);
+
+ gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size();
+ }
+ gp.LogDenX = ImLog10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min);
+ for (int i = 0; i < MAX_Y_AXES; i++) {
+ gp.LogDenY[i] = ImLog10(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();
+}
+
+ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
+ const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
+ ImPlotPoint plt;
+ 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;
+ if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
+ 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;
+ }
+ if (ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
+ 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;
+ }
+ return plt;
+}
+
+ImPlotPoint 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.
+ImVec2 PlotToPixels(double x, double y, int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
+ const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
+ ImVec2 pix;
+ if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
+ 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);
+ }
+ if (ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) {
+ double t = ImLog10(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, (float)t);
+ }
+ 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));
+ return pix;
+}
+
+// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
+ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) {
+ return PlotToPixels(plt.x, plt.y, y_axis);
+}
+
+//-----------------------------------------------------------------------------
+// Item Utils
+//-----------------------------------------------------------------------------
+
+ImPlotItem* RegisterItem(const char* label_id) {
+ ImPlotContext& gp = *GImPlot;
+ ImGuiID id = ImGui::GetID(label_id);
+ ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id);
+ if (item->SeenThisFrame)
+ return item;
+ item->SeenThisFrame = true;
+ int idx = gp.CurrentPlot->Items.GetIndex(item);
+ item->ID = id;
+ if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) {
+ gp.LegendIndices.push_back(idx);
+ item->NameOffset = gp.LegendLabels.size();
+ gp.LegendLabels.append(label_id, label_id + strlen(label_id) + 1);
+ }
+ else {
+ item->Show = true;
+ }
+ if (item->Show)
+ gp.VisibleItemCount++;
+ return item;
+}
+
+ImPlotItem* GetItem(int i) {
+ ImPlotContext& gp = *GImPlot;
+ return gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]);
+}
+
+ImPlotItem* GetItem(const char* label_id) {
+ ImPlotContext& gp = *GImPlot;
+ ImGuiID id = ImGui::GetID(label_id);
+ return gp.CurrentPlot->Items.GetByKey(id);
+}
+
+ImPlotItem* GetItem(const char* plot_title, const char* item_label_id) {
+ ImPlotState* plot = GetPlot(plot_title);
+ if (plot) {
+ ImGuiID id = ImGui::GetID(item_label_id);
+ return plot->Items.GetByKey(id);
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Legend Utils
+//-----------------------------------------------------------------------------
+
+int GetLegendCount() {
+ ImPlotContext& gp = *GImPlot;
+ return gp.LegendIndices.size();
+}
+
+const char* GetLegendLabel(int i) {
+ ImPlotContext& gp = *GImPlot;
+ 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;
+}
+
+//-----------------------------------------------------------------------------
+// Tick Utils
+//-----------------------------------------------------------------------------
+
+void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) {
if (logscale) {
if (range.Min <= 0 || range.Max <= 0)
return;
@@ -716,7 +440,7 @@ inline void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bo
}
}
-inline void AddCustomTicks(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer) {
+void AddCustomTicks(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer) {
for (int i = 0; i < n; ++i) {
ImPlotTick tick(values[i],false);
tick.TextOffset = buffer.size();
@@ -729,7 +453,7 @@ inline void AddCustomTicks(const double* values, const char** labels, int n, ImV
}
}
-inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) {
+void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) {
char temp[32];
for (int t = 0; t < ticks.Size; t++) {
ImPlotTick *tk = &ticks[t];
@@ -746,7 +470,7 @@ inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBu
}
}
-inline float MaxTickLabelWidth(ImVector& ticks) {
+float MaxTickLabelWidth(ImVector& ticks) {
float w = 0;
for (int i = 0; i < ticks.Size; ++i)
w = ticks[i].Size.x > w ? ticks[i].Size.x : w;
@@ -759,6 +483,7 @@ class YPadCalculator {
: ImPlotAxisStates(axis_states), MaxLabelWidths(max_label_widths), TxtOff(txt_off) {}
float operator()(int y_axis) {
+ ImPlotContext& gp = *GImPlot;
ImPlotState& plot = *gp.CurrentPlot;
if (!ImPlotAxisStates[y_axis].Present) { return 0; }
// If we have more than 1 axis present before us, then we need
@@ -767,7 +492,7 @@ class YPadCalculator {
if (ImPlotAxisStates[y_axis].PresentSoFar >= 3) {
pad_result += 6.0f;
}
- if (!HasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_TickLabels)) {
+ if (!ImHasFlag(plot.YAxis[y_axis].Flags, ImPlotAxisFlags_TickLabels)) {
return pad_result;
}
pad_result += MaxLabelWidths[y_axis] + TxtOff;
@@ -785,26 +510,20 @@ class YPadCalculator {
//-----------------------------------------------------------------------------
void UpdateAxisColor(int axis_flag, ImPlotAxisColor* col) {
+ ImPlotContext& gp = *GImPlot;
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;
-};
-
//-----------------------------------------------------------------------------
// BeginPlot()
//-----------------------------------------------------------------------------
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) {
-
+ IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!");
// FRONT MATTER -----------------------------------------------------------
@@ -812,7 +531,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
ImGuiContext &G = *GImGui;
ImGuiWindow * Window = G.CurrentWindow;
if (Window->SkipItems) {
- gp.Reset();
+ Reset(GImPlot);
return false;
}
@@ -855,7 +574,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// capture scroll with a child region
const float default_w = 400;
const float default_h = 300;
- if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) {
+ if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) {
ImGui::BeginChild(title, ImVec2(size.x == 0 ? default_w : size.x, size.y == 0 ? default_h : size.y));
Window = ImGui::GetCurrentWindow();
Window->ScrollMax.y = 1.0f;
@@ -889,9 +608,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
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],
- HasFlag(plot.Flags, ImPlotFlags_YAxis2), gp.Y[0].PresentSoFar);
+ ImHasFlag(plot.Flags, ImPlotFlags_YAxis2), gp.Y[0].PresentSoFar);
gp.Y[2] = ImPlotAxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2],
- HasFlag(plot.Flags, ImPlotFlags_YAxis3), gp.Y[1].PresentSoFar);
+ ImHasFlag(plot.Flags, ImPlotFlags_YAxis3), gp.Y[1].PresentSoFar);
gp.LockPlot = gp.X.Lock && gp.Y[0].Lock && gp.Y[1].Lock && gp.Y[2].Lock;
@@ -904,14 +623,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.YAxis[i].Range.Max = ConstrainNan(ConstrainInf(plot.YAxis[i].Range.Max));
}
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
plot.XAxis.Range.Min = ConstrainLog(plot.XAxis.Range.Min);
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
plot.XAxis.Range.Max = ConstrainLog(plot.XAxis.Range.Max);
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
+ if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
plot.YAxis[i].Range.Min = ConstrainLog(plot.YAxis[i].Range.Min);
- if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
+ if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
plot.YAxis[i].Range.Max = ConstrainLog(plot.YAxis[i].Range.Max);
}
@@ -947,7 +666,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
ImGui::ItemSize(gp.BB_Frame);
if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) {
- gp.Reset();
+ Reset(GImPlot);
return false;
}
gp.Hov_Frame = ImGui::ItemHoverable(gp.BB_Frame, ID);
@@ -963,33 +682,33 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
y_divisions[i] = ImMax(2, (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight()));
}
- gp.RenderX = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines) ||
- HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks) ||
- HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) && x_divisions > 1;
+ gp.RenderX = (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines) ||
+ ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks) ||
+ ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) && x_divisions > 1;
for (int i = 0; i < MAX_Y_AXES; i++) {
gp.RenderY[i] =
gp.Y[i].Present &&
- (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) ||
- HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) ||
- HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) && y_divisions[i] > 1;
+ (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) ||
+ ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) ||
+ ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) && y_divisions[i] > 1;
}
// get ticks
if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX)
- AddDefaultTicks(plot.XAxis.Range, x_divisions, 10, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
+ AddDefaultTicks(plot.XAxis.Range, x_divisions, 10, ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks);
for (int i = 0; i < MAX_Y_AXES; i++) {
if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) {
- AddDefaultTicks(plot.YAxis[i].Range, y_divisions[i], 10, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]);
+ AddDefaultTicks(plot.YAxis[i].Range, y_divisions[i], 10, ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]);
}
}
// label ticks
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels))
- LabelTicks(gp.XTicks, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Scientific), gp.XTickLabels);
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels))
+ LabelTicks(gp.XTicks, ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Scientific), gp.XTickLabels);
float max_label_width[MAX_Y_AXES] = {};
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (gp.Y[i].Present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) {
- LabelTicks(gp.YTicks[i], HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific), gp.YTickLabels[i]);
+ if (gp.Y[i].Present && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) {
+ LabelTicks(gp.YTicks[i], ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific), gp.YTickLabels[i]);
max_label_width[i] = MaxTickLabelWidth(gp.YTicks[i]);
}
}
@@ -999,7 +718,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
const float txt_off = 5;
const float txt_height = ImGui::GetTextLineHeight();
const float pad_top = title_size.x > 0.0f ? txt_height + txt_off : 0;
- const float pad_bot = (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels) ? txt_height + txt_off : 0) + (x_label ? txt_height + txt_off : 0);
+ const float pad_bot = (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels) ? txt_height + txt_off : 0) + (x_label ? txt_height + txt_off : 0);
YPadCalculator y_axis_pad(gp.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);
@@ -1037,7 +756,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
const bool any_hov_y_axis_region = plot.YAxis[0].Hovered || plot.YAxis[1].Hovered || plot.YAxis[2].Hovered;
// legend hovered from last frame
- const bool hov_legend = HasFlag(plot.Flags, ImPlotFlags_Legend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
+ const bool hov_legend = ImHasFlag(plot.Flags, ImPlotFlags_Legend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
bool hov_query = false;
if (gp.Hov_Frame && gp.Hov_Plot && plot.Queried && !plot.Querying) {
@@ -1131,7 +850,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
}
// start drag
- if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && HasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) {
+ if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) {
if (plot.XAxis.Hovered) {
plot.XAxis.Dragging = true;
}
@@ -1149,8 +868,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
float zoom_rate = 0.1f;
if (IO.MouseWheel > 0)
zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate));
- float tx = Remap(IO.MousePos.x, gp.BB_Plot.Min.x, gp.BB_Plot.Max.x, 0.0f, 1.0f);
- float ty = Remap(IO.MousePos.y, gp.BB_Plot.Min.y, gp.BB_Plot.Max.y, 0.0f, 1.0f);
+ float tx = ImRemap(IO.MousePos.x, gp.BB_Plot.Min.x, gp.BB_Plot.Max.x, 0.0f, 1.0f);
+ float ty = ImRemap(IO.MousePos.y, gp.BB_Plot.Min.y, gp.BB_Plot.Max.y, 0.0f, 1.0f);
if (plot.XAxis.Hovered && !gp.X.Lock) {
ImPlotAxisScale axis_scale(0, tx, ty, zoom_rate);
const ImPlotPoint& plot_tl = axis_scale.Min;
@@ -1180,7 +899,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
if (plot.Selecting && (IO.MouseReleased[gp.InputMap.BoxSelectButton] || !IO.MouseDown[gp.InputMap.BoxSelectButton])) {
UpdateTransformCache();
ImVec2 select_size = plot.SelectStart - IO.MousePos;
- if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) {
ImPlotPoint p1 = PixelsToPlot(plot.SelectStart);
ImPlotPoint p2 = PixelsToPlot(IO.MousePos);
if (!gp.X.LockMin && IO.KeyMods != gp.InputMap.HorizontalMod)
@@ -1199,7 +918,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.Selecting = false;
}
// bad selection
- if (plot.Selecting && (!HasFlag(plot.Flags, ImPlotFlags_BoxSelect) || gp.LockPlot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) {
+ if (plot.Selecting && (!ImHasFlag(plot.Flags, ImPlotFlags_BoxSelect) || gp.LockPlot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) {
ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
}
// cancel selection
@@ -1207,17 +926,17 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.Selecting = false;
}
// begin selection or query
- if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && HasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) {
+ if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && ImHasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) {
plot.SelectStart = IO.MousePos;
plot.Selecting = true;
}
// update query
if (plot.Querying) {
UpdateTransformCache();
- plot.QueryRect.Min.x = HasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x);
- plot.QueryRect.Max.x = HasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x);
- plot.QueryRect.Min.y = HasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y);
- plot.QueryRect.Max.y = HasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y);
+ plot.QueryRect.Min.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x);
+ plot.QueryRect.Max.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x);
+ plot.QueryRect.Min.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y);
+ plot.QueryRect.Max.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y);
plot.QueryRect.Min -= gp.BB_Plot.Min;
plot.QueryRect.Max -= gp.BB_Plot.Min;
@@ -1234,21 +953,21 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
// begin query
- if (HasFlag(plot.Flags, ImPlotFlags_Query) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.QueryButton] && HasFlag(IO.KeyMods, gp.InputMap.QueryMod)) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.QueryButton] && ImHasFlag(IO.KeyMods, gp.InputMap.QueryMod)) {
plot.QueryRect = ImRect(0,0,0,0);
plot.Querying = true;
plot.Queried = true;
plot.QueryStart = IO.MousePos;
}
// toggle between select/query
- if (HasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && HasFlag(IO.KeyMods,gp.InputMap.QueryToggleMod)) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && ImHasFlag(IO.KeyMods,gp.InputMap.QueryToggleMod)) {
plot.Selecting = false;
plot.QueryRect = ImRect(0,0,0,0);
plot.Querying = true;
plot.Queried = true;
plot.QueryStart = plot.SelectStart;
}
- if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && !HasFlag(IO.KeyMods, gp.InputMap.QueryToggleMod) && !IO.MouseDown[gp.InputMap.QueryButton]) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && !ImHasFlag(IO.KeyMods, gp.InputMap.QueryToggleMod) && !IO.MouseDown[gp.InputMap.QueryButton]) {
plot.Selecting = true;
plot.Querying = false;
plot.Queried = false;
@@ -1310,7 +1029,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
// render grid
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines)) {
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines)) {
for (int t = 0; t < gp.XTicks.Size; t++) {
ImPlotTick *xt = &gp.XTicks[t];
DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Plot.Min.y), ImVec2(xt->PixelPos, gp.BB_Plot.Max.y), xt->Major ? gp.Col_X.Major : gp.Col_X.Minor, 1);
@@ -1318,7 +1037,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (gp.Y[i].Present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines)) {
+ if (gp.Y[i].Present && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines)) {
for (int t = 0; t < gp.YTicks[i].Size; t++) {
ImPlotTick *yt = &gp.YTicks[i][t];
DrawList.AddLine(ImVec2(gp.BB_Plot.Min.x, yt->PixelPos), ImVec2(gp.BB_Plot.Max.x, yt->PixelPos), yt->Major ? gp.Col_Y[i].Major : gp.Col_Y[i].Minor, 1);
@@ -1334,7 +1053,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
// render labels
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) {
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) {
ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true);
for (int t = 0; t < gp.XTicks.Size; t++) {
ImPlotTick *xt = &gp.XTicks[t];
@@ -1351,7 +1070,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
}
ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true);
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (gp.Y[i].Present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) {
+ if (gp.Y[i].Present && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) {
for (int t = 0; t < gp.YTicks[i].Size; t++) {
const float x_start = gp.AxisLabelReference[i] + (i == 0 ? (-txt_off - gp.YTicks[i][t].Size.x) : txt_off);
ImPlotTick *yt = &gp.YTicks[i][t];
@@ -1410,14 +1129,14 @@ inline void EndDisabledControls(bool cond) {
inline void AxisMenu(ImPlotAxisState& state) {
ImGui::PushItemWidth(75);
bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always;
- bool logscale = HasFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
- bool grid = HasFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines);
- bool ticks = HasFlag(state.Axis->Flags, ImPlotAxisFlags_TickMarks);
- bool labels = HasFlag(state.Axis->Flags, ImPlotAxisFlags_TickLabels);
+ bool logscale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
+ bool grid = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines);
+ bool ticks = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_TickMarks);
+ bool labels = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_TickLabels);
BeginDisabledControls(total_lock);
if (ImGui::Checkbox("##LockMin", &state.LockMin))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMin);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMin);
EndDisabledControls(total_lock);
ImGui::SameLine();
@@ -1427,7 +1146,7 @@ inline void AxisMenu(ImPlotAxisState& state) {
BeginDisabledControls(total_lock);
if (ImGui::Checkbox("##LockMax", &state.LockMax))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMax);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMax);
EndDisabledControls(total_lock);
ImGui::SameLine();
@@ -1438,20 +1157,21 @@ inline void AxisMenu(ImPlotAxisState& state) {
ImGui::Separator();
if (ImGui::Checkbox("Invert", &state.Invert))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_Invert);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Invert);
if (ImGui::Checkbox("Log Scale", &logscale))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
ImGui::Separator();
if (ImGui::Checkbox("Grid Lines", &grid))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines);
if (ImGui::Checkbox("Tick Marks", &ticks))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_TickMarks);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_TickMarks);
if (ImGui::Checkbox("Labels", &labels))
- FlipFlag(state.Axis->Flags, ImPlotAxisFlags_TickLabels);
+ ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_TickLabels);
}
void PlotContextMenu(ImPlotState& plot) {
+ ImPlotContext& gp = *GImPlot;
if (ImGui::BeginMenu("X-Axis")) {
ImGui::PushID("X");
AxisMenu(gp.X);
@@ -1459,10 +1179,10 @@ void PlotContextMenu(ImPlotState& plot) {
ImGui::EndMenu();
}
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (i == 1 && !HasFlag(plot.Flags, ImPlotFlags_YAxis2)) {
+ if (i == 1 && !ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)) {
continue;
}
- if (i == 2 && !HasFlag(plot.Flags, ImPlotFlags_YAxis3)) {
+ if (i == 2 && !ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)) {
continue;
}
char buf[10] = {};
@@ -1481,25 +1201,25 @@ void PlotContextMenu(ImPlotState& plot) {
ImGui::Separator();
if ((ImGui::BeginMenu("Settings"))) {
- if (ImGui::MenuItem("Box Select",NULL,HasFlag(plot.Flags, ImPlotFlags_BoxSelect))) {
- FlipFlag(plot.Flags, ImPlotFlags_BoxSelect);
+ if (ImGui::MenuItem("Box Select",NULL,ImHasFlag(plot.Flags, ImPlotFlags_BoxSelect))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_BoxSelect);
}
- if (ImGui::MenuItem("Query",NULL,HasFlag(plot.Flags, ImPlotFlags_Query))) {
- FlipFlag(plot.Flags, ImPlotFlags_Query);
+ if (ImGui::MenuItem("Query",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Query))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_Query);
}
- if (ImGui::MenuItem("Crosshairs",NULL,HasFlag(plot.Flags, ImPlotFlags_Crosshairs))) {
- FlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
+ if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs);
}
- if (ImGui::MenuItem("Mouse Position",NULL,HasFlag(plot.Flags, ImPlotFlags_MousePos))) {
- FlipFlag(plot.Flags, ImPlotFlags_MousePos);
+ if (ImGui::MenuItem("Mouse Position",NULL,ImHasFlag(plot.Flags, ImPlotFlags_MousePos))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_MousePos);
}
- if (ImGui::MenuItem("Anti-Aliased Lines",NULL,HasFlag(plot.Flags, ImPlotFlags_AntiAliased))) {
- FlipFlag(plot.Flags, ImPlotFlags_AntiAliased);
+ if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased);
}
ImGui::EndMenu();
}
- if (ImGui::MenuItem("Legend",NULL,HasFlag(plot.Flags, ImPlotFlags_Legend))) {
- FlipFlag(plot.Flags, ImPlotFlags_Legend);
+ if (ImGui::MenuItem("Legend",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Legend))) {
+ ImFlipFlag(plot.Flags, ImPlotFlags_Legend);
}
#if 0
if (ImGui::BeginMenu("Metrics")) {
@@ -1514,41 +1234,16 @@ void PlotContextMenu(ImPlotState& plot) {
}
-namespace {
-class BufferWriter {
- public:
- BufferWriter(char* buffer, size_t size) : Buffer(buffer), Pos(0), Size(size) {}
-
- void Write(const char* fmt, ...) IM_FMTARGS(2) {
- va_list argp;
- va_start(argp, fmt);
- VWrite(fmt, argp);
- va_end(argp);
- }
-
- private:
- void VWrite(const char* fmt, va_list argp) {
- const int written = ::vsnprintf(&Buffer[Pos], Size - Pos - 1, fmt, argp);
- if (written > 0)
- Pos += ImMin(size_t(written), Size-Pos-1);
- }
-
- char* const Buffer;
- size_t Pos;
- const size_t Size;
-};
-}
-
//-----------------------------------------------------------------------------
// EndPlot()
//-----------------------------------------------------------------------------
void EndPlot() {
-
+ IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Mismatched BeginPlot()/EndPlot()!");
-
- ImPlotState &plot = *gp.CurrentPlot;
ImGuiContext &G = *GImGui;
+ ImPlotState &plot = *gp.CurrentPlot;
ImGuiWindow * Window = G.CurrentWindow;
ImDrawList & DrawList = *Window->DrawList;
const ImGuiIO & IO = ImGui::GetIO();
@@ -1563,7 +1258,7 @@ void EndPlot() {
// render ticks
PushPlotClipRect();
- if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks)) {
+ if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks)) {
for (int t = 0; t < gp.XTicks.Size; t++) {
ImPlotTick *xt = &gp.XTicks[t];
DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Plot.Max.y),ImVec2(xt->PixelPos, gp.BB_Plot.Max.y - (xt->Major ? 10.0f : 5.0f)), gp.Col_Border, 1);
@@ -1578,7 +1273,7 @@ void EndPlot() {
axis_count++;
float x_start = gp.AxisLabelReference[i];
- if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks)) {
+ if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks)) {
float direction = (i == 0) ? 1.0f : -1.0f;
bool no_major = axis_count >= 3;
@@ -1603,7 +1298,7 @@ void EndPlot() {
}
ImGui::PopClipRect();
- // render y-axis drag/drop hover
+ // render y-axis drag/drop hover
if (ImGui::IsDragDropPayloadBeingAccepted()) {
ImRect bb_plot_pad = gp.BB_Plot;
bb_plot_pad.Min.x += 5; bb_plot_pad.Max.x -= 5;
@@ -1625,7 +1320,7 @@ void EndPlot() {
if (plot.Selecting) {
ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart));
bool select_big_enough = ImLengthSqr(select_bb.GetSize()) > 4;
- if (plot.Selecting && !gp.LockPlot && HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && select_big_enough) {
+ if (plot.Selecting && !gp.LockPlot && ImHasFlag(plot.Flags, ImPlotFlags_BoxSelect) && select_big_enough) {
if (IO.KeyAlt && IO.KeyShift) {
DrawList.AddRectFilled(gp.BB_Plot.Min, gp.BB_Plot.Max, gp.Col_SlctBg);
DrawList.AddRect( gp.BB_Plot.Min, gp.BB_Plot.Max, gp.Col_SlctBd);
@@ -1669,7 +1364,7 @@ void EndPlot() {
ImRect legend_content_bb;
int nItems = GetLegendCount();
bool hov_legend = false;
- if (HasFlag(plot.Flags, ImPlotFlags_Legend) && nItems > 0) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_Legend) && nItems > 0) {
// get max width
float max_label_width = 0;
for (int i = 0; i < nItems; ++i) {
@@ -1679,13 +1374,13 @@ void EndPlot() {
}
legend_content_bb = ImRect(gp.BB_Plot.Min + legend_offset, gp.BB_Plot.Min + legend_offset + ImVec2(max_label_width, nItems * txt_ht));
plot.BB_Legend = ImRect(legend_content_bb.Min, legend_content_bb.Max + legend_padding * 2 + ImVec2(legend_icon_size, 0));
- hov_legend = HasFlag(plot.Flags, ImPlotFlags_Legend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
+ hov_legend = ImHasFlag(plot.Flags, ImPlotFlags_Legend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
// render legend box
DrawList.AddRectFilled(plot.BB_Legend.Min, plot.BB_Legend.Max, ImGui::GetColorU32(ImGuiCol_PopupBg));
DrawList.AddRect(plot.BB_Legend.Min, plot.BB_Legend.Max, gp.Col_Border);
// render each legend item
for (int i = 0; i < nItems; ++i) {
- ImPlotItem* item = GetLegendItem(i);
+ ImPlotItem* item = GetItem(i);
ImRect icon_bb;
icon_bb.Min = legend_content_bb.Min + legend_padding + ImVec2(0, i * txt_ht) + ImVec2(2, 2);
icon_bb.Max = legend_content_bb.Min + legend_padding + ImVec2(0, i * txt_ht) + ImVec2(legend_icon_size - 2, legend_icon_size - 2);
@@ -1693,7 +1388,7 @@ void EndPlot() {
label_bb.Min = legend_content_bb.Min + legend_padding + ImVec2(0, i * txt_ht) + ImVec2(2, 2);
label_bb.Max = legend_content_bb.Min + legend_padding + ImVec2(0, i * txt_ht) + ImVec2(legend_content_bb.Max.x, legend_icon_size - 2);
ImU32 col_hl_txt;
- if (HasFlag(plot.Flags, ImPlotFlags_Highlight) && hov_legend && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_Highlight) && hov_legend && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) {
item->Highlight = true;
col_hl_txt = ImGui::GetColorU32(ImLerp(G.Style.Colors[ImGuiCol_Text], item->Color, 0.25f));
}
@@ -1722,7 +1417,7 @@ void EndPlot() {
}
// render crosshairs
- if (HasFlag(plot.Flags, ImPlotFlags_Crosshairs) && gp.Hov_Plot && gp.Hov_Frame &&
+ if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && gp.Hov_Plot && gp.Hov_Frame &&
!(plot.XAxis.Dragging || any_y_dragging) && !plot.Selecting && !plot.Querying && !hov_legend) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
ImVec2 xy = IO.MousePos;
@@ -1741,19 +1436,19 @@ void EndPlot() {
}
// render mouse pos
- if (HasFlag(plot.Flags, ImPlotFlags_MousePos) && gp.Hov_Plot) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_MousePos) && gp.Hov_Plot) {
char buffer[128] = {};
- BufferWriter writer(buffer, sizeof(buffer));
+ ImBufferWriter writer(buffer, sizeof(buffer));
double range_x = gp.XTicks.Size > 1 ? (gp.XTicks[1].PlotPos - gp.XTicks[0].PlotPos) : plot.XAxis.Range.Size();
double range_y = gp.YTicks[0].Size > 1 ? (gp.YTicks[0][1].PlotPos - gp.YTicks[0][0].PlotPos) : plot.YAxis[0].Range.Size();
writer.Write("%.*f,%.*f", Precision(range_x), gp.LastMousePos[0].x, Precision(range_y), gp.LastMousePos[0].y);
- if (HasFlag(plot.Flags, ImPlotFlags_YAxis2)) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)) {
range_y = gp.YTicks[1].Size > 1 ? (gp.YTicks[1][1].PlotPos - gp.YTicks[1][0].PlotPos) : plot.YAxis[1].Range.Size();
writer.Write(",(%.*f)", Precision(range_y), gp.LastMousePos[1].y);
}
- if (HasFlag(plot.Flags, ImPlotFlags_YAxis3)) {
+ if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)) {
range_y = gp.YTicks[2].Size > 1 ? (gp.YTicks[2][1].PlotPos - gp.YTicks[2][0].PlotPos) : plot.YAxis[2].Range.Size();
writer.Write(",(%.*f)", Precision(range_y), gp.LastMousePos[2].y);
}
@@ -1770,17 +1465,17 @@ void EndPlot() {
// FIT DATA --------------------------------------------------------------
if (gp.FitThisFrame && (gp.VisibleItemCount > 0 || plot.Queried)) {
- if (gp.FitX && !HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMin) && !NanOrInf(gp.ExtentsX.Min)) {
+ if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMin) && !NanOrInf(gp.ExtentsX.Min)) {
plot.XAxis.Range.Min = gp.ExtentsX.Min;
}
- if (gp.FitX && !HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMax) && !NanOrInf(gp.ExtentsX.Max)) {
+ if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMax) && !NanOrInf(gp.ExtentsX.Max)) {
plot.XAxis.Range.Max = gp.ExtentsX.Max;
}
for (int i = 0; i < MAX_Y_AXES; i++) {
- if (gp.FitY[i] && !HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMin) && !NanOrInf(gp.ExtentsY[i].Min)) {
+ if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMin) && !NanOrInf(gp.ExtentsY[i].Min)) {
plot.YAxis[i].Range.Min = gp.ExtentsY[i].Min;
}
- if (gp.FitY[i] && !HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMax) && !NanOrInf(gp.ExtentsY[i].Max)) {
+ if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMax) && !NanOrInf(gp.ExtentsY[i].Max)) {
plot.YAxis[i].Range.Max = gp.ExtentsY[i].Max;
}
}
@@ -1788,7 +1483,7 @@ void EndPlot() {
// CONTEXT MENU -----------------------------------------------------------
- if (HasFlag(plot.Flags, ImPlotFlags_ContextMenu) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend)
+ if (ImHasFlag(plot.Flags, ImPlotFlags_ContextMenu) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend)
ImGui::OpenPopup("##Context");
if (ImGui::BeginPopup("##Context")) {
PlotContextMenu(plot);
@@ -1804,20 +1499,26 @@ void EndPlot() {
// Pop ImGui::PushID at the end of BeginPlot
ImGui::PopID();
// Reset context for next plot
- gp.Reset();
+ Reset(GImPlot);
}
//-----------------------------------------------------------------------------
// MISC API
//-----------------------------------------------------------------------------
+ImPlotInputMap& GetInputMap() {
+ return GImPlot->InputMap;
+}
+
void SetNextPlotLimits(double x_min, double x_max, double y_min, double y_max, ImGuiCond cond) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimits() needs to be called before BeginPlot()!");
SetNextPlotLimitsX(x_min, x_max, cond);
SetNextPlotLimitsY(y_min, y_max, cond);
}
void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLSetNextPlotLimitsXimitsY() needs to be called before BeginPlot()!");
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
gp.NextPlotData.HasXRange = true;
@@ -1827,6 +1528,7 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) {
}
void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimitsY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
@@ -1837,6 +1539,7 @@ void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis)
}
void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels, bool show_default) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksX() needs to be called before BeginPlot()!");
gp.NextPlotData.ShowDefaultTicksX = show_default;
AddCustomTicks(values, labels, n_ticks, gp.XTicks, gp.XTickLabels);
@@ -1850,6 +1553,7 @@ void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** lab
}
void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels, bool show_default, int y_axis) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
gp.NextPlotData.ShowDefaultTicksY[y_axis] = show_default;
@@ -1864,22 +1568,26 @@ void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** lab
}
void SetPlotYAxis(int y_axis) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES");
gp.CurrentPlot->CurrentYAxis = y_axis;
}
ImVec2 GetPlotPos() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!");
return gp.BB_Plot.Min;
}
ImVec2 GetPlotSize() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!");
return gp.BB_Plot.GetSize();
}
void PushPlotClipRect() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!");
ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true);
}
@@ -1889,17 +1597,20 @@ void PopPlotClipRect() {
}
bool IsPlotHovered() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!");
return gp.Hov_Plot;
}
bool IsPlotXAxisHovered() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!");
ImRect bb_plot_pad = gp.BB_Plot; bb_plot_pad.Max.y -= 5;
return gp.CurrentPlot->XAxis.Hovered && !bb_plot_pad.Contains(ImGui::GetMousePos());
}
bool IsPlotYAxisHovered(int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotYAxisHovered() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
@@ -1909,6 +1620,7 @@ bool IsPlotYAxisHovered(int y_axis_in) {
}
ImPlotPoint GetPlotMousePos(int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
@@ -1917,6 +1629,7 @@ ImPlotPoint GetPlotMousePos(int y_axis_in) {
ImPlotLimits GetPlotLimits(int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
@@ -1929,11 +1642,13 @@ ImPlotLimits GetPlotLimits(int y_axis_in) {
}
bool IsPlotQueried() {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() needs to be called between BeginPlot() and EndPlot()!");
return gp.CurrentPlot->Queried;
}
ImPlotLimits GetPlotQuery(int y_axis_in) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!");
ImPlotState& plot = *gp.CurrentPlot;
@@ -1952,6 +1667,7 @@ ImPlotLimits GetPlotQuery(int y_axis_in) {
}
bool IsLegendEntryHovered(const char* label_id) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotItemHighlight() needs to be called between BeginPlot() and EndPlot()!");
ImGuiID id = ImGui::GetID(label_id);
ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id);
@@ -1993,10 +1709,12 @@ static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx)
}
ImPlotStyle& GetStyle() {
+ ImPlotContext& gp = *GImPlot;
return gp.Style;
}
void PushStyleColor(ImPlotCol idx, ImU32 col) {
+ ImPlotContext& gp = *GImPlot;
ImGuiColorMod backup;
backup.Col = idx;
backup.BackupValue = gp.Style.Colors[idx];
@@ -2005,6 +1723,7 @@ void PushStyleColor(ImPlotCol idx, ImU32 col) {
}
void PushStyleColor(ImPlotCol idx, const ImVec4& col) {
+ ImPlotContext& gp = *GImPlot;
ImGuiColorMod backup;
backup.Col = idx;
backup.BackupValue = gp.Style.Colors[idx];
@@ -2013,6 +1732,7 @@ void PushStyleColor(ImPlotCol idx, const ImVec4& col) {
}
void PopStyleColor(int count) {
+ ImPlotContext& gp = *GImPlot;
while (count > 0)
{
ImGuiColorMod& backup = gp.ColorModifiers.back();
@@ -2023,6 +1743,7 @@ void PopStyleColor(int count) {
}
void PushStyleVar(ImPlotStyleVar idx, float val) {
+ ImPlotContext& gp = *GImPlot;
const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) {
float* pvar = (float*)var_info->GetVarPtr(&gp.Style);
@@ -2034,6 +1755,7 @@ void PushStyleVar(ImPlotStyleVar idx, float val) {
}
void PushStyleVar(ImPlotStyleVar idx, int val) {
+ ImPlotContext& gp = *GImPlot;
const ImPlotStyleVarInfo* var_info = GetPlotStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_S32 && var_info->Count == 1) {
int* pvar = (int*)var_info->GetVarPtr(&gp.Style);
@@ -2051,6 +1773,7 @@ void PushStyleVar(ImPlotStyleVar idx, int val) {
}
void PopStyleVar(int count) {
+ ImPlotContext& gp = *GImPlot;
while (count > 0) {
ImGuiStyleMod& backup = gp.StyleModifiers.back();
const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx);
@@ -2070,1398 +1793,25 @@ void PopStyleVar(int count) {
}
}
-//-----------------------------------------------------------------------------
-// GETTERS
-//-----------------------------------------------------------------------------
-
-template
-inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) {
- idx = PosMod(offset + idx, count);
- return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride);
-}
-
-template
-struct GetterYs {
- GetterYs(const T* ys, int count, int offset, int stride) {
- Ys = ys;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- Stride = stride;
- }
- const T* Ys;
- int Count;
- int Offset;
- int Stride;
- inline ImPlotPoint operator()(int idx) {
- return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride));
- }
-};
-
-template
-struct GetterXsYs {
- GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) {
- Xs = xs; Ys = ys;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- Stride = stride;
- }
- const T* Xs;
- const T* Ys;
- int Count;
- int Offset;
- int Stride;
- inline ImPlotPoint operator()(int idx) {
- return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride));
- }
-};
-
-template
-struct GetterXsYRef {
- GetterXsYRef(const T* xs, T y_ref, int count, int offset, int stride) {
- Xs = xs;
- YRef = y_ref;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- Stride = stride;
- }
- const T* Xs;
- T YRef;
- int Count;
- int Offset;
- int Stride;
- inline ImPlotPoint operator()(int idx) {
- return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef);
- }
-};
-
-struct GetterImVec2 {
- GetterImVec2(const ImVec2* data, int count, int offset) {
- Data = data;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- }
- inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); }
- const ImVec2* Data;
- int Count;
- int Offset;
-};
-
-struct GetterImPlotPoint {
- GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) {
- Data = data;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- }
- inline ImPlotPoint operator()(int idx) { return Data[idx]; }
- const ImPlotPoint* Data;
- int Count;
- int Offset;
-};
-
-struct GetterFuncPtrImPlotPoint {
- GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) {
- getter = g;
- Data = d;
- Count = count;
- Offset = count ? PosMod(offset, count) : 0;
- }
- inline ImPlotPoint operator()(int idx) { return getter(Data, idx); }
- ImPlotPoint (*getter)(void* data, int idx);
- void* Data;
- int Count;
- int Offset;
-};
-
-template
-struct GetterBarV {
- const T* Ys; T XShift; int Count; int Offset; int Stride;
- GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; }
- inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); }
-};
-
-template
-struct GetterBarH {
- const T* Xs; T YShift; int Count; int Offset; int Stride;
- GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; }
- inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); }
-};
-
-struct ImPlotPointError {
- ImPlotPointError(double _x, double _y, double _neg, double _pos) {
- x = _x; y = _y; neg = _neg; pos = _pos;
- }
- double x, y, neg, pos;
-};
-
-template
-struct GetterError {
- const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride;
- GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) {
- Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride;
- }
- ImPlotPointError operator()(int idx) {
- return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride),
- OffsetAndStride(Ys, idx, Count, Offset, Stride),
- OffsetAndStride(Neg, idx, Count, Offset, Stride),
- OffsetAndStride(Pos, idx, Count, Offset, Stride));
- }
-};
-
-//-----------------------------------------------------------------------------
-// TRANSFORMERS
-//-----------------------------------------------------------------------------
-
-struct TransformerLinLin {
- TransformerLinLin(int y_axis) : YAxis(y_axis) {}
-
- inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
- inline ImVec2 operator()(double x, double y) {
- 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)) );
- }
-
- int YAxis;
-};
-
-struct TransformerLogLin {
- TransformerLogLin(int y_axis) : YAxis(y_axis) {}
-
- inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
- 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);
- 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)) );
- }
-
- int YAxis;
-};
-
-struct TransformerLinLog {
- TransformerLinLog(int y_axis) : YAxis(y_axis) {}
-
- inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
- inline ImVec2 operator()(double x, double y) {
- 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)) );
- }
- int YAxis;
-};
-
-struct TransformerLogLog {
- TransformerLogLog(int y_axis) : YAxis(y_axis) {}
-
- inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
- 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);
- 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)) );
- }
-
- int YAxis;
-};
-
-//-----------------------------------------------------------------------------
-// RENDERERS
-//-----------------------------------------------------------------------------
-
-/// Renders primitive shapes in bulk as efficiently as possible.
-template
-inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList) {
- unsigned int prims = renderer.Prims;
- unsigned int prims_culled = 0;
- unsigned int idx = 0;
- static const unsigned int max_idx = (unsigned int)(ImPow(2.0f, (float)(sizeof(ImDrawIdx) * 8)) - 1);
- const ImVec2 uv = DrawList._Data->TexUvWhitePixel;
- while (prims) {
- // find how many can be reserved up to end of current draw command's limit
- unsigned int cnt = (unsigned int)ImMin(prims, (max_idx - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed);
- // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time
- if (cnt >= ImMin(64u, prims)) {
- if (prims_culled >= cnt)
- prims_culled -= cnt; // reuse previous reservation
- else {
- DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation
- prims_culled = 0;
- }
- }
- else
- {
- if (prims_culled > 0) {
- DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed);
- prims_culled = 0;
- }
- cnt = (unsigned int)ImMin(prims, (max_idx - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed);
- DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command
- }
- prims -= cnt;
- for (unsigned int ie = idx + cnt; idx != ie; ++idx) {
- if (!renderer(DrawList, uv, idx))
- prims_culled++;
- }
- }
- if (prims_culled > 0)
- DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed);
-}
-
-#define SQRT_1_2 0.70710678118f
-#define SQRT_3_2 0.86602540378f
-
-inline void TransformMarker(ImVec2* points, int n, const ImVec2& c, float s) {
- for (int i = 0; i < n; ++i) {
- points[i].x = c.x + points[i].x * s;
- points[i].y = c.y + points[i].y * s;
- }
-}
-
-inline void MarkerGeneral(ImDrawList& DrawList, ImVec2* points, int n, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- TransformMarker(points, n, c, s);
- if (fill)
- DrawList.AddConvexPolyFilled(points, n, col_fill);
- if (outline && !(fill && col_outline == col_fill)) {
- for (int i = 0; i < n; ++i)
- DrawList.AddLine(points[i], points[(i+1)%n], col_outline, weight);
- }
-}
-
-inline void MarkerCircle(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[10] = {ImVec2(1.0f, 0.0f),
- ImVec2(0.809017f, 0.58778524f),
- ImVec2(0.30901697f, 0.95105654f),
- ImVec2(-0.30901703f, 0.9510565f),
- ImVec2(-0.80901706f, 0.5877852f),
- ImVec2(-1.0f, 0.0f),
- ImVec2(-0.80901694f, -0.58778536f),
- ImVec2(-0.3090171f, -0.9510565f),
- ImVec2(0.30901712f, -0.9510565f),
- ImVec2(0.80901694f, -0.5877853f)};
- MarkerGeneral(DrawList, marker, 10, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerDiamond(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};
- MarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerSquare(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[4] = {ImVec2(SQRT_1_2,SQRT_1_2),ImVec2(SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,SQRT_1_2)};
- MarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerUp(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[3] = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f)};
- MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerDown(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[3] = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f)};
- MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerLeft(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[3] = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)};
- MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerRight(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
- ImVec2 marker[3] = {ImVec2(1,0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)};
- MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
-}
-
-inline void MarkerAsterisk(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
- ImVec2 marker[6] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f)};
- TransformMarker(marker, 6, c, s);
- DrawList.AddLine(marker[0], marker[5], col_outline, weight);
- DrawList.AddLine(marker[1], marker[4], col_outline, weight);
- DrawList.AddLine(marker[2], marker[3], col_outline, weight);
-}
-
-inline void MarkerPlus(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
- ImVec2 marker[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};
- TransformMarker(marker, 4, c, s);
- DrawList.AddLine(marker[0], marker[2], col_outline, weight);
- DrawList.AddLine(marker[1], marker[3], col_outline, weight);
-}
-
-inline void MarkerCross(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
- ImVec2 marker[4] = {ImVec2(SQRT_1_2,SQRT_1_2),ImVec2(SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,SQRT_1_2)};
- TransformMarker(marker, 4, c, s);
- DrawList.AddLine(marker[0], marker[2], col_outline, weight);
- DrawList.AddLine(marker[1], marker[3], col_outline, weight);
-}
-
-template
-inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, bool rend_mk_line, ImU32 col_mk_line, bool rend_mk_fill, ImU32 col_mk_fill) {
- for (int i = 0; i < getter.Count; ++i) {
- ImVec2 c = transformer(getter(i));
- if (gp.BB_Plot.Contains(c)) {
- // TODO: Optimize the loop and if statements, this is atrocious
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Circle))
- MarkerCircle(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Square))
- MarkerSquare(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Diamond))
- MarkerDiamond(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Up))
- MarkerUp(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Down))
- MarkerDown(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Left))
- MarkerLeft(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Right))
- MarkerRight(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Cross))
- MarkerCross(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Plus))
- MarkerPlus(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- if (HasFlag(gp.Style.Marker, ImPlotMarker_Asterisk))
- MarkerAsterisk(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
- }
- }
-}
-
-template
-struct LineRenderer {
- inline LineRenderer(Getter _getter, Transformer _transformer, ImU32 col, float weight) :
- getter(_getter),
- transformer(_transformer)
- {
- Prims = getter.Count - 1;
- Col = col;
- Weight = weight;
- p1 = transformer(getter(0));
- }
- inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) {
- ImVec2 p2 = transformer(getter(prim + 1));
- if (!gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) {
- p1 = p2;
- return false;
- }
- float dx = p2.x - p1.x;
- float dy = p2.y - p1.y;
- IM_NORMALIZE2F_OVER_ZERO(dx, dy);
- dx *= (Weight * 0.5f);
- dy *= (Weight * 0.5f);
- DrawList._VtxWritePtr[0].pos.x = p1.x + dy;
- DrawList._VtxWritePtr[0].pos.y = p1.y - dx;
- DrawList._VtxWritePtr[0].uv = uv;
- DrawList._VtxWritePtr[0].col = Col;
- DrawList._VtxWritePtr[1].pos.x = p2.x + dy;
- DrawList._VtxWritePtr[1].pos.y = p2.y - dx;
- DrawList._VtxWritePtr[1].uv = uv;
- DrawList._VtxWritePtr[1].col = Col;
- DrawList._VtxWritePtr[2].pos.x = p2.x - dy;
- DrawList._VtxWritePtr[2].pos.y = p2.y + dx;
- DrawList._VtxWritePtr[2].uv = uv;
- DrawList._VtxWritePtr[2].col = Col;
- DrawList._VtxWritePtr[3].pos.x = p1.x - dy;
- DrawList._VtxWritePtr[3].pos.y = p1.y + dx;
- DrawList._VtxWritePtr[3].uv = uv;
- DrawList._VtxWritePtr[3].col = Col;
- DrawList._VtxWritePtr += 4;
- DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
- DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1);
- DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2);
- DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
- DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2);
- DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3);
- DrawList._IdxWritePtr += 6;
- DrawList._VtxCurrentIdx += 4;
- p1 = p2;
- return true;
- }
- Getter getter;
- Transformer transformer;
- int Prims;
- ImU32 Col;
- float Weight;
- ImVec2 p1;
- static const int IdxConsumed = 6;
- static const int VtxConsumed = 4;
-};
-
-template
-inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col) {
- if (HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased)) {
- ImVec2 p1 = transformer(getter(0));
- for (int i = 0; i < getter.Count; ++i) {
- ImVec2 p2 = transformer(getter(i));
- if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2))))
- DrawList.AddLine(p1, p2, col, line_weight);
- p1 = p2;
- }
- }
- else {
- RenderPrimitives(LineRenderer(getter, transformer, col, line_weight), DrawList);
- }
-}
-
-template
-struct ShadedRenderer {
- ShadedRenderer(Getter1 _getter1, Getter2 _getter2, Transformer _transformer, ImU32 col) :
- getter1(_getter1),
- getter2(_getter2),
- transformer(_transformer),
- Col(col)
- {
- Prims = ImMin(getter1.Count, getter2.Count) - 1;
- p11 = transformer(getter1(0));
- p12 = transformer(getter2(0));
- }
-
- inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) {
- ImVec2 p21 = transformer(getter1(prim+1));
- ImVec2 p22 = transformer(getter2(prim+1));
- const int intersect = (p11.y > p12.y && p22.y > p21.y) || (p12.y > p11.y && p21.y > p22.y);
- ImVec2 intersection = Intersection(p11,p21,p12,p22);
- DrawList._VtxWritePtr[0].pos = p11;
- DrawList._VtxWritePtr[0].uv = uv;
- DrawList._VtxWritePtr[0].col = Col;
- DrawList._VtxWritePtr[1].pos = p21;
- DrawList._VtxWritePtr[1].uv = uv;
- DrawList._VtxWritePtr[1].col = Col;
- DrawList._VtxWritePtr[2].pos = intersection;
- DrawList._VtxWritePtr[2].uv = uv;
- DrawList._VtxWritePtr[2].col = Col;
- DrawList._VtxWritePtr[3].pos = p12;
- DrawList._VtxWritePtr[3].uv = uv;
- DrawList._VtxWritePtr[3].col = Col;
- DrawList._VtxWritePtr[4].pos = p22;
- DrawList._VtxWritePtr[4].uv = uv;
- DrawList._VtxWritePtr[4].col = Col;
- DrawList._VtxWritePtr += 5;
- DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
- DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1 + intersect);
- DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3);
- DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1);
- DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - intersect);
- DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4);
- DrawList._IdxWritePtr += 6;
- DrawList._VtxCurrentIdx += 5;
- p11 = p21;
- p12 = p22;
- return true;
- }
- Getter1 getter1;
- Getter2 getter2;
- Transformer transformer;
- int Prims;
- ImU32 Col;
- ImVec2 p11, p12;
- static const int IdxConsumed = 6;
- static const int VtxConsumed = 5;
-};
-
-//-----------------------------------------------------------------------------
-// RENDERING UTILS
-//-----------------------------------------------------------------------------
-
-// Returns true if a style color is set to be automaticaly determined
-inline bool ColorIsAuto(ImPlotCol idx) {
- return gp.Style.Colors[idx].w == -1;
-}
-// Recolors an item from an the current ImPlotCol if it is not automatic (i.e. alpha != -1)
-inline void TryRecolorItem(ImPlotItem* item, ImPlotCol idx) {
- if (gp.Style.Colors[idx].w != -1)
- item->Color = gp.Style.Colors[idx];
-}
-// Returns true if lines will render (e.g. basic lines, bar outlines)
-inline bool WillLineRender() {
- return gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0;
-}
-// Returns true if fills will render (e.g. shaded plots, bar fills)
-inline bool WillFillRender() {
- return gp.Style.Colors[ImPlotCol_Fill].w != 0 && gp.Style.FillAlpha > 0;
-}
-// Returns true if marker outlines will render
-inline bool WillMarkerOutlineRender() {
- return gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0;
-}
-// Returns true if mark fill will render
-inline bool WillMarkerFillRender() {
- return gp.Style.Colors[ImPlotCol_MarkerFill].w != 0 && gp.Style.FillAlpha > 0;
-}
-// Gets the line color for an item
-inline ImVec4 GetLineColor(ImPlotItem* item) {
- return ColorIsAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line];
-}
-// Gets the fill color for an item
-inline ImVec4 GetItemFillColor(ImPlotItem* item) {
- ImVec4 col = ColorIsAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill];
- col.w *= gp.Style.FillAlpha;
- return col;
-}
-// Gets the marker outline color for an item
-inline ImVec4 GetMarkerOutlineColor(ImPlotItem* item) {
- return ColorIsAuto(ImPlotCol_MarkerOutline) ? GetLineColor(item) : gp.Style.Colors[ImPlotCol_MarkerOutline];
-}
-// Gets the marker fill color for an item
-inline ImVec4 GetMarkerFillColor(ImPlotItem* item) {
- ImVec4 col = ColorIsAuto(ImPlotCol_MarkerFill) ? GetLineColor(item) :gp.Style.Colors[ImPlotCol_MarkerFill];
- col.w *= gp.Style.FillAlpha;
- return col;
-}
-// Gets the error bar color
-inline ImVec4 GetErrorBarColor() {
- return ColorIsAuto(ImPlotCol_ErrorBar) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : gp.Style.Colors[ImPlotCol_ErrorBar];
-}
-
-//-----------------------------------------------------------------------------
-// PLOT LINES / MARKERS
-//-----------------------------------------------------------------------------
-
-template
-inline void PlotEx(const char* label_id, Getter getter)
-{
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotEx() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- TryRecolorItem(item, ImPlotCol_Line);
-
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint p = getter(i);
- FitPoint(p);
- }
- }
-
- ImDrawList& DrawList = *ImGui::GetWindowDrawList();
- ImPlotState* plot = gp.CurrentPlot;
- const int y_axis = plot->CurrentYAxis;
-
- PushPlotClipRect();
- // render line
- if (getter.Count > 1 && WillLineRender()) {
- ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
- const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight;
- if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderLineStrip(getter, TransformerLogLog(y_axis), DrawList, line_weight, col_line);
- else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
- RenderLineStrip(getter, TransformerLogLin(y_axis), DrawList, line_weight, col_line);
- else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderLineStrip(getter, TransformerLinLog(y_axis), DrawList, line_weight, col_line);
- else
- RenderLineStrip(getter, TransformerLinLin(y_axis), DrawList, line_weight, col_line);
- }
- // render markers
- if (gp.Style.Marker != ImPlotMarker_None) {
- const bool rend_mk_line = WillMarkerOutlineRender();
- const bool rend_mk_fill = WillMarkerFillRender();
- const ImU32 col_mk_line = ImGui::GetColorU32(GetMarkerOutlineColor(item));
- const ImU32 col_mk_fill = ImGui::GetColorU32(GetMarkerFillColor(item));
- if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderMarkers(getter, TransformerLogLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
- else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
- RenderMarkers(getter, TransformerLogLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
- else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderMarkers(getter, TransformerLinLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
- else
- RenderMarkers(getter, TransformerLinLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotLine(const char* label_id, const float* values, int count, int offset, int stride) {
- GetterYs getter(values,count,offset,stride);
- PlotEx(label_id, getter);
-}
-
-void PlotLine(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- return PlotEx(label_id, getter);
-}
-
-void PlotLine(const char* label_id, const ImVec2* data, int count, int offset) {
- GetterImVec2 getter(data, count, offset);
- return PlotEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotLine(const char* label_id, const double* values, int count, int offset, int stride) {
- GetterYs getter(values,count,offset,stride);
- PlotEx(label_id, getter);
-}
-
-void PlotLine(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- return PlotEx(label_id, getter);
-}
-
-void PlotLine(const char* label_id, const ImPlotPoint* data, int count, int offset) {
- GetterImPlotPoint getter(data, count, offset);
- return PlotEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// custom
-
-void PlotLine(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) {
- GetterFuncPtrImPlotPoint getter(getter_func,data, count, offset);
- return PlotEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT SCATTER
-//-----------------------------------------------------------------------------
-
-inline int PushScatterStyle() {
- int vars = 1;
- PushStyleVar(ImPlotStyleVar_LineWeight, 0);
- if (GetStyle().Marker == ImPlotMarker_None) {
- PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Circle);
- vars++;
- }
- return vars;
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotScatter(const char* label_id, const float* values, int count, int offset, int stride) {
- int vars = PushScatterStyle();
- PlotLine(label_id, values, count, offset, stride);
- PopStyleVar(vars);
-}
-
-void PlotScatter(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
- int vars = PushScatterStyle();
- PlotLine(label_id, xs, ys, count, offset, stride);
- PopStyleVar(vars);
-}
-
-void PlotScatter(const char* label_id, const ImVec2* data, int count, int offset) {
- int vars = PushScatterStyle();
- PlotLine(label_id, data, count, offset);
- PopStyleVar(vars);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotScatter(const char* label_id, const double* values, int count, int offset, int stride) {
- int vars = PushScatterStyle();
- PlotLine(label_id, values, count, offset, stride);
- PopStyleVar(vars);
-}
-
-void PlotScatter(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
- int vars = PushScatterStyle();
- PlotLine(label_id, xs, ys, count, offset, stride);
- PopStyleVar(vars);
-}
-
-void PlotScatter(const char* label_id, const ImPlotPoint* data, int count, int offset) {
- int vars = PushScatterStyle();
- PlotLine(label_id, data, count, offset);
- PopStyleVar(vars);
-}
-
-//-----------------------------------------------------------------------------
-// custom
-
-void PlotScatter(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset) {
- int vars = PushScatterStyle();
- PlotLine(label_id, getter, data, count, offset);
- PopStyleVar(vars);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT SHADED
-//-----------------------------------------------------------------------------
-
-template
-inline void PlotShadedEx(const char* label_id, Getter1 getter1, Getter2 getter2) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotShaded() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- TryRecolorItem(item, ImPlotCol_Fill);
-
- if (!WillFillRender())
- return;
-
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < ImMin(getter1.Count, getter2.Count); ++i) {
- ImPlotPoint p1 = getter1(i);
- ImPlotPoint p2 = getter2(i);
- FitPoint(p1);
- FitPoint(p2);
- }
- }
-
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
- ImPlotState* plot = gp.CurrentPlot;
- const int y_axis = plot->CurrentYAxis;
-
- ImU32 col = ImGui::GetColorU32(GetItemFillColor(item));
-
- PushPlotClipRect();
- if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(y_axis), col), DrawList);
- else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
- RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(y_axis), col), DrawList);
- else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(y_axis), col), DrawList);
- else
- RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(y_axis), col), DrawList);
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotShaded(const char* label_id, const float* xs, const float* ys1, const float* ys2, int count, int offset, int stride) {
- GetterXsYs getter1(xs, ys1, count, offset, stride);
- GetterXsYs getter2(xs, ys2, count, offset, stride);
- PlotShadedEx(label_id, getter1, getter2);
-}
-
-void PlotShaded(const char* label_id, const float* xs, const float* ys, int count, float y_ref, int offset, int stride) {
- GetterXsYs getter1(xs, ys, count, offset, stride);
- GetterXsYRef getter2(xs, y_ref, count, offset, stride);
- PlotShadedEx(label_id, getter1, getter2);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset, int stride) {
- GetterXsYs getter1(xs, ys1, count, offset, stride);
- GetterXsYs getter2(xs, ys2, count, offset, stride);
- PlotShadedEx(label_id, getter1, getter2);
-}
-
-void PlotShaded(const char* label_id, const double* xs, const double* ys, int count, double y_ref, int offset, int stride) {
- GetterXsYs getter1(xs, ys, count, offset, stride);
- GetterXsYRef getter2(xs, y_ref, count, offset, stride);
- PlotShadedEx(label_id, getter1, getter2);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT BAR V
-//-----------------------------------------------------------------------------
-
-template
-void PlotBarsEx(const char* label_id, Getter getter, TWidth width) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- TryRecolorItem(item, ImPlotCol_Fill);
-
- const TWidth half_width = width / 2;
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint p = getter(i);
- FitPoint(ImPlotPoint(p.x - half_width, p.y));
- FitPoint(ImPlotPoint(p.x + half_width, 0));
- }
- }
-
- ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
- ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item));
- const bool rend_fill = WillFillRender();
- bool rend_line = WillLineRender();
- if (rend_fill && col_line == col_fill)
- rend_line = false;
-
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
- PushPlotClipRect();
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint p = getter(i);
- if (p.y == 0)
- continue;
- ImVec2 a = PlotToPixels(p.x - half_width, p.y);
- ImVec2 b = PlotToPixels(p.x + half_width, 0);
- if (rend_fill)
- DrawList.AddRectFilled(a, b, col_fill);
- if (rend_line)
- DrawList.AddRect(a, b, col_line);
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotBars(const char* label_id, const float* values, int count, float width, float shift, int offset, int stride) {
- GetterBarV getter(values,shift,count,offset,stride);
- PlotBarsEx(label_id, getter, width);
-}
-
-void PlotBars(const char* label_id, const float* xs, const float* ys, int count, float width, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- PlotBarsEx(label_id, getter, width);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotBars(const char* label_id, const double* values, int count, double width, double shift, int offset, int stride) {
- GetterBarV getter(values,shift,count,offset,stride);
- PlotBarsEx(label_id, getter, width);
-}
-
-void PlotBars(const char* label_id, const double* xs, const double* ys, int count, double width, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- PlotBarsEx(label_id, getter, width);
-}
-
-//-----------------------------------------------------------------------------
-// custom
-
-void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double width, int offset) {
- GetterFuncPtrImPlotPoint getter(getter_func, data, count, offset);
- PlotBarsEx(label_id, getter, width);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT BAR H
-//-----------------------------------------------------------------------------
-
-// TODO: Migrate to RenderPrimitives
-
-template
-void PlotBarsHEx(const char* label_id, Getter getter, THeight height) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- TryRecolorItem(item, ImPlotCol_Fill);
-
- const THeight half_height = height / 2;
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint p = getter(i);
- FitPoint(ImPlotPoint(0, p.y - half_height));
- FitPoint(ImPlotPoint(p.x, p.y + half_height));
- }
- }
-
- ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
- ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item));
- const bool rend_fill = WillFillRender();
- bool rend_line = WillLineRender();
- if (rend_fill && col_line == col_fill)
- rend_line = false;
-
- PushPlotClipRect();
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint p = getter(i);
- if (p.x == 0)
- continue;
- ImVec2 a = PlotToPixels(0, p.y - half_height);
- ImVec2 b = PlotToPixels(p.x, p.y + half_height);
- if (rend_fill)
- DrawList.AddRectFilled(a, b, col_fill);
- if (rend_line)
- DrawList.AddRect(a, b, col_line);
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotBarsH(const char* label_id, const float* values, int count, float height, float shift, int offset, int stride) {
- GetterBarH getter(values,shift,count,offset,stride);
- PlotBarsHEx(label_id, getter, height);
-}
-
-void PlotBarsH(const char* label_id, const float* xs, const float* ys, int count, float height, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- PlotBarsHEx(label_id, getter, height);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotBarsH(const char* label_id, const double* values, int count, double height, double shift, int offset, int stride) {
- GetterBarH getter(values,shift,count,offset,stride);
- PlotBarsHEx(label_id, getter, height);
-}
-
-void PlotBarsH(const char* label_id, const double* xs, const double* ys, int count, double height, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- PlotBarsHEx(label_id, getter, height);
-}
-
-//-----------------------------------------------------------------------------
-// custom
-
-void PlotBarsH(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double height, int offset) {
- GetterFuncPtrImPlotPoint getter(getter_func, data, count, offset);
- PlotBarsHEx(label_id, getter, height);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT ERROR BARS
-//-----------------------------------------------------------------------------
-
-template
-void PlotErrorBarsEx(const char* label_id, Getter getter) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
-
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPointError e = getter(i);
- FitPoint(ImPlotPoint(e.x , e.y - e.neg));
- FitPoint(ImPlotPoint(e.x , e.y + e.pos ));
- }
- }
-
- const ImU32 col = ImGui::GetColorU32(GetErrorBarColor());
- const bool rend_whisker = gp.Style.ErrorBarSize > 0;
- const float half_whisker = gp.Style.ErrorBarSize * 0.5f;
-
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
-
- PushPlotClipRect();
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPointError e = getter(i);
- ImVec2 p1 = PlotToPixels(e.x, e.y - e.neg);
- ImVec2 p2 = PlotToPixels(e.x, e.y + e.pos);
- DrawList.AddLine(p1,p2,col, gp.Style.ErrorBarWeight);
- if (rend_whisker) {
- DrawList.AddLine(p1 - ImVec2(half_whisker, 0), p1 + ImVec2(half_whisker, 0), col, gp.Style.ErrorBarWeight);
- DrawList.AddLine(p2 - ImVec2(half_whisker, 0), p2 + ImVec2(half_whisker, 0), col, gp.Style.ErrorBarWeight);
- }
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotErrorBars(const char* label_id, const float* xs, const float* ys, const float* err, int count, int offset, int stride) {
- GetterError getter(xs, ys, err, err, count, offset, stride);
- PlotErrorBarsEx(label_id, getter);
-}
-
-void PlotErrorBars(const char* label_id, const float* xs, const float* ys, const float* neg, const float* pos, int count, int offset, int stride) {
- GetterError getter(xs, ys, neg, pos, count, offset, stride);
- PlotErrorBarsEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotErrorBars(const char* label_id, const double* xs, const double* ys, const double* err, int count, int offset, int stride) {
- GetterError getter(xs, ys, err, err, count, offset, stride);
- PlotErrorBarsEx(label_id, getter);
-}
-
-void PlotErrorBars(const char* label_id, const double* xs, const double* ys, const double* neg, const double* pos, int count, int offset, int stride) {
- GetterError getter(xs, ys, neg, pos, count, offset, stride);
- PlotErrorBarsEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT ERROR BARS H
-//-----------------------------------------------------------------------------
-
-template
-void PlotErrorBarsHEx(const char* label_id, Getter getter) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBarsH() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
-
- // find data extents
- if (gp.FitThisFrame) {
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPointError e = getter(i);
- FitPoint(ImPlotPoint(e.x - e.neg, e.y));
- FitPoint(ImPlotPoint(e.x + e.pos, e.y));
- }
- }
-
- const ImU32 col = ImGui::GetColorU32(GetErrorBarColor());
- const bool rend_whisker = gp.Style.ErrorBarSize > 0;
- const float half_whisker = gp.Style.ErrorBarSize * 0.5f;
-
- ImDrawList& DrawList = *ImGui::GetWindowDrawList();
-
- PushPlotClipRect();
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPointError e = getter(i);
- ImVec2 p1 = PlotToPixels(e.x - e.neg, e.y);
- ImVec2 p2 = PlotToPixels(e.x + e.pos, e.y);
- DrawList.AddLine(p1, p2, col, gp.Style.ErrorBarWeight);
- if (rend_whisker) {
- DrawList.AddLine(p1 - ImVec2(0, half_whisker), p1 + ImVec2(0, half_whisker), col, gp.Style.ErrorBarWeight);
- DrawList.AddLine(p2 - ImVec2(0, half_whisker), p2 + ImVec2(0, half_whisker), col, gp.Style.ErrorBarWeight);
- }
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotErrorBarsH(const char* label_id, const float* xs, const float* ys, const float* err, int count, int offset, int stride) {
- GetterError getter(xs, ys, err, err, count, offset, stride);
- PlotErrorBarsHEx(label_id, getter);
-}
-
-void PlotErrorBarsH(const char* label_id, const float* xs, const float* ys, const float* neg, const float* pos, int count, int offset, int stride) {
- GetterError getter(xs, ys, neg, pos, count, offset, stride);
- PlotErrorBarsHEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, const double* err, int count, int offset, int stride) {
- GetterError getter(xs, ys, err, err, count, offset, stride);
- PlotErrorBarsHEx(label_id, getter);
-}
-
-void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, const double* neg, const double* pos, int count, int offset, int stride) {
- GetterError getter(xs, ys, neg, pos, count, offset, stride);
- PlotErrorBarsHEx(label_id, getter);
-}
-
-
-//-----------------------------------------------------------------------------
-// PLOT PIE CHART
-//-----------------------------------------------------------------------------
-
-inline void RenderPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) {
- static const float resolution = 50 / (2 * IM_PI);
- static ImVec2 buffer[50];
- buffer[0] = PlotToPixels(center);
- int n = ImMax(3, (int)((a1 - a0) * resolution));
- double da = (a1 - a0) / (n - 1);
- for (int i = 0; i < n; ++i) {
- double a = a0 + i * da;
- buffer[i + 1] = PlotToPixels(center.x + radius * cos(a), center.y + radius * sin(a));
- }
- DrawList.AddConvexPolyFilled(buffer, n + 1, col);
-}
-
-template
-void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y, T radius, bool normalize, const char* fmt, T angle0) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
-
- T sum = 0;
- for (int i = 0; i < count; ++i)
- sum += values[i];
-
- normalize = normalize || sum > 1.0f;
-
- ImPlotPoint center(x,y);
-
- PushPlotClipRect();
- T a0 = angle0 * 2 * IM_PI / 360.0f;
- T a1 = angle0 * 2 * IM_PI / 360.0f;
- for (int i = 0; i < count; ++i) {
- ImPlotItem* item = RegisterItem(label_ids[i]);
- ImU32 col = ImGui::GetColorU32(GetItemFillColor(item));
- T percent = normalize ? values[i] / sum : values[i];
- a1 = a0 + 2 * IM_PI * percent;
- if (item->Show) {
- if (percent < 0.5) {
- RenderPieSlice(DrawList, center, radius, a0, a1, col);
- }
- else {
- RenderPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col);
- RenderPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col);
- }
- }
- a0 = a1;
- }
- if (fmt != NULL) {
- a0 = angle0 * 2 * IM_PI / 360.0f;
- a1 = angle0 * 2 * IM_PI / 360.0f;
- char buffer[32];
- for (int i = 0; i < count; ++i) {
- ImPlotItem* item = GetLegendItem(label_ids[i]);
- T percent = normalize ? values[i] / sum : values[i];
- a1 = a0 + 2 * IM_PI * percent;
- if (item->Show) {
- sprintf(buffer, fmt, values[i]);
- ImVec2 size = ImGui::CalcTextSize(buffer);
- T angle = a0 + (a1 - a0) * 0.5f;
- ImVec2 pos = PlotToPixels(center.x + 0.5f * radius * cos(angle), center.y + 0.5f * radius * sin(angle));
- ImU32 col = CalcTextColor(GetItemFillColor(item));
- DrawList.AddText(pos - size * 0.5f, col, buffer);
- }
- a0 = a1;
- }
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotPieChart(const char** label_ids, const float* values, int count, float x, float y, float radius, bool normalize, const char* fmt, float angle0) {
- return PlotPieChartEx(label_ids, values, count, x, y, radius, normalize, fmt, angle0);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotPieChart(const char** label_ids, const double* values, int count, double x, double y, double radius, bool normalize, const char* fmt, double angle0) {
- return PlotPieChartEx(label_ids, values, count, x, y, radius, normalize, fmt, angle0);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT HEATMAP
-//-----------------------------------------------------------------------------
-
-template
-void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, T scale_min, T scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
- const double w = (bounds_max.x - bounds_min.x) / cols;
- const double h = (bounds_max.y - bounds_min.y) / rows;
- const ImPlotPoint half_size(w*0.5,h*0.5);
- int i = 0;
- for (int r = 0; r < rows; ++r) {
- for (int c = 0; c < cols; ++c) {
- ImPlotPoint p;
- p.x = bounds_min.x + 0.5*w + c*w;
- p.y = bounds_max.y - (0.5*h + r*h);
- ImVec2 a = transformer(p.x - half_size.x, p.y - half_size.y);
- ImVec2 b = transformer(p.x + half_size.x, p.y + half_size.y);
- float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1));
- ImVec4 color = LerpColormap(t);
- color.w *= gp.Style.FillAlpha;
- ImU32 col = ImGui::GetColorU32(color);
- DrawList.AddRectFilled(a, b, col);
- i++;
- }
- }
- if (fmt != NULL) {
- i = 0;
- for (int r = 0; r < rows; ++r) {
- for (int c = 0; c < cols; ++c) {
- ImPlotPoint p;
- p.x = bounds_min.x + 0.5*w + c*w;
- p.y = bounds_min.y + 1 - (0.5*h + r*h);
- ImVec2 px = transformer(p);
- char buff[32];
- sprintf(buff, fmt, values[i]);
- ImVec2 size = ImGui::CalcTextSize(buff);
- float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1));
- ImVec4 color = LerpColormap(t);
- ImU32 col = CalcTextColor(color);
- DrawList.AddText(px - size * 0.5f, col, buff);
- i++;
- }
- }
- }
-}
-
-template
-void PlotHeatmapEx(const char* label_id, const T* values, int rows, int cols, T scale_min, T scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() needs to be called between BeginPlot() and EndPlot()!");
- IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!");
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- if (gp.FitThisFrame) {
- FitPoint(bounds_min);
- FitPoint(bounds_max);
- }
- ImDrawList& DrawList = *ImGui::GetWindowDrawList();
- ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true);
- ImPlotState* plot = gp.CurrentPlot;
- int y_axis = plot->CurrentYAxis;
- if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderHeatmap(TransformerLogLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
- else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
- RenderHeatmap(TransformerLogLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
- else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
- RenderHeatmap(TransformerLinLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
- else
- RenderHeatmap(TransformerLinLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
- ImGui::PopClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotHeatmap(const char* label_id, const float* values, int rows, int cols, float scale_min, float scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
- return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
- return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT DIGITAL
-//-----------------------------------------------------------------------------
-
-template
-inline void PlotDigitalEx(const char* label_id, Getter getter)
-{
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() needs to be called between BeginPlot() and EndPlot()!");
-
- ImPlotItem* item = RegisterItem(label_id);
- if (!item->Show)
- return;
- TryRecolorItem(item, ImPlotCol_Line);
-
- // render digital signals as "pixel bases" rectangles
- PushPlotClipRect();
- if (getter.Count > 1 && WillLineRender()) {
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
- const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight;
- const int y_axis = gp.CurrentPlot->CurrentYAxis;
- int pixYMax = 0;
- ImPlotPoint itemData1 = getter(0);
- for (int i = 0; i < getter.Count; ++i) {
- ImPlotPoint itemData2 = getter(i);
- if (NanOrInf(itemData1.y)) {
- itemData1 = itemData2;
- continue;
- }
- if (NanOrInf(itemData2.y)) itemData2.y = ConstrainNan(ConstrainInf(itemData2.y));
- int pixY_0 = (int)(line_weight);
- itemData1.y = ImMax(0.0, itemData1.y);
- float pixY_1_float = gp.Style.DigitalBitHeight * (float)itemData1.y;
- int pixY_1 = (int)(pixY_1_float); //allow only positive values
- int pixY_chPosOffset = (int)(ImMax(gp.Style.DigitalBitHeight, pixY_1_float) + gp.Style.DigitalBitGap);
- pixYMax = ImMax(pixYMax, pixY_chPosOffset);
- ImVec2 pMin = PlotToPixels(itemData1);
- ImVec2 pMax = PlotToPixels(itemData2);
- int pixY_Offset = 20; //20 pixel from bottom due to mouse cursor label
- pMin.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset);
- pMax.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset);
- //plot only one rectangle for same digital state
- while (((i+2) < getter.Count) && (itemData1.y == itemData2.y)) {
- const int in = (i + 1);
- itemData2 = getter(in);
- if (NanOrInf(itemData2.y)) break;
- pMax.x = PlotToPixels(itemData2).x;
- i++;
- }
- //do not extend plot outside plot range
- if (pMin.x < gp.PixelRange[y_axis].Min.x) pMin.x = gp.PixelRange[y_axis].Min.x;
- if (pMax.x < gp.PixelRange[y_axis].Min.x) pMax.x = gp.PixelRange[y_axis].Min.x;
- if (pMin.x > gp.PixelRange[y_axis].Max.x) pMin.x = gp.PixelRange[y_axis].Max.x;
- if (pMax.x > gp.PixelRange[y_axis].Max.x) pMax.x = gp.PixelRange[y_axis].Max.x;
- //plot a rectangle that extends up to x2 with y1 height
- if ((pMax.x > pMin.x) && (gp.BB_Plot.Contains(pMin) || gp.BB_Plot.Contains(pMax))) {
- ImVec4 colAlpha = item->Color;
- colAlpha.w = item->Highlight ? 1.0f : 0.9f;
- DrawList.AddRectFilled(pMin, pMax, ImGui::GetColorU32(colAlpha));
- }
- itemData1 = itemData2;
- }
- gp.DigitalPlotItemCnt++;
- gp.DigitalPlotOffset += pixYMax;
- }
- PopPlotClipRect();
-}
-
-//-----------------------------------------------------------------------------
-// float
-
-void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- return PlotDigitalEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// double
-
-void PlotDigital(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
- GetterXsYs getter(xs,ys,count,offset,stride);
- return PlotDigitalEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// custom
-
-void PlotDigital(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) {
- GetterFuncPtrImPlotPoint getter(getter_func,data,count,offset);
- return PlotDigitalEx(label_id, getter);
-}
-
-//-----------------------------------------------------------------------------
-// PLOT TEXT
-//-----------------------------------------------------------------------------
-// float
-
-void PlotText(const char* text, float x, float y, bool vertical, const ImVec2& pixel_offset) {
- return PlotText(text, (double)x, (double)y, vertical, pixel_offset);
-}
-
-//-----------------------------------------------------------------------------
-// double
-void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& pixel_offset) {
- IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() needs to be called between BeginPlot() and EndPlot()!");
- ImDrawList & DrawList = *ImGui::GetWindowDrawList();
- PushPlotClipRect();
- ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) + pixel_offset;
- ImU32 colTxt = ImGui::GetColorU32(ImGuiCol_Text);
- if (vertical)
- AddTextVertical(&DrawList, text, pos, colTxt);
- else
- DrawList.AddText(pos, colTxt, text);
- PopPlotClipRect();
-}
-
//------------------------------------------------------------------------------
// COLORMAPS
//------------------------------------------------------------------------------
-void SetColormap(const ImVec4* colors, int num_colors) {
- IM_ASSERT_USER_ERROR(num_colors > 1, "The number of colors must be greater than 1!");
- static ImVector user_colormap;
- user_colormap.shrink(0);
- user_colormap.reserve(num_colors);
- for (int i = 0; i < num_colors; ++i)
- user_colormap.push_back(colors[i]);
- gp.Colormap = &user_colormap[0];
- gp.ColormapSize = num_colors;
-}
-
// Returns the size of the current colormap
int GetColormapSize() {
+ ImPlotContext& gp = *GImPlot;
return gp.ColormapSize;
}
// Returns a color from the Color map given an index > 0
ImVec4 GetColormapColor(int index) {
+ ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(index >= 0, "The Colormap index must be greater than zero!");
return gp.Colormap[index % gp.ColormapSize];
}
ImVec4 LerpColormap(float t) {
+ ImPlotContext& gp = *GImPlot;
float tc = ImClamp(t,0.0f,1.0f);
int i1 = (int)((gp.ColormapSize -1 ) * tc);
int i2 = i1 + 1;
@@ -3469,17 +1819,19 @@ ImVec4 LerpColormap(float t) {
return gp.Colormap[i1];
float t1 = (float)i1 / (float)(gp.ColormapSize - 1);
float t2 = (float)i2 / (float)(gp.ColormapSize - 1);
- float tr = Remap(t, t1, t2, 0.0f, 1.0f);
+ float tr = ImRemap(t, t1, t2, 0.0f, 1.0f);
return ImLerp(gp.Colormap[i1], gp.Colormap[i2], tr);
}
ImVec4 NextColormapColor() {
+ ImPlotContext& gp = *GImPlot;
ImVec4 col = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.ColormapSize];
gp.CurrentPlot->ColorIdx++;
return col;
}
void ShowColormapScale(double scale_min, double scale_max, float height) {
+ ImPlotContext& gp = *GImPlot;
static ImVector ticks;
static ImGuiTextBuffer txt_buff;
ImPlotRange range;
@@ -3522,7 +1874,7 @@ void ShowColormapScale(double scale_min, double scale_max, float height) {
ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true);
for (int i = 0; i < ticks.Size; ++i) {
- float ypos = Remap((float)ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y);
+ float ypos = ImRemap((float)ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y);
if (ypos < bb_grad.Max.y - 2 && ypos > bb_grad.Min.y + 2)
DrawList.AddLine(ImVec2(bb_grad.Max.x-1, ypos), ImVec2(bb_grad.Max.x - (ticks[i].Major ? 10.0f : 5.0f), ypos), col_border, 1.0f);
DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks[i].Size.y * 0.5f), ImGui::GetColorU32(ImGuiCol_Text), txt_buff.Buf.Data + ticks[i].TextOffset);
@@ -3533,9 +1885,9 @@ void ShowColormapScale(double scale_min, double scale_max, float height) {
}
-void SetColormap(ImPlotColormap colormap, int samples) {
+void SetColormapEx(ImPlotColormap colormap, int samples, ImPlotContext* ctx) {
static int csizes[ImPlotColormap_COUNT] = {10,9,9,12,11,11,11,11,11,11};
- static OffsetCalculator coffs(csizes);
+ static ImOffsetCalculator coffs(csizes);
static ImVec4 cdata[] = {
// ImPlotColormap_Default // X11 Named Colors
ImVec4(0.0f, 0.7490196228f, 1.0f, 1.0f), // Blues::DeepSkyBlue,
@@ -3654,8 +2006,8 @@ void SetColormap(ImPlotColormap colormap, int samples) {
ImVec4(1.0000f, 0.3333f, 0.f, 1.0f),
ImVec4(1.0000f, 0.f, 0.f, 1.0f)
};
- gp.Colormap = &cdata[coffs.Offsets[colormap]];
- gp.ColormapSize = csizes[colormap];
+ ctx->Colormap = &cdata[coffs.Offsets[colormap]];
+ ctx->ColormapSize = csizes[colormap];
if (samples > 1) {
static ImVector resampled;
resampled.resize(samples);
@@ -3663,8 +2015,27 @@ void SetColormap(ImPlotColormap colormap, int samples) {
float t = i * 1.0f / (samples - 1);
resampled[i] = LerpColormap(t);
}
- SetColormap(&resampled[0], samples);
+ SetColormapEx(&resampled[0], samples, ctx);
}
}
+void SetColormapEx(const ImVec4* colors, int num_colors, ImPlotContext* ctx) {
+ IM_ASSERT_USER_ERROR(num_colors > 1, "The number of colors must be greater than 1!");
+ static ImVector user_colormap;
+ user_colormap.shrink(0);
+ user_colormap.reserve(num_colors);
+ for (int i = 0; i < num_colors; ++i)
+ user_colormap.push_back(colors[i]);
+ ctx->Colormap = &user_colormap[0];
+ ctx->ColormapSize = num_colors;
+}
+
+void SetColormap(ImPlotColormap colormap, int samples) {
+ SetColormapEx(colormap, samples, GImPlot);
+}
+
+void SetColormap(const ImVec4* colors, int num_colors) {
+ SetColormapEx(colors, num_colors, GImPlot);
+}
+
} // namespace ImPlot
diff --git a/implot.h b/implot.h
index 62127bc..621b6b4 100644
--- a/implot.h
+++ b/implot.h
@@ -25,16 +25,22 @@
#pragma once
#include "imgui.h"
+#define IMPLOT_VERSION "0.5 WIP"
+
//-----------------------------------------------------------------------------
-// Basic types and flags
+// Forward Declarations and Basic Types
//-----------------------------------------------------------------------------
-typedef int ImPlotFlags;
-typedef int ImPlotAxisFlags;
-typedef int ImPlotCol;
-typedef int ImPlotStyleVar;
-typedef int ImPlotMarker;
-typedef int ImPlotColormap;
+// Forward declarations
+struct ImPlotContext; // ImPlot context (opaque struct, see implot_internal.h)
+
+// Enums/Flags
+typedef int ImPlotFlags; // -> enum ImPlotFlags_
+typedef int ImPlotAxisFlags; // -> enum ImPlotAxisFlags_
+typedef int ImPlotCol; // -> enum ImPlotCol_
+typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_
+typedef int ImPlotMarker; // -> enum ImPlotMarker_
+typedef int ImPlotColormap; // -> enum ImPlotColormap_
// Options for plots.
enum ImPlotFlags_ {
@@ -133,7 +139,7 @@ enum ImPlotColormap_ {
struct ImPlotPoint {
double x, y;
ImPlotPoint() { x = y = 0.0; }
- ImPlotPoint(double _x, double _y) { x = _x; y = _y; }
+ ImPlotPoint(double _x, double _y) { x = _x; y = _y; }
double operator[] (size_t idx) const { return (&x)[idx]; }
double& operator[] (size_t idx) { return (&x)[idx]; }
#ifdef IMPLOT_POINT_CLASS_EXTRA
@@ -145,16 +151,15 @@ struct ImPlotPoint {
struct ImPlotRange {
double Min, Max;
ImPlotRange();
- bool Contains(double value) const;
- double Size() const;
+ bool Contains(double value) const { return value >= Min && value <= Max; };
+ double Size() const { return Max - Min; };
};
// Combination of two ranges for X and Y axes.
struct ImPlotLimits {
ImPlotRange X, Y;
- ImPlotLimits();
- bool Contains(const ImPlotPoint& p) const;
- bool Contains(double x, double y) const;
+ bool Contains(const ImPlotPoint& p) const { return Contains(p.x, p.y); }
+ bool Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); }
};
// Plot style structure
@@ -190,11 +195,28 @@ struct ImPlotInputMap {
};
//-----------------------------------------------------------------------------
-// Begin/End Plot
+// ImPlot End-User API
//-----------------------------------------------------------------------------
namespace ImPlot {
+//-----------------------------------------------------------------------------
+// ImPlot Context
+//-----------------------------------------------------------------------------
+
+// Creates a new ImPlot context. Call this after ImGui::CreateContext.
+ImPlotContext* CreateContext();
+// Destroys an ImPlot context. Call this before ImGui::DestroyContext. NULL = destroy current context
+void DestroyContext(ImPlotContext* ctx = NULL);
+// Returns the current context. NULL if not context has ben set.
+ImPlotContext* GetCurrentContext();
+// Sets the current context.
+void SetCurrentContext(ImPlotContext* ctx);
+
+//-----------------------------------------------------------------------------
+// Begin/End Plot
+//-----------------------------------------------------------------------------
+
// Starts a 2D plotting context. If this function returns true, EndPlot() must
// be called, e.g. "if (BeginPlot(...)) { ... EndPlot(); }". #title_id must
// be unique. If you need to avoid ID collisions or don't want to display a
@@ -346,7 +368,7 @@ int GetColormapSize();
ImVec4 GetColormapColor(int index);
// Linearly interpolates a color from the current colormap given t between 0 and 1.
ImVec4 LerpColormap(float t);
-// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired.
+// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired.
ImVec4 NextColormapColor();
//-----------------------------------------------------------------------------
@@ -363,6 +385,7 @@ void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond = ImGuiCond_O
// Set the X axis ticks and optionally the labels for the next plot.
void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false);
void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels = NULL, bool show_default = false);
+
// Set the Y axis ticks and optionally the labels for the next plot.
void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0);
void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0);
@@ -377,8 +400,11 @@ ImVec2 GetPlotSize();
// Convert pixels to a position in the current plot's coordinate system. A negative y_axis uses the current value of SetPlotYAxis (0 initially).
ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis = -1);
+ImPlotPoint PixelsToPlot(float x, float y, int y_axis = -1);
+
// Convert a position in the current plot's coordinate system to pixels. A negative y_axis uses the current value of SetPlotYAxis (0 initially).
ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis = -1);
+ImVec2 PlotToPixels(double x, double y, int y_axis = -1);
// Renders a vertical color scale using the current color map
void ShowColormapScale(double scale_min, double scale_max, float height);
diff --git a/implot_internal.h b/implot_internal.h
new file mode 100644
index 0000000..9307a77
--- /dev/null
+++ b/implot_internal.h
@@ -0,0 +1,447 @@
+// 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.
+
+// ImPlot v0.5 WIP
+
+// You may use this file to debug, understand or extend ImPlot features but we don't provide any guarantee of forward compatibility!
+
+//-----------------------------------------------------------------------------
+// [SECTION] Header mess
+//-----------------------------------------------------------------------------
+
+#pragma once
+
+#ifndef IMGUI_DEFINE_MATH_OPERATORS
+#define IMGUI_DEFINE_MATH_OPERATORS
+#endif
+
+#include "imgui_internal.h"
+
+#ifndef IMPLOT_VERSION
+#error Must include implot.h before implot_internal.h
+#endif
+
+//-----------------------------------------------------------------------------
+// [SECTION] Forward declarations
+//-----------------------------------------------------------------------------
+
+struct ImPlotTick;
+struct ImPlotAxis;
+struct ImPlotAxisState;
+struct ImPlotAxisColor;
+struct ImPlotItem;
+struct ImPlotState;
+struct ImPlotNextPlotData;
+
+//-----------------------------------------------------------------------------
+// [SECTION] Context pointer
+//-----------------------------------------------------------------------------
+
+extern ImPlotContext* GImPlot; // Current implicit context pointer
+
+//-----------------------------------------------------------------------------
+// [SECTION] Macros
+//-----------------------------------------------------------------------------
+
+// The maximum number of supported y-axes (DO NOT CHANGE THIS)
+#define MAX_Y_AXES 3
+
+//-----------------------------------------------------------------------------
+// [SECTION] Generic helpers
+//-----------------------------------------------------------------------------
+
+// Computes the common (base-10) logarithm
+static inline float ImLog10(float x) { return log10f(x); }
+static inline double ImLog10(double x) { return log10(x); }
+
+// Returns true if a flag is set
+template
+inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; }
+
+// Flips a flag in a flagset
+template
+inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~flag : set |= flag; }
+
+// Linearly remaps x from [x0 x1] to [y0 y1].
+template
+inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); }
+
+// Returns always positive modulo (r != 0)
+inline int ImPosMod(int l, int r) { return (l % r + r) % r; }
+
+// Offset calculator helper
+template
+struct ImOffsetCalculator {
+ ImOffsetCalculator(int* sizes) {
+ Offsets[0] = 0;
+ for (int i = 1; i < Count; ++i)
+ Offsets[i] = Offsets[i-1] + sizes[i-1];
+ }
+ int Offsets[Count];
+};
+
+// Character buffer writer helper
+struct ImBufferWriter
+{
+ char* const Buffer;
+ size_t Pos;
+ const size_t Size;
+
+ ImBufferWriter(char* buffer, size_t size) : Buffer(buffer), Pos(0), Size(size) {}
+
+ void Write(const char* fmt, ...) IM_FMTARGS(2) {
+ va_list argp;
+ va_start(argp, fmt);
+ const int written = ::vsnprintf(&Buffer[Pos], Size - Pos - 1, fmt, argp);
+ if (written > 0)
+ Pos += ImMin(size_t(written), Size-Pos-1);
+ va_end(argp);
+ }
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] ImPlot Structs
+//-----------------------------------------------------------------------------
+
+// Tick mark info
+struct ImPlotTick
+{
+ double PlotPos;
+ float PixelPos;
+ ImVec2 Size;
+ int TextOffset;
+ bool Major;
+ bool RenderLabel;
+ bool Labeled;
+
+ ImPlotTick(double value, bool major, bool render_label = true) {
+ PlotPos = value;
+ Major = major;
+ RenderLabel = render_label;
+ Labeled = false;
+ }
+};
+
+// Axis state information that must persist after EndPlot
+struct ImPlotAxis
+{
+ bool Dragging;
+ bool Hovered;
+ ImPlotRange Range;
+ ImPlotAxisFlags Flags, PreviousFlags;
+ ImPlotAxis() {
+ Dragging = false;
+ Hovered = false;
+ Range.Min = 0;
+ Range.Max = 1;
+ Flags = PreviousFlags = ImPlotAxisFlags_Default;
+ }
+};
+
+// Axis state information only needed between BeginPlot/EndPlot
+struct ImPlotAxisState
+{
+ 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(ImHasFlag(Axis->Flags, ImPlotAxisFlags_Invert)),
+ LockMin(ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always)),
+ LockMax(ImHasFlag(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()
+ {}
+};
+
+struct ImPlotAxisColor
+{
+ ImU32 Major, Minor, Txt;
+ ImPlotAxisColor() { Major = Minor = Txt = 0; }
+};
+
+// State information for Plot items
+struct ImPlotItem
+{
+ bool Show;
+ bool SeenThisFrame;
+ bool Highlight;
+ ImVec4 Color;
+ int NameOffset;
+ ImGuiID ID;
+
+ ImPlotItem() {
+ Show = true;
+ SeenThisFrame = false;
+ Highlight = false;
+ Color = ImPlot::NextColormapColor();
+ NameOffset = -1;
+ ID = 0;
+ }
+
+ ~ImPlotItem() { ID = 0; }
+};
+
+// Holds Plot state information that must persist after EndPlot
+struct ImPlotState
+{
+ ImPool Items;
+ ImRect BB_Legend;
+ ImVec2 SelectStart;
+ bool Selecting;
+ bool Querying;
+ bool Queried;
+ bool DraggingQuery;
+ ImVec2 QueryStart;
+ ImRect QueryRect;
+ ImPlotAxis XAxis;
+ ImPlotAxis YAxis[MAX_Y_AXES];
+ ImPlotFlags Flags, PreviousFlags;
+ int ColorIdx;
+ int CurrentYAxis;
+
+ ImPlotState() {
+ Selecting = Querying = Queried = DraggingQuery = false;
+ SelectStart = QueryStart = ImVec2(0,0);
+ Flags = PreviousFlags = ImPlotFlags_Default;
+ ColorIdx = 0;
+ CurrentYAxis = 0;
+ }
+};
+
+// Temporary data storage for upcoming plot
+struct ImPlotNextPlotData
+{
+ ImGuiCond XRangeCond;
+ ImGuiCond YRangeCond[MAX_Y_AXES];
+ ImPlotRange X;
+ ImPlotRange Y[MAX_Y_AXES];
+ bool HasXRange;
+ bool HasYRange[MAX_Y_AXES];
+ bool ShowDefaultTicksX;
+ bool ShowDefaultTicksY[MAX_Y_AXES];
+
+ ImPlotNextPlotData() {
+ HasXRange = false;
+ ShowDefaultTicksX = true;
+ for (int i = 0; i < MAX_Y_AXES; ++i) {
+ HasYRange[i] = false;
+ ShowDefaultTicksY[i] = true;
+ }
+ }
+};
+
+// Holds state information that must persist between calls to BeginPlot()/EndPlot()
+struct ImPlotContext {
+ // Plot States
+ ImPool Plots;
+ ImPlotState* CurrentPlot;
+
+ // Legend
+ ImVector LegendIndices;
+ ImGuiTextBuffer LegendLabels;
+
+ // Bounding Boxes
+ ImRect BB_Frame;
+ ImRect BB_Canvas;
+ ImRect BB_Plot;
+
+ // Cached Colors
+ ImU32 Col_Frame;
+ ImU32 Col_Bg;
+ ImU32 Col_Border;
+ ImU32 Col_Txt;
+ ImU32 Col_TxtDis;
+ ImU32 Col_SlctBg;
+ ImU32 Col_SlctBd;
+ ImU32 Col_QryBg;
+ ImU32 Col_QryBd;
+
+ // Axis States
+ ImPlotAxisColor Col_X;
+ ImPlotAxisColor Col_Y[MAX_Y_AXES];
+ ImPlotAxisState X;
+ ImPlotAxisState Y[MAX_Y_AXES];
+
+ // Tick Marks and Labels
+ ImVector XTicks;
+ ImVector YTicks[MAX_Y_AXES];
+ ImGuiTextBuffer XTickLabels;
+ ImGuiTextBuffer YTickLabels[MAX_Y_AXES];
+ float AxisLabelReference[MAX_Y_AXES];
+
+ // Transformations and Data Extents
+ ImRect PixelRange[MAX_Y_AXES];
+ double Mx;
+ double My[MAX_Y_AXES];
+ double LogDenX;
+ double LogDenY[MAX_Y_AXES];
+ ImPlotRange ExtentsX;
+ ImPlotRange ExtentsY[MAX_Y_AXES];
+
+ // Data Fitting Flags
+ bool FitThisFrame;
+ bool FitX;
+ bool FitY[MAX_Y_AXES];
+
+ // Hover states
+ bool Hov_Frame;
+ bool Hov_Plot;
+
+ // Axis Rendering Flags
+ bool RenderX;
+ bool RenderY[MAX_Y_AXES];
+
+ // Axis Locking Flags
+ bool LockPlot;
+ bool ChildWindowMade;
+
+ // Style and Colormaps
+ ImPlotStyle Style;
+ ImVector ColorModifiers;
+ ImVector StyleModifiers;
+ ImVec4* Colormap;
+ int ColormapSize;
+
+ // Misc
+ int VisibleItemCount;
+ int DigitalPlotItemCnt;
+ int DigitalPlotOffset;
+ ImPlotNextPlotData NextPlotData;
+ ImPlotInputMap InputMap;
+ ImPlotPoint LastMousePos[MAX_Y_AXES];
+};
+
+struct ImPlotAxisScale {
+ ImPlotAxisScale(int y_axis, float tx, float ty, float zoom_rate) {
+ ImPlotContext& gp = *GImPlot;
+ Min = ImPlot::PixelsToPlot(gp.BB_Plot.Min - gp.BB_Plot.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), y_axis);
+ Max = ImPlot::PixelsToPlot(gp.BB_Plot.Max + gp.BB_Plot.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), y_axis);
+ }
+ ImPlotPoint Min, Max;
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] Internal API
+// No guarantee of forward compatibility here!
+//-----------------------------------------------------------------------------
+
+namespace ImPlot {
+
+// Initializes an ImPlotContext
+void Initialize(ImPlotContext* ctx);
+// Resets an ImPlot context for the next call to BeginPlot
+void Reset(ImPlotContext* ctx);
+// Gets a plot from the current ImPlotContext
+ImPlotState* GetPlot(const char* title);
+// Gets the current plot from the current ImPlotContext
+ImPlotState* GetCurrentPlot();
+// Updates pixel space to plot space transformation variables for the current plot
+void UpdateTransformCache();
+// Extends the current plots axes so that it encompasses point p
+void FitPoint(const ImPlotPoint& p);
+// Register or get an existing item from the current plot
+ImPlotItem* RegisterItem(const char* label_id);
+// Get the ith plot item from the current plot
+ImPlotItem* GetItem(int i);
+// Get a plot item from the current plot
+ImPlotItem* GetItem(const char* label_id);
+// Gets a plot item from a specific plot
+ImPlotItem* GetItem(const char* plot_title, const char* item_label_id);
+// Returns the number of entries in the current legend
+int GetLegendCount();
+// Gets the ith entry string for the current legend
+const char* GetLegendLabel(int i);
+// Populates a list of ImPlotTicks with automatically spaced ticks
+void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out);
+// Populates a list of ImPlotTicks with custom spaced and labeled ticks
+void AddCustomTicks(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer);
+// Creates label information for a list of ImPlotTick
+void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer);
+// Calculates the maximum width of a list of ImPlotTick
+float MaxTickLabelWidth(ImVector& ticks);
+// Updates axis ticks, lins, and label colors
+void UpdateAxisColor(int axis_flag, ImPlotAxisColor* col);
+// Sets the colormap for a particular ImPlotContext
+void SetColormapEx(ImPlotColormap colormap, int samples, ImPlotContext* ctx);
+void SetColormapEx(const ImVec4* colors, int num_colors, ImPlotContext* ctx);
+// Draws vertical text. The position is the bottom left of the text rect.
+void AddTextVertical(ImDrawList *DrawList, const char *text, ImVec2 pos, ImU32 text_color);
+// Calculates the size of vertical text
+ImVec2 CalcTextSizeVertical(const char *text);
+// Returns white or black text given background color
+inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.729 ? IM_COL32_BLACK : IM_COL32_WHITE; }
+// 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"
+double NiceNum(double x, bool round);
+// Turns NANs to 0s
+inline double ConstrainNan(double val) { return isnan(val) ? 0 : val; }
+// Turns infinity to floating point maximums
+inline double ConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : val == -HUGE_VAL ? - DBL_MAX : val; }
+// Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?)
+inline double ConstrainLog(double val) { return val <= 0 ? 0.001f : val; }
+// Returns true if val is NAN or INFINITY
+inline bool NanOrInf(double val) { return val == HUGE_VAL || val == -HUGE_VAL || isnan(val); }
+// 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; }
+// Returns a floating point precision to use given a value
+inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); }
+// Returns the intersection point of two lines A and B (assumes they are not parallel!)
+inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) {
+ float v1 = (a1.x * a2.y - a1.y * a2.x);
+ float v2 = (b1.x * b2.y - b1.y * b2.x);
+ float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x));
+ return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3);
+}
+// Fills a buffer with n samples linear interpolated from vmin to vmax
+template
+void FillRange(ImVector& 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;
+ }
+}
+// Offsets and strides a data buffer
+template
+inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) {
+ idx = ImPosMod(offset + idx, count);
+ return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride);
+}
+
+} // namespace ImPlot
\ No newline at end of file
diff --git a/implot_items.cpp b/implot_items.cpp
new file mode 100644
index 0000000..5837e52
--- /dev/null
+++ b/implot_items.cpp
@@ -0,0 +1,1442 @@
+// 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.
+
+// ImPlot v0.5 WIP
+
+#include "implot.h"
+#include "implot_internal.h"
+
+#ifdef _MSC_VER
+#define sprintf sprintf_s
+#endif
+
+#define SQRT_1_2 0.70710678118f
+#define SQRT_3_2 0.86602540378f
+
+#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; \
+ } \
+ }
+
+namespace ImPlot {
+
+//-----------------------------------------------------------------------------
+// GETTERS
+//-----------------------------------------------------------------------------
+
+template
+struct GetterYs {
+ GetterYs(const T* ys, int count, int offset, int stride) {
+ Ys = ys;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ Stride = stride;
+ }
+ const T* Ys;
+ int Count;
+ int Offset;
+ int Stride;
+ inline ImPlotPoint operator()(int idx) {
+ return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride));
+ }
+};
+
+template
+struct GetterXsYs {
+ GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) {
+ Xs = xs; Ys = ys;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ Stride = stride;
+ }
+ const T* Xs;
+ const T* Ys;
+ int Count;
+ int Offset;
+ int Stride;
+ inline ImPlotPoint operator()(int idx) {
+ return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride));
+ }
+};
+
+template
+struct GetterXsYRef {
+ GetterXsYRef(const T* xs, T y_ref, int count, int offset, int stride) {
+ Xs = xs;
+ YRef = y_ref;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ Stride = stride;
+ }
+ const T* Xs;
+ T YRef;
+ int Count;
+ int Offset;
+ int Stride;
+ inline ImPlotPoint operator()(int idx) {
+ return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef);
+ }
+};
+
+struct GetterImVec2 {
+ GetterImVec2(const ImVec2* data, int count, int offset) {
+ Data = data;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ }
+ inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); }
+ const ImVec2* Data;
+ int Count;
+ int Offset;
+};
+
+struct GetterImPlotPoint {
+ GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) {
+ Data = data;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ }
+ inline ImPlotPoint operator()(int idx) { return Data[idx]; }
+ const ImPlotPoint* Data;
+ int Count;
+ int Offset;
+};
+
+struct GetterFuncPtrImPlotPoint {
+ GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) {
+ getter = g;
+ Data = d;
+ Count = count;
+ Offset = count ? ImPosMod(offset, count) : 0;
+ }
+ inline ImPlotPoint operator()(int idx) { return getter(Data, idx); }
+ ImPlotPoint (*getter)(void* data, int idx);
+ void* Data;
+ int Count;
+ int Offset;
+};
+
+template
+struct GetterBarV {
+ const T* Ys; T XShift; int Count; int Offset; int Stride;
+ GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; }
+ inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); }
+};
+
+template
+struct GetterBarH {
+ const T* Xs; T YShift; int Count; int Offset; int Stride;
+ GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; }
+ inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); }
+};
+
+struct ImPlotPointError {
+ ImPlotPointError(double _x, double _y, double _neg, double _pos) {
+ x = _x; y = _y; neg = _neg; pos = _pos;
+ }
+ double x, y, neg, pos;
+};
+
+template
+struct GetterError {
+ const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride;
+ GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) {
+ Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride;
+ }
+ ImPlotPointError operator()(int idx) {
+ return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride),
+ OffsetAndStride(Ys, idx, Count, Offset, Stride),
+ OffsetAndStride(Neg, idx, Count, Offset, Stride),
+ OffsetAndStride(Pos, idx, Count, Offset, Stride));
+ }
+};
+
+//-----------------------------------------------------------------------------
+// TRANSFORMERS
+//-----------------------------------------------------------------------------
+
+struct TransformerLinLin {
+ TransformerLinLin(int y_axis) : YAxis(y_axis) {}
+
+ inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
+ inline ImVec2 operator()(double x, double y) {
+ ImPlotContext& gp = *GImPlot;
+ 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)) );
+ }
+
+ int YAxis;
+};
+
+struct TransformerLogLin {
+ TransformerLogLin(int y_axis) : YAxis(y_axis) {}
+
+ inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
+ inline ImVec2 operator()(double x, double y) {
+ ImPlotContext& gp = *GImPlot;
+ 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);
+ 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)) );
+ }
+
+ int YAxis;
+};
+
+struct TransformerLinLog {
+ TransformerLinLog(int y_axis) : YAxis(y_axis) {}
+
+ inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
+ inline ImVec2 operator()(double x, double y) {
+ ImPlotContext& gp = *GImPlot;
+ 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)) );
+ }
+ int YAxis;
+};
+
+struct TransformerLogLog {
+ TransformerLogLog(int y_axis) : YAxis(y_axis) {}
+
+ inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); }
+ inline ImVec2 operator()(double x, double y) {
+ ImPlotContext& gp = *GImPlot;
+ 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);
+ 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)) );
+ }
+
+ int YAxis;
+};
+
+//-----------------------------------------------------------------------------
+// RENDERERS
+//-----------------------------------------------------------------------------
+
+/// Renders primitive shapes in bulk as efficiently as possible.
+template
+inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList) {
+ unsigned int prims = renderer.Prims;
+ unsigned int prims_culled = 0;
+ unsigned int idx = 0;
+ static const unsigned int max_idx = (unsigned int)(ImPow(2.0f, (float)(sizeof(ImDrawIdx) * 8)) - 1);
+ const ImVec2 uv = DrawList._Data->TexUvWhitePixel;
+ while (prims) {
+ // find how many can be reserved up to end of current draw command's limit
+ unsigned int cnt = (unsigned int)ImMin(prims, (max_idx - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed);
+ // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time
+ if (cnt >= ImMin(64u, prims)) {
+ if (prims_culled >= cnt)
+ prims_culled -= cnt; // reuse previous reservation
+ else {
+ DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation
+ prims_culled = 0;
+ }
+ }
+ else
+ {
+ if (prims_culled > 0) {
+ DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed);
+ prims_culled = 0;
+ }
+ cnt = (unsigned int)ImMin(prims, (max_idx - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed);
+ DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command
+ }
+ prims -= cnt;
+ for (unsigned int ie = idx + cnt; idx != ie; ++idx) {
+ if (!renderer(DrawList, uv, idx))
+ prims_culled++;
+ }
+ }
+ if (prims_culled > 0)
+ DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed);
+}
+
+inline void TransformMarker(ImVec2* points, int n, const ImVec2& c, float s) {
+ for (int i = 0; i < n; ++i) {
+ points[i].x = c.x + points[i].x * s;
+ points[i].y = c.y + points[i].y * s;
+ }
+}
+
+inline void MarkerGeneral(ImDrawList& DrawList, ImVec2* points, int n, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ TransformMarker(points, n, c, s);
+ if (fill)
+ DrawList.AddConvexPolyFilled(points, n, col_fill);
+ if (outline && !(fill && col_outline == col_fill)) {
+ for (int i = 0; i < n; ++i)
+ DrawList.AddLine(points[i], points[(i+1)%n], col_outline, weight);
+ }
+}
+
+inline void MarkerCircle(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[10] = {ImVec2(1.0f, 0.0f),
+ ImVec2(0.809017f, 0.58778524f),
+ ImVec2(0.30901697f, 0.95105654f),
+ ImVec2(-0.30901703f, 0.9510565f),
+ ImVec2(-0.80901706f, 0.5877852f),
+ ImVec2(-1.0f, 0.0f),
+ ImVec2(-0.80901694f, -0.58778536f),
+ ImVec2(-0.3090171f, -0.9510565f),
+ ImVec2(0.30901712f, -0.9510565f),
+ ImVec2(0.80901694f, -0.5877853f)};
+ MarkerGeneral(DrawList, marker, 10, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerDiamond(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};
+ MarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerSquare(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[4] = {ImVec2(SQRT_1_2,SQRT_1_2),ImVec2(SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,SQRT_1_2)};
+ MarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerUp(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[3] = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f)};
+ MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerDown(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[3] = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f)};
+ MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerLeft(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[3] = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)};
+ MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerRight(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) {
+ ImVec2 marker[3] = {ImVec2(1,0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)};
+ MarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight);
+}
+
+inline void MarkerAsterisk(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
+ ImVec2 marker[6] = {ImVec2(SQRT_3_2, 0.5f), ImVec2(0, -1), ImVec2(-SQRT_3_2, 0.5f), ImVec2(SQRT_3_2, -0.5f), ImVec2(0, 1), ImVec2(-SQRT_3_2, -0.5f)};
+ TransformMarker(marker, 6, c, s);
+ DrawList.AddLine(marker[0], marker[5], col_outline, weight);
+ DrawList.AddLine(marker[1], marker[4], col_outline, weight);
+ DrawList.AddLine(marker[2], marker[3], col_outline, weight);
+}
+
+inline void MarkerPlus(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
+ ImVec2 marker[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)};
+ TransformMarker(marker, 4, c, s);
+ DrawList.AddLine(marker[0], marker[2], col_outline, weight);
+ DrawList.AddLine(marker[1], marker[3], col_outline, weight);
+}
+
+inline void MarkerCross(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) {
+ ImVec2 marker[4] = {ImVec2(SQRT_1_2,SQRT_1_2),ImVec2(SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,-SQRT_1_2),ImVec2(-SQRT_1_2,SQRT_1_2)};
+ TransformMarker(marker, 4, c, s);
+ DrawList.AddLine(marker[0], marker[2], col_outline, weight);
+ DrawList.AddLine(marker[1], marker[3], col_outline, weight);
+}
+
+template
+inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, bool rend_mk_line, ImU32 col_mk_line, bool rend_mk_fill, ImU32 col_mk_fill) {
+ ImPlotContext& gp = *GImPlot;
+ for (int i = 0; i < getter.Count; ++i) {
+ ImVec2 c = transformer(getter(i));
+ if (gp.BB_Plot.Contains(c)) {
+ // TODO: Optimize the loop and if statements, this is atrocious
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Circle))
+ MarkerCircle(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Square))
+ MarkerSquare(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Diamond))
+ MarkerDiamond(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Up))
+ MarkerUp(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Down))
+ MarkerDown(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Left))
+ MarkerLeft(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Right))
+ MarkerRight(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Cross))
+ MarkerCross(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Plus))
+ MarkerPlus(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ if (ImHasFlag(gp.Style.Marker, ImPlotMarker_Asterisk))
+ MarkerAsterisk(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight);
+ }
+ }
+}
+
+template
+struct LineRenderer {
+ inline LineRenderer(Getter _getter, Transformer _transformer, ImU32 col, float weight) :
+ getter(_getter),
+ transformer(_transformer)
+ {
+ Prims = getter.Count - 1;
+ Col = col;
+ Weight = weight;
+ p1 = transformer(getter(0));
+ }
+ inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) {
+ ImPlotContext& gp = *GImPlot;
+ ImVec2 p2 = transformer(getter(prim + 1));
+ if (!gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) {
+ p1 = p2;
+ return false;
+ }
+ float dx = p2.x - p1.x;
+ float dy = p2.y - p1.y;
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
+ dx *= (Weight * 0.5f);
+ dy *= (Weight * 0.5f);
+ DrawList._VtxWritePtr[0].pos.x = p1.x + dy;
+ DrawList._VtxWritePtr[0].pos.y = p1.y - dx;
+ DrawList._VtxWritePtr[0].uv = uv;
+ DrawList._VtxWritePtr[0].col = Col;
+ DrawList._VtxWritePtr[1].pos.x = p2.x + dy;
+ DrawList._VtxWritePtr[1].pos.y = p2.y - dx;
+ DrawList._VtxWritePtr[1].uv = uv;
+ DrawList._VtxWritePtr[1].col = Col;
+ DrawList._VtxWritePtr[2].pos.x = p2.x - dy;
+ DrawList._VtxWritePtr[2].pos.y = p2.y + dx;
+ DrawList._VtxWritePtr[2].uv = uv;
+ DrawList._VtxWritePtr[2].col = Col;
+ DrawList._VtxWritePtr[3].pos.x = p1.x - dy;
+ DrawList._VtxWritePtr[3].pos.y = p1.y + dx;
+ DrawList._VtxWritePtr[3].uv = uv;
+ DrawList._VtxWritePtr[3].col = Col;
+ DrawList._VtxWritePtr += 4;
+ DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
+ DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1);
+ DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2);
+ DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
+ DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2);
+ DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3);
+ DrawList._IdxWritePtr += 6;
+ DrawList._VtxCurrentIdx += 4;
+ p1 = p2;
+ return true;
+ }
+ Getter getter;
+ Transformer transformer;
+ int Prims;
+ ImU32 Col;
+ float Weight;
+ ImVec2 p1;
+ static const int IdxConsumed = 6;
+ static const int VtxConsumed = 4;
+};
+
+template
+inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col) {
+ ImPlotContext& gp = *GImPlot;
+ if (ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased)) {
+ ImVec2 p1 = transformer(getter(0));
+ for (int i = 0; i < getter.Count; ++i) {
+ ImVec2 p2 = transformer(getter(i));
+ if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2))))
+ DrawList.AddLine(p1, p2, col, line_weight);
+ p1 = p2;
+ }
+ }
+ else {
+ RenderPrimitives(LineRenderer(getter, transformer, col, line_weight), DrawList);
+ }
+}
+
+template
+struct ShadedRenderer {
+ ShadedRenderer(Getter1 _getter1, Getter2 _getter2, Transformer _transformer, ImU32 col) :
+ getter1(_getter1),
+ getter2(_getter2),
+ transformer(_transformer),
+ Col(col)
+ {
+ Prims = ImMin(getter1.Count, getter2.Count) - 1;
+ p11 = transformer(getter1(0));
+ p12 = transformer(getter2(0));
+ }
+
+ inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) {
+ ImVec2 p21 = transformer(getter1(prim+1));
+ ImVec2 p22 = transformer(getter2(prim+1));
+ const int intersect = (p11.y > p12.y && p22.y > p21.y) || (p12.y > p11.y && p21.y > p22.y);
+ ImVec2 intersection = Intersection(p11,p21,p12,p22);
+ DrawList._VtxWritePtr[0].pos = p11;
+ DrawList._VtxWritePtr[0].uv = uv;
+ DrawList._VtxWritePtr[0].col = Col;
+ DrawList._VtxWritePtr[1].pos = p21;
+ DrawList._VtxWritePtr[1].uv = uv;
+ DrawList._VtxWritePtr[1].col = Col;
+ DrawList._VtxWritePtr[2].pos = intersection;
+ DrawList._VtxWritePtr[2].uv = uv;
+ DrawList._VtxWritePtr[2].col = Col;
+ DrawList._VtxWritePtr[3].pos = p12;
+ DrawList._VtxWritePtr[3].uv = uv;
+ DrawList._VtxWritePtr[3].col = Col;
+ DrawList._VtxWritePtr[4].pos = p22;
+ DrawList._VtxWritePtr[4].uv = uv;
+ DrawList._VtxWritePtr[4].col = Col;
+ DrawList._VtxWritePtr += 5;
+ DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx);
+ DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1 + intersect);
+ DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3);
+ DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1);
+ DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - intersect);
+ DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4);
+ DrawList._IdxWritePtr += 6;
+ DrawList._VtxCurrentIdx += 5;
+ p11 = p21;
+ p12 = p22;
+ return true;
+ }
+ Getter1 getter1;
+ Getter2 getter2;
+ Transformer transformer;
+ int Prims;
+ ImU32 Col;
+ ImVec2 p11, p12;
+ static const int IdxConsumed = 6;
+ static const int VtxConsumed = 5;
+};
+
+//-----------------------------------------------------------------------------
+// RENDERING UTILS
+//-----------------------------------------------------------------------------
+
+// Returns true if a style color is set to be automaticaly determined
+inline bool ColorIsAuto(ImPlotCol idx) {
+ ImPlotContext& gp = *GImPlot;
+ return gp.Style.Colors[idx].w == -1;
+}
+
+// Recolors an item from an the current ImPlotCol if it is not automatic (i.e. alpha != -1)
+inline void TryRecolorItem(ImPlotItem* item, ImPlotCol idx) {
+ ImPlotContext& gp = *GImPlot;
+ if (gp.Style.Colors[idx].w != -1)
+ item->Color = gp.Style.Colors[idx];
+}
+
+// Returns true if lines will render (e.g. basic lines, bar outlines)
+inline bool WillLineRender() {
+ ImPlotContext& gp = *GImPlot;
+ return gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0;
+}
+
+// Returns true if fills will render (e.g. shaded plots, bar fills)
+inline bool WillFillRender() {
+ ImPlotContext& gp = *GImPlot;
+ return gp.Style.Colors[ImPlotCol_Fill].w != 0 && gp.Style.FillAlpha > 0;
+}
+
+// Returns true if marker outlines will render
+inline bool WillMarkerOutlineRender() {
+ ImPlotContext& gp = *GImPlot;
+ return gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0;
+}
+
+// Returns true if mark fill will render
+inline bool WillMarkerFillRender() {
+ ImPlotContext& gp = *GImPlot;
+ return gp.Style.Colors[ImPlotCol_MarkerFill].w != 0 && gp.Style.FillAlpha > 0;
+}
+
+// Gets the line color for an item
+inline ImVec4 GetLineColor(ImPlotItem* item) {
+ ImPlotContext& gp = *GImPlot;
+ return ColorIsAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line];
+}
+
+// Gets the fill color for an item
+inline ImVec4 GetItemFillColor(ImPlotItem* item) {
+ ImPlotContext& gp = *GImPlot;
+ ImVec4 col = ColorIsAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill];
+ col.w *= gp.Style.FillAlpha;
+ return col;
+}
+
+// Gets the marker outline color for an item
+inline ImVec4 GetMarkerOutlineColor(ImPlotItem* item) {
+ ImPlotContext& gp = *GImPlot;
+ return ColorIsAuto(ImPlotCol_MarkerOutline) ? GetLineColor(item) : gp.Style.Colors[ImPlotCol_MarkerOutline];
+}
+
+// Gets the marker fill color for an item
+inline ImVec4 GetMarkerFillColor(ImPlotItem* item) {
+ ImPlotContext& gp = *GImPlot;
+ ImVec4 col = ColorIsAuto(ImPlotCol_MarkerFill) ? GetLineColor(item) :gp.Style.Colors[ImPlotCol_MarkerFill];
+ col.w *= gp.Style.FillAlpha;
+ return col;
+}
+
+// Gets the error bar color
+inline ImVec4 GetErrorBarColor() {
+ ImPlotContext& gp = *GImPlot;
+ return ColorIsAuto(ImPlotCol_ErrorBar) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : gp.Style.Colors[ImPlotCol_ErrorBar];
+}
+
+//-----------------------------------------------------------------------------
+// PLOT LINES / MARKERS
+//-----------------------------------------------------------------------------
+
+template
+inline void PlotEx(const char* label_id, Getter getter)
+{
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotEx() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ TryRecolorItem(item, ImPlotCol_Line);
+
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint p = getter(i);
+ FitPoint(p);
+ }
+ }
+
+ ImDrawList& DrawList = *ImGui::GetWindowDrawList();
+ ImPlotState* plot = gp.CurrentPlot;
+ const int y_axis = plot->CurrentYAxis;
+
+ PushPlotClipRect();
+ // render line
+ if (getter.Count > 1 && WillLineRender()) {
+ ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
+ const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight;
+ if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderLineStrip(getter, TransformerLogLog(y_axis), DrawList, line_weight, col_line);
+ else if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
+ RenderLineStrip(getter, TransformerLogLin(y_axis), DrawList, line_weight, col_line);
+ else if (ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderLineStrip(getter, TransformerLinLog(y_axis), DrawList, line_weight, col_line);
+ else
+ RenderLineStrip(getter, TransformerLinLin(y_axis), DrawList, line_weight, col_line);
+ }
+ // render markers
+ if (gp.Style.Marker != ImPlotMarker_None) {
+ const bool rend_mk_line = WillMarkerOutlineRender();
+ const bool rend_mk_fill = WillMarkerFillRender();
+ const ImU32 col_mk_line = ImGui::GetColorU32(GetMarkerOutlineColor(item));
+ const ImU32 col_mk_fill = ImGui::GetColorU32(GetMarkerFillColor(item));
+ if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderMarkers(getter, TransformerLogLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
+ else if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
+ RenderMarkers(getter, TransformerLogLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
+ else if (ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderMarkers(getter, TransformerLinLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
+ else
+ RenderMarkers(getter, TransformerLinLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill);
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotLine(const char* label_id, const float* values, int count, int offset, int stride) {
+ GetterYs getter(values,count,offset,stride);
+ PlotEx(label_id, getter);
+}
+
+void PlotLine(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ return PlotEx(label_id, getter);
+}
+
+void PlotLine(const char* label_id, const ImVec2* data, int count, int offset) {
+ GetterImVec2 getter(data, count, offset);
+ return PlotEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotLine(const char* label_id, const double* values, int count, int offset, int stride) {
+ GetterYs getter(values,count,offset,stride);
+ PlotEx(label_id, getter);
+}
+
+void PlotLine(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ return PlotEx(label_id, getter);
+}
+
+void PlotLine(const char* label_id, const ImPlotPoint* data, int count, int offset) {
+ GetterImPlotPoint getter(data, count, offset);
+ return PlotEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// custom
+
+void PlotLine(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) {
+ GetterFuncPtrImPlotPoint getter(getter_func,data, count, offset);
+ return PlotEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT SCATTER
+//-----------------------------------------------------------------------------
+
+inline int PushScatterStyle() {
+ int vars = 1;
+ PushStyleVar(ImPlotStyleVar_LineWeight, 0);
+ if (GetStyle().Marker == ImPlotMarker_None) {
+ PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Circle);
+ vars++;
+ }
+ return vars;
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotScatter(const char* label_id, const float* values, int count, int offset, int stride) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, values, count, offset, stride);
+ PopStyleVar(vars);
+}
+
+void PlotScatter(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, xs, ys, count, offset, stride);
+ PopStyleVar(vars);
+}
+
+void PlotScatter(const char* label_id, const ImVec2* data, int count, int offset) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, data, count, offset);
+ PopStyleVar(vars);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotScatter(const char* label_id, const double* values, int count, int offset, int stride) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, values, count, offset, stride);
+ PopStyleVar(vars);
+}
+
+void PlotScatter(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, xs, ys, count, offset, stride);
+ PopStyleVar(vars);
+}
+
+void PlotScatter(const char* label_id, const ImPlotPoint* data, int count, int offset) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, data, count, offset);
+ PopStyleVar(vars);
+}
+
+//-----------------------------------------------------------------------------
+// custom
+
+void PlotScatter(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset) {
+ int vars = PushScatterStyle();
+ PlotLine(label_id, getter, data, count, offset);
+ PopStyleVar(vars);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT SHADED
+//-----------------------------------------------------------------------------
+
+template
+inline void PlotShadedEx(const char* label_id, Getter1 getter1, Getter2 getter2) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotShaded() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ TryRecolorItem(item, ImPlotCol_Fill);
+
+ if (!WillFillRender())
+ return;
+
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < ImMin(getter1.Count, getter2.Count); ++i) {
+ ImPlotPoint p1 = getter1(i);
+ ImPlotPoint p2 = getter2(i);
+ FitPoint(p1);
+ FitPoint(p2);
+ }
+ }
+
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+ ImPlotState* plot = gp.CurrentPlot;
+ const int y_axis = plot->CurrentYAxis;
+
+ ImU32 col = ImGui::GetColorU32(GetItemFillColor(item));
+
+ PushPlotClipRect();
+ if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(y_axis), col), DrawList);
+ else if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
+ RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(y_axis), col), DrawList);
+ else if (ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(y_axis), col), DrawList);
+ else
+ RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(y_axis), col), DrawList);
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotShaded(const char* label_id, const float* xs, const float* ys1, const float* ys2, int count, int offset, int stride) {
+ GetterXsYs getter1(xs, ys1, count, offset, stride);
+ GetterXsYs getter2(xs, ys2, count, offset, stride);
+ PlotShadedEx(label_id, getter1, getter2);
+}
+
+void PlotShaded(const char* label_id, const float* xs, const float* ys, int count, float y_ref, int offset, int stride) {
+ GetterXsYs getter1(xs, ys, count, offset, stride);
+ GetterXsYRef getter2(xs, y_ref, count, offset, stride);
+ PlotShadedEx(label_id, getter1, getter2);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset, int stride) {
+ GetterXsYs getter1(xs, ys1, count, offset, stride);
+ GetterXsYs getter2(xs, ys2, count, offset, stride);
+ PlotShadedEx(label_id, getter1, getter2);
+}
+
+void PlotShaded(const char* label_id, const double* xs, const double* ys, int count, double y_ref, int offset, int stride) {
+ GetterXsYs getter1(xs, ys, count, offset, stride);
+ GetterXsYRef getter2(xs, y_ref, count, offset, stride);
+ PlotShadedEx(label_id, getter1, getter2);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT BAR V
+//-----------------------------------------------------------------------------
+
+template
+void PlotBarsEx(const char* label_id, Getter getter, TWidth width) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ TryRecolorItem(item, ImPlotCol_Fill);
+
+ const TWidth half_width = width / 2;
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint p = getter(i);
+ FitPoint(ImPlotPoint(p.x - half_width, p.y));
+ FitPoint(ImPlotPoint(p.x + half_width, 0));
+ }
+ }
+
+ ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
+ ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item));
+ const bool rend_fill = WillFillRender();
+ bool rend_line = WillLineRender();
+ if (rend_fill && col_line == col_fill)
+ rend_line = false;
+
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+ PushPlotClipRect();
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint p = getter(i);
+ if (p.y == 0)
+ continue;
+ ImVec2 a = PlotToPixels(p.x - half_width, p.y);
+ ImVec2 b = PlotToPixels(p.x + half_width, 0);
+ if (rend_fill)
+ DrawList.AddRectFilled(a, b, col_fill);
+ if (rend_line)
+ DrawList.AddRect(a, b, col_line);
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotBars(const char* label_id, const float* values, int count, float width, float shift, int offset, int stride) {
+ GetterBarV getter(values,shift,count,offset,stride);
+ PlotBarsEx(label_id, getter, width);
+}
+
+void PlotBars(const char* label_id, const float* xs, const float* ys, int count, float width, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ PlotBarsEx(label_id, getter, width);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotBars(const char* label_id, const double* values, int count, double width, double shift, int offset, int stride) {
+ GetterBarV getter(values,shift,count,offset,stride);
+ PlotBarsEx(label_id, getter, width);
+}
+
+void PlotBars(const char* label_id, const double* xs, const double* ys, int count, double width, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ PlotBarsEx(label_id, getter, width);
+}
+
+//-----------------------------------------------------------------------------
+// custom
+
+void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double width, int offset) {
+ GetterFuncPtrImPlotPoint getter(getter_func, data, count, offset);
+ PlotBarsEx(label_id, getter, width);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT BAR H
+//-----------------------------------------------------------------------------
+
+// TODO: Migrate to RenderPrimitives
+
+template
+void PlotBarsHEx(const char* label_id, Getter getter, THeight height) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ TryRecolorItem(item, ImPlotCol_Fill);
+
+ const THeight half_height = height / 2;
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint p = getter(i);
+ FitPoint(ImPlotPoint(0, p.y - half_height));
+ FitPoint(ImPlotPoint(p.x, p.y + half_height));
+ }
+ }
+
+ ImU32 col_line = ImGui::GetColorU32(GetLineColor(item));
+ ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item));
+ const bool rend_fill = WillFillRender();
+ bool rend_line = WillLineRender();
+ if (rend_fill && col_line == col_fill)
+ rend_line = false;
+
+ PushPlotClipRect();
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint p = getter(i);
+ if (p.x == 0)
+ continue;
+ ImVec2 a = PlotToPixels(0, p.y - half_height);
+ ImVec2 b = PlotToPixels(p.x, p.y + half_height);
+ if (rend_fill)
+ DrawList.AddRectFilled(a, b, col_fill);
+ if (rend_line)
+ DrawList.AddRect(a, b, col_line);
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotBarsH(const char* label_id, const float* values, int count, float height, float shift, int offset, int stride) {
+ GetterBarH getter(values,shift,count,offset,stride);
+ PlotBarsHEx(label_id, getter, height);
+}
+
+void PlotBarsH(const char* label_id, const float* xs, const float* ys, int count, float height, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ PlotBarsHEx(label_id, getter, height);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotBarsH(const char* label_id, const double* values, int count, double height, double shift, int offset, int stride) {
+ GetterBarH getter(values,shift,count,offset,stride);
+ PlotBarsHEx(label_id, getter, height);
+}
+
+void PlotBarsH(const char* label_id, const double* xs, const double* ys, int count, double height, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ PlotBarsHEx(label_id, getter, height);
+}
+
+//-----------------------------------------------------------------------------
+// custom
+
+void PlotBarsH(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double height, int offset) {
+ GetterFuncPtrImPlotPoint getter(getter_func, data, count, offset);
+ PlotBarsHEx(label_id, getter, height);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT ERROR BARS
+//-----------------------------------------------------------------------------
+
+template
+void PlotErrorBarsEx(const char* label_id, Getter getter) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPointError e = getter(i);
+ FitPoint(ImPlotPoint(e.x , e.y - e.neg));
+ FitPoint(ImPlotPoint(e.x , e.y + e.pos ));
+ }
+ }
+
+ const ImU32 col = ImGui::GetColorU32(GetErrorBarColor());
+ const bool rend_whisker = gp.Style.ErrorBarSize > 0;
+ const float half_whisker = gp.Style.ErrorBarSize * 0.5f;
+
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+
+ PushPlotClipRect();
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPointError e = getter(i);
+ ImVec2 p1 = PlotToPixels(e.x, e.y - e.neg);
+ ImVec2 p2 = PlotToPixels(e.x, e.y + e.pos);
+ DrawList.AddLine(p1,p2,col, gp.Style.ErrorBarWeight);
+ if (rend_whisker) {
+ DrawList.AddLine(p1 - ImVec2(half_whisker, 0), p1 + ImVec2(half_whisker, 0), col, gp.Style.ErrorBarWeight);
+ DrawList.AddLine(p2 - ImVec2(half_whisker, 0), p2 + ImVec2(half_whisker, 0), col, gp.Style.ErrorBarWeight);
+ }
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotErrorBars(const char* label_id, const float* xs, const float* ys, const float* err, int count, int offset, int stride) {
+ GetterError getter(xs, ys, err, err, count, offset, stride);
+ PlotErrorBarsEx(label_id, getter);
+}
+
+void PlotErrorBars(const char* label_id, const float* xs, const float* ys, const float* neg, const float* pos, int count, int offset, int stride) {
+ GetterError getter(xs, ys, neg, pos, count, offset, stride);
+ PlotErrorBarsEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotErrorBars(const char* label_id, const double* xs, const double* ys, const double* err, int count, int offset, int stride) {
+ GetterError getter(xs, ys, err, err, count, offset, stride);
+ PlotErrorBarsEx(label_id, getter);
+}
+
+void PlotErrorBars(const char* label_id, const double* xs, const double* ys, const double* neg, const double* pos, int count, int offset, int stride) {
+ GetterError getter(xs, ys, neg, pos, count, offset, stride);
+ PlotErrorBarsEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT ERROR BARS H
+//-----------------------------------------------------------------------------
+
+template
+void PlotErrorBarsHEx(const char* label_id, Getter getter) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBarsH() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+
+ // find data extents
+ if (gp.FitThisFrame) {
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPointError e = getter(i);
+ FitPoint(ImPlotPoint(e.x - e.neg, e.y));
+ FitPoint(ImPlotPoint(e.x + e.pos, e.y));
+ }
+ }
+
+ const ImU32 col = ImGui::GetColorU32(GetErrorBarColor());
+ const bool rend_whisker = gp.Style.ErrorBarSize > 0;
+ const float half_whisker = gp.Style.ErrorBarSize * 0.5f;
+
+ ImDrawList& DrawList = *ImGui::GetWindowDrawList();
+
+ PushPlotClipRect();
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPointError e = getter(i);
+ ImVec2 p1 = PlotToPixels(e.x - e.neg, e.y);
+ ImVec2 p2 = PlotToPixels(e.x + e.pos, e.y);
+ DrawList.AddLine(p1, p2, col, gp.Style.ErrorBarWeight);
+ if (rend_whisker) {
+ DrawList.AddLine(p1 - ImVec2(0, half_whisker), p1 + ImVec2(0, half_whisker), col, gp.Style.ErrorBarWeight);
+ DrawList.AddLine(p2 - ImVec2(0, half_whisker), p2 + ImVec2(0, half_whisker), col, gp.Style.ErrorBarWeight);
+ }
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotErrorBarsH(const char* label_id, const float* xs, const float* ys, const float* err, int count, int offset, int stride) {
+ GetterError getter(xs, ys, err, err, count, offset, stride);
+ PlotErrorBarsHEx(label_id, getter);
+}
+
+void PlotErrorBarsH(const char* label_id, const float* xs, const float* ys, const float* neg, const float* pos, int count, int offset, int stride) {
+ GetterError getter(xs, ys, neg, pos, count, offset, stride);
+ PlotErrorBarsHEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, const double* err, int count, int offset, int stride) {
+ GetterError getter(xs, ys, err, err, count, offset, stride);
+ PlotErrorBarsHEx(label_id, getter);
+}
+
+void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, const double* neg, const double* pos, int count, int offset, int stride) {
+ GetterError getter(xs, ys, neg, pos, count, offset, stride);
+ PlotErrorBarsHEx(label_id, getter);
+}
+
+
+//-----------------------------------------------------------------------------
+// PLOT PIE CHART
+//-----------------------------------------------------------------------------
+
+inline void RenderPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) {
+ static const float resolution = 50 / (2 * IM_PI);
+ static ImVec2 buffer[50];
+ buffer[0] = PlotToPixels(center);
+ int n = ImMax(3, (int)((a1 - a0) * resolution));
+ double da = (a1 - a0) / (n - 1);
+ for (int i = 0; i < n; ++i) {
+ double a = a0 + i * da;
+ buffer[i + 1] = PlotToPixels(center.x + radius * cos(a), center.y + radius * sin(a));
+ }
+ DrawList.AddConvexPolyFilled(buffer, n + 1, col);
+}
+
+template
+void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y, T radius, bool normalize, const char* fmt, T angle0) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!");
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+
+ T sum = 0;
+ for (int i = 0; i < count; ++i)
+ sum += values[i];
+
+ normalize = normalize || sum > 1.0f;
+
+ ImPlotPoint center(x,y);
+
+ PushPlotClipRect();
+ T a0 = angle0 * 2 * IM_PI / 360.0f;
+ T a1 = angle0 * 2 * IM_PI / 360.0f;
+ for (int i = 0; i < count; ++i) {
+ ImPlotItem* item = RegisterItem(label_ids[i]);
+ ImU32 col = ImGui::GetColorU32(GetItemFillColor(item));
+ T percent = normalize ? values[i] / sum : values[i];
+ a1 = a0 + 2 * IM_PI * percent;
+ if (item->Show) {
+ if (percent < 0.5) {
+ RenderPieSlice(DrawList, center, radius, a0, a1, col);
+ }
+ else {
+ RenderPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col);
+ RenderPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col);
+ }
+ }
+ a0 = a1;
+ }
+ if (fmt != NULL) {
+ a0 = angle0 * 2 * IM_PI / 360.0f;
+ a1 = angle0 * 2 * IM_PI / 360.0f;
+ char buffer[32];
+ for (int i = 0; i < count; ++i) {
+ ImPlotItem* item = GetItem(label_ids[i]);
+ T percent = normalize ? values[i] / sum : values[i];
+ a1 = a0 + 2 * IM_PI * percent;
+ if (item->Show) {
+ sprintf(buffer, fmt, values[i]);
+ ImVec2 size = ImGui::CalcTextSize(buffer);
+ T angle = a0 + (a1 - a0) * 0.5f;
+ ImVec2 pos = PlotToPixels(center.x + 0.5f * radius * cos(angle), center.y + 0.5f * radius * sin(angle));
+ ImU32 col = CalcTextColor(GetItemFillColor(item));
+ DrawList.AddText(pos - size * 0.5f, col, buffer);
+ }
+ a0 = a1;
+ }
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotPieChart(const char** label_ids, const float* values, int count, float x, float y, float radius, bool normalize, const char* fmt, float angle0) {
+ return PlotPieChartEx(label_ids, values, count, x, y, radius, normalize, fmt, angle0);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotPieChart(const char** label_ids, const double* values, int count, double x, double y, double radius, bool normalize, const char* fmt, double angle0) {
+ return PlotPieChartEx(label_ids, values, count, x, y, radius, normalize, fmt, angle0);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT HEATMAP
+//-----------------------------------------------------------------------------
+
+template
+void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, T scale_min, T scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
+ ImPlotContext& gp = *GImPlot;
+ const double w = (bounds_max.x - bounds_min.x) / cols;
+ const double h = (bounds_max.y - bounds_min.y) / rows;
+ const ImPlotPoint half_size(w*0.5,h*0.5);
+ int i = 0;
+ for (int r = 0; r < rows; ++r) {
+ for (int c = 0; c < cols; ++c) {
+ ImPlotPoint p;
+ p.x = bounds_min.x + 0.5*w + c*w;
+ p.y = bounds_max.y - (0.5*h + r*h);
+ ImVec2 a = transformer(p.x - half_size.x, p.y - half_size.y);
+ ImVec2 b = transformer(p.x + half_size.x, p.y + half_size.y);
+ float t = (float)ImRemap(values[i], scale_min, scale_max, T(0), T(1));
+ ImVec4 color = LerpColormap(t);
+ color.w *= gp.Style.FillAlpha;
+ ImU32 col = ImGui::GetColorU32(color);
+ DrawList.AddRectFilled(a, b, col);
+ i++;
+ }
+ }
+ if (fmt != NULL) {
+ i = 0;
+ for (int r = 0; r < rows; ++r) {
+ for (int c = 0; c < cols; ++c) {
+ ImPlotPoint p;
+ p.x = bounds_min.x + 0.5*w + c*w;
+ p.y = bounds_min.y + 1 - (0.5*h + r*h);
+ ImVec2 px = transformer(p);
+ char buff[32];
+ sprintf(buff, fmt, values[i]);
+ ImVec2 size = ImGui::CalcTextSize(buff);
+ float t = (float)ImRemap(values[i], scale_min, scale_max, T(0), T(1));
+ ImVec4 color = LerpColormap(t);
+ ImU32 col = CalcTextColor(color);
+ DrawList.AddText(px - size * 0.5f, col, buff);
+ i++;
+ }
+ }
+ }
+}
+
+template
+void PlotHeatmapEx(const char* label_id, const T* values, int rows, int cols, T scale_min, T scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() needs to be called between BeginPlot() and EndPlot()!");
+ IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!");
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ if (gp.FitThisFrame) {
+ FitPoint(bounds_min);
+ FitPoint(bounds_max);
+ }
+ ImDrawList& DrawList = *ImGui::GetWindowDrawList();
+ ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true);
+ ImPlotState* plot = gp.CurrentPlot;
+ int y_axis = plot->CurrentYAxis;
+ if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderHeatmap(TransformerLogLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+ else if (ImHasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale))
+ RenderHeatmap(TransformerLogLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+ else if (ImHasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale))
+ RenderHeatmap(TransformerLinLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+ else
+ RenderHeatmap(TransformerLinLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+ ImGui::PopClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotHeatmap(const char* label_id, const float* values, int rows, int cols, float scale_min, float scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
+ return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) {
+ return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT DIGITAL
+//-----------------------------------------------------------------------------
+
+template
+inline void PlotDigitalEx(const char* label_id, Getter getter)
+{
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() needs to be called between BeginPlot() and EndPlot()!");
+
+ ImPlotItem* item = RegisterItem(label_id);
+ if (!item->Show)
+ return;
+ TryRecolorItem(item, ImPlotCol_Line);
+
+ // render digital signals as "pixel bases" rectangles
+ PushPlotClipRect();
+ if (getter.Count > 1 && WillLineRender()) {
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+ const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight;
+ const int y_axis = gp.CurrentPlot->CurrentYAxis;
+ int pixYMax = 0;
+ ImPlotPoint itemData1 = getter(0);
+ for (int i = 0; i < getter.Count; ++i) {
+ ImPlotPoint itemData2 = getter(i);
+ if (NanOrInf(itemData1.y)) {
+ itemData1 = itemData2;
+ continue;
+ }
+ if (NanOrInf(itemData2.y)) itemData2.y = ConstrainNan(ConstrainInf(itemData2.y));
+ int pixY_0 = (int)(line_weight);
+ itemData1.y = ImMax(0.0, itemData1.y);
+ float pixY_1_float = gp.Style.DigitalBitHeight * (float)itemData1.y;
+ int pixY_1 = (int)(pixY_1_float); //allow only positive values
+ int pixY_chPosOffset = (int)(ImMax(gp.Style.DigitalBitHeight, pixY_1_float) + gp.Style.DigitalBitGap);
+ pixYMax = ImMax(pixYMax, pixY_chPosOffset);
+ ImVec2 pMin = PlotToPixels(itemData1);
+ ImVec2 pMax = PlotToPixels(itemData2);
+ int pixY_Offset = 20; //20 pixel from bottom due to mouse cursor label
+ pMin.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset);
+ pMax.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset);
+ //plot only one rectangle for same digital state
+ while (((i+2) < getter.Count) && (itemData1.y == itemData2.y)) {
+ const int in = (i + 1);
+ itemData2 = getter(in);
+ if (NanOrInf(itemData2.y)) break;
+ pMax.x = PlotToPixels(itemData2).x;
+ i++;
+ }
+ //do not extend plot outside plot range
+ if (pMin.x < gp.PixelRange[y_axis].Min.x) pMin.x = gp.PixelRange[y_axis].Min.x;
+ if (pMax.x < gp.PixelRange[y_axis].Min.x) pMax.x = gp.PixelRange[y_axis].Min.x;
+ if (pMin.x > gp.PixelRange[y_axis].Max.x) pMin.x = gp.PixelRange[y_axis].Max.x;
+ if (pMax.x > gp.PixelRange[y_axis].Max.x) pMax.x = gp.PixelRange[y_axis].Max.x;
+ //plot a rectangle that extends up to x2 with y1 height
+ if ((pMax.x > pMin.x) && (gp.BB_Plot.Contains(pMin) || gp.BB_Plot.Contains(pMax))) {
+ ImVec4 colAlpha = item->Color;
+ colAlpha.w = item->Highlight ? 1.0f : 0.9f;
+ DrawList.AddRectFilled(pMin, pMax, ImGui::GetColorU32(colAlpha));
+ }
+ itemData1 = itemData2;
+ }
+ gp.DigitalPlotItemCnt++;
+ gp.DigitalPlotOffset += pixYMax;
+ }
+ PopPlotClipRect();
+}
+
+//-----------------------------------------------------------------------------
+// float
+
+void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ return PlotDigitalEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// double
+
+void PlotDigital(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) {
+ GetterXsYs getter(xs,ys,count,offset,stride);
+ return PlotDigitalEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// custom
+
+void PlotDigital(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) {
+ GetterFuncPtrImPlotPoint getter(getter_func,data,count,offset);
+ return PlotDigitalEx(label_id, getter);
+}
+
+//-----------------------------------------------------------------------------
+// PLOT TEXT
+//-----------------------------------------------------------------------------
+// float
+
+void PlotText(const char* text, float x, float y, bool vertical, const ImVec2& pixel_offset) {
+ return PlotText(text, (double)x, (double)y, vertical, pixel_offset);
+}
+
+//-----------------------------------------------------------------------------
+// double
+void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& pixel_offset) {
+ ImPlotContext& gp = *GImPlot;
+ IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() needs to be called between BeginPlot() and EndPlot()!");
+ ImDrawList & DrawList = *ImGui::GetWindowDrawList();
+ PushPlotClipRect();
+ ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) + pixel_offset;
+ ImU32 colTxt = ImGui::GetColorU32(ImGuiCol_Text);
+ if (vertical)
+ AddTextVertical(&DrawList, text, pos, colTxt);
+ else
+ DrawList.AddText(pos, colTxt, text);
+ PopPlotClipRect();
+}
+
+} // namespace ImPlot
\ No newline at end of file