diff options
Diffstat (limited to '3rdparty/implot')
-rw-r--r-- | 3rdparty/implot/epezent-implot.stamp | 2 | ||||
-rw-r--r-- | 3rdparty/implot/implot.cpp | 4591 | ||||
-rw-r--r-- | 3rdparty/implot/implot.h | 783 | ||||
-rw-r--r-- | 3rdparty/implot/implot_demo.cpp | 3021 | ||||
-rw-r--r-- | 3rdparty/implot/implot_internal.h | 879 | ||||
-rw-r--r-- | 3rdparty/implot/implot_items.cpp | 952 |
6 files changed, 6311 insertions, 3917 deletions
diff --git a/3rdparty/implot/epezent-implot.stamp b/3rdparty/implot/epezent-implot.stamp index 7279f6c..8c13dd4 100644 --- a/3rdparty/implot/epezent-implot.stamp +++ b/3rdparty/implot/epezent-implot.stamp @@ -1,2 +1,2 @@ # This file labels the commit which our source is from -555ff688a8134bc0c602123149abe9c17d577475
\ No newline at end of file +b47c8bacdbc78bc521691f70666f13924bb522ab
\ No newline at end of file diff --git a/3rdparty/implot/implot.cpp b/3rdparty/implot/implot.cpp index f97e08c..3747627 100644 --- a/3rdparty/implot/implot.cpp +++ b/3rdparty/implot/implot.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.10 WIP +// ImPlot v0.13 WIP /* @@ -31,78 +31,116 @@ 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. -- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. - ShowColormapScale was changed to ColormapScale and requires additional arguments. -- 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. -- 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. -- 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved - to implot_internal.h due to its immaturity. -- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding -- 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. -- 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) -- 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset - is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). -- 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. -- 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. -- 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. -- 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. -- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. -- 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. -- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. -- 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. -- 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 -- 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` - and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate - that multiple bars will be plotted. -- 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. -- 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` -- 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: - - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. - It should be fairly obvious what was what. - - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent - style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. -- 2020/05/10 (0.2) - The following function/struct names were changes: - - ImPlotRange -> ImPlotLimits - - GetPlotRange() -> GetPlotLimits() - - SetNextPlotRange -> SetNextPlotLimits - - SetNextPlotRangeX -> SetNextPlotLimitsX - - SetNextPlotRangeY -> SetNextPlotLimitsY -- 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. +- 2021/10/19 (0.13) MAJOR API OVERHAUL! + - TRIVIAL RENAME: + - ImPlotLimits -> ImPlotRect + - ImPlotYAxis_ -> ImAxis_ + - SetPlotYAxis -> SetAxis + - BeginDragDropTarget -> BeginDragDropTargetPlot + - BeginDragDropSource -> BeginDragDropSourcePlot + - ImPlotFlags_NoMousePos -> ImPlotFlags_NoMouseText + - SetNextPlotLimits -> SetNextAxesLimits + - SetMouseTextLocation -> SetupMouseText + - SIGNATURE MODIFIED: + - PixelsToPlot/PlotToPixels -> added optional X-Axis arg + - GetPlotMousePos -> added optional X-Axis arg + - GetPlotLimits -> added optional X-Axis arg + - GetPlotSelection -> added optional X-Axis arg + - DragLineX/Y/DragPoint -> now takes int id; removed labels (render with Annotation/Tag instead) + - REPLACED: + - IsPlotXAxisHovered/IsPlotXYAxisHovered -> IsAxisHovered(ImAxis) + - BeginDragDropTargetX/BeginDragDropTargetY -> BeginDragDropTargetAxis(ImAxis) + - BeginDragDropSourceX/BeginDragDropSourceY -> BeginDragDropSourceAxis(ImAxis) + - ImPlotCol_XAxis, ImPlotCol_YAxis1, etc. -> ImPlotCol_AxisText (push/pop this around SetupAxis to style individual axes) + - ImPlotCol_XAxisGrid, ImPlotCol_Y1AxisGrid -> ImPlotCol_AxisGrid (push/pop this around SetupAxis to style individual axes) + - SetNextPlotLimitsX/Y -> SetNextAxisLimits(ImAxis) + - LinkNextPlotLimits -> SetNextAxisLinks(ImAxis) + - FitNextPlotAxes -> SetNextAxisToFit(ImAxis)/SetNextAxesToFit + - SetLegendLocation -> SetupLegend + - ImPlotFlags_NoHighlight -> ImPlotLegendFlags_NoHighlight + - ImPlotOrientation -> ImPlotLegendFlags_Horizontal + - Annotate -> Annotation + - REMOVED: + - GetPlotQuery, SetPlotQuery, IsPlotQueried -> use DragRect + - SetNextPlotTicksX, SetNextPlotTicksY -> use SetupAxisTicks + - SetNextPlotFormatX, SetNextPlotFormatY -> use SetupAxisFormat + - AnnotateClamped -> use Annotation(bool clamp = true) + - OBSOLETED: + - BeginPlot (original signature) -> use simplified signature + Setup API +- 2021/07/30 (0.12) - The offset argument of `PlotXG` functions was been removed. Implement offsetting in your getter callback instead. +- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. + ShowColormapScale was changed to ColormapScale and requires additional arguments. +- 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. +- 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. +- 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved + to implot_internal.h due to its immaturity. +- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding +- 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. +- 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) +- 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset + is simply 0. This more closely matches ImGui's style and makes it easier to enable non-default but commonly used flags (e.g. ImPlotAxisFlags_Time). +- 2020/08/28 (0.5) - ImPlotMarker_ can no longer be combined with bitwise OR, |. This features caused unecessary slow-down, and almost no one used it. +- 2020/08/25 (0.5) - ImPlotAxisFlags_Scientific was removed. Logarithmic axes automatically uses scientific notation. +- 2020/08/17 (0.5) - PlotText was changed so that text is centered horizontally and vertically about the desired point. +- 2020/08/16 (0.5) - An ImPlotContext must be explicitly created and destroyed now with `CreateContext` and `DestroyContext`. Previously, the context was statically initialized in this source file. +- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. +- 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. +- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. +- 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. +- 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 +- 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` + and `PlotScatter` (however, `PlotLine` can still be used to plot scatter points as `Plot` did before.). `Bar` is not `PlotBars`, to indicate + that multiple bars will be plotted. +- 2020/05/13 (0.2) - `ImMarker` was change to `ImPlotMarker` and `ImAxisFlags` was changed to `ImPlotAxisFlags`. +- 2020/05/11 (0.2) - `ImPlotFlags_Selection` was changed to `ImPlotFlags_BoxSelect` +- 2020/05/11 (0.2) - The namespace ImGui:: was replaced with ImPlot::. As a result, the following additional changes were made: + - Functions that were prefixed or decorated with the word "Plot" have been truncated. E.g., `ImGui::PlotBars` is now just `ImPlot::Bar`. + It should be fairly obvious what was what. + - Some functions have been given names that would have otherwise collided with the ImGui namespace. This has been done to maintain a consistent + style with ImGui. E.g., 'ImGui::PushPlotStyleVar` is now 'ImPlot::PushStyleVar'. +- 2020/05/10 (0.2) - The following function/struct names were changes: + - ImPlotRange -> ImPlotLimits + - GetPlotRange() -> GetPlotLimits() + - SetNextPlotRange -> SetNextPlotLimits + - SetNextPlotRangeX -> SetNextPlotLimitsX + - SetNextPlotRangeY -> SetNextPlotLimitsY +- 2020/05/10 (0.2) - Plot queries are pixel based by default. Query rects that maintain relative plot position have been removed. This was done to support multi-y-axis. */ #include "implot.h" #include "implot_internal.h" -#ifdef _MSC_VER -#define sprintf sprintf_s -#endif +#include <stdlib.h> -// Support for pre-1.82 version. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. +// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#endif + +// Clang/GCC warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked +#endif + // Global plot context +#ifndef GImPlot ImPlotContext* GImPlot = NULL; +#endif //----------------------------------------------------------------------------- // Struct Implementations //----------------------------------------------------------------------------- ImPlotInputMap::ImPlotInputMap() { - 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; + ImPlot::MapInputDefault(this); } ImPlotStyle::ImPlotStyle() { @@ -134,7 +172,7 @@ ImPlotStyle::ImPlotStyle() { AnnotationPadding = ImVec2(2,2); FitPadding = ImVec2(0,0); PlotDefaultSize = ImVec2(400,300); - PlotMinSize = ImVec2(300,225); + PlotMinSize = ImVec2(200,150); ImPlot::StyleColorsAuto(this); @@ -146,18 +184,6 @@ ImPlotStyle::ImPlotStyle() { UseISO8601 = false; } -ImPlotItem* ImPlotPlot::GetLegendItem(int i) { - IM_ASSERT(Items.GetSize() > 0); - return Items.GetByIndex(LegendData.Indices[i]); -} - -const char* ImPlotPlot::GetLegendLabel(int i) { - ImPlotItem* item = GetLegendItem(i); - IM_ASSERT(item != NULL); - IM_ASSERT(item->NameOffset != -1 && item->NameOffset < LegendData.Labels.Buf.Size); - return LegendData.Labels.Buf.Data + item->NameOffset; -} - //----------------------------------------------------------------------------- // Style //----------------------------------------------------------------------------- @@ -165,7 +191,7 @@ const char* ImPlotPlot::GetLegendLabel(int i) { namespace ImPlot { const char* GetStyleColorName(ImPlotCol col) { - static const char* col_names[] = { + static const char* col_names[ImPlotCol_COUNT] = { "Line", "Fill", "MarkerOutline", @@ -179,16 +205,13 @@ const char* GetStyleColorName(ImPlotCol col) { "LegendText", "TitleText", "InlayText", - "XAxis", - "XAxisGrid", - "YAxis", - "YAxisGrid", - "YAxis2", - "YAxisGrid2", - "YAxis3", - "YAxisGrid3", + "AxisText", + "AxisGrid", + "AxisTick", + "AxisBg", + "AxisBgHovered", + "AxisBgActive", "Selection", - "Query", "Crosshairs" }; return col_names[col]; @@ -227,16 +250,13 @@ ImVec4 GetAutoColor(ImPlotCol idx) { case ImPlotCol_LegendText: return GetStyleColorVec4(ImPlotCol_InlayText); case ImPlotCol_TitleText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); case ImPlotCol_InlayText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_XAxis: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_XAxisGrid: return GetStyleColorVec4(ImPlotCol_XAxis) * ImVec4(1,1,1,0.25f); - case ImPlotCol_YAxis: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_YAxisGrid: return GetStyleColorVec4(ImPlotCol_YAxis) * ImVec4(1,1,1,0.25f); - case ImPlotCol_YAxis2: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_YAxisGrid2: return GetStyleColorVec4(ImPlotCol_YAxis2) * ImVec4(1,1,1,0.25f); - case ImPlotCol_YAxis3: return ImGui::GetStyleColorVec4(ImGuiCol_Text); - case ImPlotCol_YAxisGrid3: return GetStyleColorVec4(ImPlotCol_YAxis3) * ImVec4(1,1,1,0.25f); + case ImPlotCol_AxisText: return ImGui::GetStyleColorVec4(ImGuiCol_Text); + case ImPlotCol_AxisGrid: return GetStyleColorVec4(ImPlotCol_AxisText) * ImVec4(1,1,1,0.25f); + case ImPlotCol_AxisTick: return GetStyleColorVec4(ImPlotCol_AxisGrid); + case ImPlotCol_AxisBg: return ImVec4(0,0,0,0); + case ImPlotCol_AxisBgHovered: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + case ImPlotCol_AxisBgActive: return ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive); case ImPlotCol_Selection: return ImVec4(1,1,0,1); - case ImPlotCol_Query: return ImVec4(0,1,0,1); case ImPlotCol_Crosshairs: return GetStyleColorVec4(ImPlotCol_PlotBorder); default: return col; } @@ -335,11 +355,26 @@ void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char *te DrawList->PrimUnreserve(chars_skp*6, chars_skp*4); } +void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end) { + float txt_ht = ImGui::GetTextLineHeight(); + const char* title_end = ImGui::FindRenderedTextEnd(text_begin, text_end); + ImVec2 text_size; + float y = 0; + while (const char* tmp = (const char*)memchr(text_begin, '\n', title_end-text_begin)) { + text_size = ImGui::CalcTextSize(text_begin,tmp,true); + DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,tmp); + text_begin = tmp + 1; + y += txt_ht; + } + text_size = ImGui::CalcTextSize(text_begin,title_end,true); + DrawList->AddText(ImVec2(top_center.x - text_size.x * 0.5f, top_center.y+y),col,text_begin,title_end); +} + double NiceNum(double x, bool round) { - double f; /* fractional part of x */ - double nf; /* nice, rounded fraction */ + double f; + double nf; int expv = (int)floor(ImLog10(x)); - f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */ + f = x / ImPow(10.0, (double)expv); if (round) if (f < 1.5) nf = 1; @@ -396,7 +431,9 @@ void SetCurrentContext(ImPlotContext* ctx) { #define IM_RGB(r,g,b) IM_COL32(r,g,b,255) void Initialize(ImPlotContext* ctx) { - Reset(ctx); + ResetCtxForNextPlot(ctx); + ResetCtxForNextAlignedPlots(ctx); + ResetCtxForNextSubplot(ctx); const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; @@ -431,10 +468,9 @@ void Initialize(ImPlotContext* ctx) { IMPLOT_APPEND_CMAP(PiYG, false); IMPLOT_APPEND_CMAP(Spectral, false); IMPLOT_APPEND_CMAP(Greys, false); - } -void Reset(ImPlotContext* ctx) { +void ResetCtxForNextPlot(ImPlotContext* ctx) { // end child window if it was made if (ctx->ChildWindowMade) ImGui::EndChild(); @@ -442,24 +478,11 @@ void Reset(ImPlotContext* ctx) { // reset the next plot/item data ctx->NextPlotData.Reset(); ctx->NextItemData.Reset(); - // reset items count - ctx->VisibleItemCount = 0; - // reset ticks/labels - ctx->XTicks.Reset(); - for (int i = 0; i < 3; ++i) - ctx->YTicks[i].Reset(); // reset labels ctx->Annotations.Reset(); + ctx->Tags.Reset(); // reset extents/fit - ctx->FitThisFrame = false; - ctx->FitX = false; - ctx->ExtentsX.Min = HUGE_VAL; - ctx->ExtentsX.Max = -HUGE_VAL; - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - ctx->ExtentsY[i].Min = HUGE_VAL; - ctx->ExtentsY[i].Max = -HUGE_VAL; - ctx->FitY[i] = false; - } + ctx->OpenContextThisFrame = false; // reset digital plot items count ctx->DigitalPlotItemCnt = 0; ctx->DigitalPlotOffset = 0; @@ -469,6 +492,17 @@ void Reset(ImPlotContext* ctx) { ctx->PreviousItem = NULL; } +void ResetCtxForNextAlignedPlots(ImPlotContext* ctx) { + ctx->CurrentAlignmentH = NULL; + ctx->CurrentAlignmentV = NULL; +} + +void ResetCtxForNextSubplot(ImPlotContext* ctx) { + ctx->CurrentSubplot = NULL; + ctx->CurrentAlignmentH = NULL; + ctx->CurrentAlignmentV = NULL; +} + //----------------------------------------------------------------------------- // Plot Utils //----------------------------------------------------------------------------- @@ -485,82 +519,7 @@ ImPlotPlot* GetCurrentPlot() { void BustPlotCache() { GImPlot->Plots.Clear(); -} - -void PushLinkedAxis(ImPlotAxis& axis) { - if (axis.LinkedMin) { *axis.LinkedMin = axis.Range.Min; } - if (axis.LinkedMax) { *axis.LinkedMax = axis.Range.Max; } -} - -void PullLinkedAxis(ImPlotAxis& axis) { - if (axis.LinkedMin) { axis.SetMin(*axis.LinkedMin,true); } - if (axis.LinkedMax) { axis.SetMax(*axis.LinkedMax,true); } -} - -//----------------------------------------------------------------------------- -// Coordinate Utils -//----------------------------------------------------------------------------- - -void UpdateTransformCache() { - ImPlotContext& gp = *GImPlot; - // get pixels for transforms - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.PixelRange[i] = ImRect(ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.x : gp.CurrentPlot->PlotRect.Min.x, - ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.y : gp.CurrentPlot->PlotRect.Max.y, - ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.x : gp.CurrentPlot->PlotRect.Max.x, - ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.y : gp.CurrentPlot->PlotRect.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 < IMPLOT_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, ImPlotYAxis y_axis_in) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); - const ImPlotYAxis 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, ImPlotYAxis 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, ImPlotYAxis y_axis_in) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); - const ImPlotYAxis 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, ImPlotYAxis y_axis) { - return PlotToPixels(plt.x, plt.y, y_axis); + GImPlot->Subplots.Clear(); } //----------------------------------------------------------------------------- @@ -587,29 +546,37 @@ ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlot return pos; } -ImVec2 CalcLegendSize(ImPlotPlot& plot, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orn) { +ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical) { // vars - const int nItems = plot.GetLegendCount(); + const int nItems = items.GetLegendCount(); const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; // get label max width float max_label_width = 0; float sum_label_width = 0; for (int i = 0; i < nItems; ++i) { - const char* label = plot.GetLegendLabel(i); + const char* label = items.GetLegendLabel(i); const float label_width = ImGui::CalcTextSize(label, NULL, true).x; max_label_width = label_width > max_label_width ? label_width : max_label_width; sum_label_width += label_width; } // calc legend size - const ImVec2 legend_size = orn == ImPlotOrientation_Vertical ? + const ImVec2 legend_size = vertical ? ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) : ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht); return legend_size; } -void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orn, ImDrawList& DrawList) { - ImGuiIO& IO = ImGui::GetIO(); +int LegendSortingComp(void* _items, const void* _a, const void* _b) { + ImPlotItemGroup* items = (ImPlotItemGroup*)_items; + const int a = *(const int*)_a; + const int b = *(const int*)_b; + const char* label_a = items->GetLegendLabel(a); + const char* label_b = items->GetLegendLabel(b); + return strcmp(label_a,label_b); +} + +bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hovered, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList) { // vars const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; @@ -618,13 +585,29 @@ void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interacta ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); // render each legend item float sum_label_width = 0; - for (int i = 0; i < plot.GetLegendCount(); ++i) { - ImPlotItem* item = plot.GetLegendItem(i); - const char* label = plot.GetLegendLabel(i); + bool any_item_hovered = false; + + const int num_items = items.GetLegendCount(); + if (num_items < 1) + return hovered; + // ImVector<int>& indices = GImPlot->TempInt1; + // indices.resize(num_items); + // // bool sort = true; + // // if (sort && num_items > 1) { + // // qsort_s(indices.Data, num_items, sizeof(int), LegendSortingComp, &items); + // // } + // // else { + // // for (int i = 0; i < num_items; ++i) + // // indices[i] = i; + // // } + for (int i = 0; i < num_items; ++i) { + const int idx = i; //indices[i]; + ImPlotItem* item = items.GetLegendItem(idx); + const char* label = items.GetLegendLabel(idx); const float label_width = ImGui::CalcTextSize(label, NULL, true).x; - const ImVec2 top_left = orn == ImPlotOrientation_Vertical ? - legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : - legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); + const ImVec2 top_left = vertical ? + legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) : + legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0); sum_label_width += label_width; ImRect icon_bb; icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink); @@ -632,53 +615,70 @@ void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interacta ImRect label_bb; label_bb.Min = top_left; label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); - ImU32 col_hl_txt; + ImU32 col_txt_hl; ImU32 col_item = ImAlphaU32(item->Color,1); - if (interactable && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) { + + ImRect button_bb(icon_bb.Min, label_bb.Max); + + + bool item_hov = false; + bool item_hld = false; + bool item_clk = ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoButtons) + ? false + : ImGui::ButtonBehavior(button_bb, item->ID, &item_hov, &item_hld); + + if (item_clk) + item->Show = !item->Show; + + + const bool can_hover = (item_hov) + && (!ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightItem) + || !ImHasFlag(items.Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)); + + if (can_hover) { + item->LegendHoverRect.Min = icon_bb.Min; + item->LegendHoverRect.Max = label_bb.Max; item->LegendHovered = true; - col_hl_txt = ImMixU32(col_txt, col_item, 64); - } - else { - // item->LegendHovered = false; - col_hl_txt = ImGui::GetColorU32(col_txt); - } - ImU32 iconColor; - if (interactable && icon_bb.Contains(IO.MousePos)) { - ImU32 col_alpha = ImAlphaU32(col_item,0.5f); - iconColor = item->Show ? col_alpha : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); - if (IO.MouseClicked[0]) - item->Show = !item->Show; + col_txt_hl = ImMixU32(col_txt, col_item, 64); + any_item_hovered = true; } else { - iconColor = item->Show ? col_item : col_txt_dis; + col_txt_hl = ImGui::GetColorU32(col_txt); } - DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, iconColor, 1); + ImU32 col_icon; + if (item_hld) + col_icon = item->Show ? ImAlphaU32(col_item,0.5f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); + else if (item_hov) + col_icon = item->Show ? ImAlphaU32(col_item,0.75f) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.75f); + else + col_icon = item->Show ? col_item : col_txt_dis; + + DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, col_icon); const char* text_display_end = ImGui::FindRenderedTextEnd(label, NULL); if (label != text_display_end) - DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_hl_txt : col_txt_dis, label, text_display_end); + DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_txt_hl : col_txt_dis, label, text_display_end); } + return hovered && !any_item_hovered; } //----------------------------------------------------------------------------- // Tick Utils //----------------------------------------------------------------------------- -void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt) { +static const float TICK_FILL_X = 0.8f; +static const float TICK_FILL_Y = 1.0f; + +void AddTicksDefault(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { const int idx0 = ticks.Size; const int nMinor = 10; - const int nMajor = ImMax(2, (int)IM_ROUND(pix / (orn == ImPlotOrientation_Horizontal ? 400.0f : 300.0f))); + const int nMajor = ImMax(2, (int)IM_ROUND(pix / (vertical ? 300.0f : 400.0f))); const double nice_range = NiceNum(range.Size() * 0.99, false); const double interval = NiceNum(nice_range / (nMajor - 1), true); const double graphmin = floor(range.Min / interval) * interval; const double graphmax = ceil(range.Max / interval) * interval; bool first_major_set = false; int first_major_idx = 0; - - char dummy[32]; - sprintf(dummy,fmt,-ImAbs(interval / nMinor)); - ImVec2 dummy_size = ImGui::CalcTextSize(dummy); ImVec2 total_size(0,0); - for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { // is this zero? combat zero formatting issues if (major-interval < 0 && major+interval > 0) @@ -688,19 +688,17 @@ void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, first_major_idx = ticks.Size; first_major_set = true; } - ticks.Append(major, true, true, fmt); - total_size += dummy_size; + total_size += ticks.Append(major, true, true, formatter, data).LabelSize; } for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (range.Contains(minor)) { - ticks.Append(minor, false, true, fmt); - total_size += dummy_size; + total_size += ticks.Append(minor, false, true, formatter, data).LabelSize; } } } // prune if necessary - if ((orn == ImPlotOrientation_Horizontal && total_size.x > pix) || (orn == ImPlotOrientation_Vertical && total_size.y > pix)) { + if ((!vertical && total_size.x > pix*TICK_FILL_X) || (vertical && total_size.y > pix*TICK_FILL_Y)) { for (int i = first_major_idx-1; i >= idx0; i -= 2) ticks.Ticks[i].ShowLabel = false; for (int i = first_major_idx+1; i < ticks.Size; i += 2) @@ -708,10 +706,10 @@ void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, } } -void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt) { +void AddTicksLogarithmic(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { if (range.Min <= 0 || range.Max <= 0) return; - const int nMajor = orn == ImPlotOrientation_Horizontal ? ImMax(2, (int)IM_ROUND(pix * 0.01f)) : ImMax(2, (int)IM_ROUND(pix * 0.02f)); + const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); double log_min = ImLog10(range.Min); double log_max = ImLog10(range.Max); int exp_step = ImMax(1,(int)(log_max - log_min) / nMajor); @@ -726,7 +724,7 @@ void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation double major2 = ImPow(10, (double)(e + 1)); double interval = (major2 - major1) / 9; if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) - ticks.Append(major1, true, true, fmt); + ticks.Append(major1, true, true, formatter, data); for (int j = 0; j < exp_step; ++j) { major1 = ImPow(10, (double)(e+j)); major2 = ImPow(10, (double)(e+j+1)); @@ -734,14 +732,14 @@ void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { double minor = major1 + i * interval; if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) - ticks.Append(minor, false, false, fmt); + ticks.Append(minor, false, false, formatter, data); } } } } -void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, const char* fmt) { +void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { for (int i = 0; i < n; ++i) { if (labels != NULL) { ImPlotTick tick(values[i], false, true); @@ -751,7 +749,7 @@ void AddTicksCustom(const double* values, const char* const labels[], int n, ImP ticks.Append(tick); } else { - ticks.Append(values[i], false, true, fmt); + ticks.Append(values[i], false, true, formatter, data); } } } @@ -981,6 +979,7 @@ ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_pa return t; } +// TODO: allow users to define these static const char* MONTH_NAMES[] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; static const char* WD_ABRVS[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; static const char* MONTH_ABRVS[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; @@ -995,14 +994,14 @@ int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, b if (use_24_hr_clk) { const int hr = Tm.tm_hour; switch(fmt) { - case ImPlotTimeFmt_Us: return snprintf(buffer, size, ".%03d %03d", ms, us); - case ImPlotTimeFmt_SUs: return snprintf(buffer, size, ":%02d.%03d %03d", sec, ms, us); - case ImPlotTimeFmt_SMs: return snprintf(buffer, size, ":%02d.%03d", sec, ms); - case ImPlotTimeFmt_S: return snprintf(buffer, size, ":%02d", sec); - case ImPlotTimeFmt_HrMinSMs: return snprintf(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); - case ImPlotTimeFmt_HrMinS: return snprintf(buffer, size, "%02d:%02d:%02d", hr, min, sec); - case ImPlotTimeFmt_HrMin: return snprintf(buffer, size, "%02d:%02d", hr, min); - case ImPlotTimeFmt_Hr: return snprintf(buffer, size, "%02d:00", hr); + case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); + case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); + case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); + case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); + case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%02d:%02d:%02d.%03d", hr, min, sec, ms); + case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%02d:%02d:%02d", hr, min, sec); + case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%02d:%02d", hr, min); + case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%02d:00", hr); default: return 0; } } @@ -1010,14 +1009,14 @@ int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt, b const char* ap = Tm.tm_hour < 12 ? "am" : "pm"; const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12; switch(fmt) { - case ImPlotTimeFmt_Us: return snprintf(buffer, size, ".%03d %03d", ms, us); - case ImPlotTimeFmt_SUs: return snprintf(buffer, size, ":%02d.%03d %03d", sec, ms, us); - case ImPlotTimeFmt_SMs: return snprintf(buffer, size, ":%02d.%03d", sec, ms); - case ImPlotTimeFmt_S: return snprintf(buffer, size, ":%02d", sec); - case ImPlotTimeFmt_HrMinSMs: return snprintf(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); - case ImPlotTimeFmt_HrMinS: return snprintf(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); - case ImPlotTimeFmt_HrMin: return snprintf(buffer, size, "%d:%02d%s", hr, min, ap); - case ImPlotTimeFmt_Hr: return snprintf(buffer, size, "%d%s", hr, ap); + case ImPlotTimeFmt_Us: return ImFormatString(buffer, size, ".%03d %03d", ms, us); + case ImPlotTimeFmt_SUs: return ImFormatString(buffer, size, ":%02d.%03d %03d", sec, ms, us); + case ImPlotTimeFmt_SMs: return ImFormatString(buffer, size, ":%02d.%03d", sec, ms); + case ImPlotTimeFmt_S: return ImFormatString(buffer, size, ":%02d", sec); + case ImPlotTimeFmt_HrMinSMs: return ImFormatString(buffer, size, "%d:%02d:%02d.%03d%s", hr, min, sec, ms, ap); + case ImPlotTimeFmt_HrMinS: return ImFormatString(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); + case ImPlotTimeFmt_HrMin: return ImFormatString(buffer, size, "%d:%02d%s", hr, min, ap); + case ImPlotTimeFmt_Hr: return ImFormatString(buffer, size, "%d%s", hr, ap); default: return 0; } } @@ -1032,21 +1031,21 @@ int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, b const int yr = year % 100; if (use_iso_8601) { switch (fmt) { - case ImPlotDateFmt_DayMo: return snprintf(buffer, size, "--%02d-%02d", mon, day); - case ImPlotDateFmt_DayMoYr: return snprintf(buffer, size, "%d-%02d-%02d", year, mon, day); - case ImPlotDateFmt_MoYr: return snprintf(buffer, size, "%d-%02d", year, mon); - case ImPlotDateFmt_Mo: return snprintf(buffer, size, "--%02d", mon); - case ImPlotDateFmt_Yr: return snprintf(buffer, size, "%d", year); + case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "--%02d-%02d", mon, day); + case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d-%02d-%02d", year, mon, day); + case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%d-%02d", year, mon); + case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "--%02d", mon); + case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } else { switch (fmt) { - case ImPlotDateFmt_DayMo: return snprintf(buffer, size, "%d/%d", mon, day); - case ImPlotDateFmt_DayMoYr: return snprintf(buffer, size, "%d/%d/%02d", mon, day, yr); - case ImPlotDateFmt_MoYr: return snprintf(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); - case ImPlotDateFmt_Mo: return snprintf(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); - case ImPlotDateFmt_Yr: return snprintf(buffer, size, "%d", year); + case ImPlotDateFmt_DayMo: return ImFormatString(buffer, size, "%d/%d", mon, day); + case ImPlotDateFmt_DayMoYr: return ImFormatString(buffer, size, "%d/%d/%02d", mon, day, yr); + case ImPlotDateFmt_MoYr: return ImFormatString(buffer, size, "%s %d", MONTH_ABRVS[Tm.tm_mon], year); + case ImPlotDateFmt_Mo: return ImFormatString(buffer, size, "%s", MONTH_ABRVS[Tm.tm_mon]); + case ImPlotDateFmt_Yr: return ImFormatString(buffer, size, "%d", year); default: return 0; } } @@ -1237,38 +1236,469 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti } //----------------------------------------------------------------------------- +// Context Menu +//----------------------------------------------------------------------------- + +template <typename F> +bool DragFloat(const char*, F*, float, F, F) { + return false; +} + +template <> +bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) { + return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); +} + +template <> +bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) { + return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); +} + +inline void BeginDisabledControls(bool cond) { + if (cond) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); + } +} + +inline void EndDisabledControls(bool cond) { + if (cond) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } +} + +void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed) { + + ImGui::PushItemWidth(75); + bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); + bool label = axis.HasLabel(); + bool grid = axis.HasGridLines(); + bool ticks = axis.HasTickMarks(); + bool labels = axis.HasTickLabels(); + double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. + + if (axis.IsTime()) { + ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); + ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); + + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); + EndDisabledControls(always_locked); + ImGui::SameLine(); + BeginDisabledControls(axis.IsLockedMin() || always_locked); + if (ImGui::BeginMenu("Min Time")) { + if (ShowTimePicker("mintime", &tmin)) { + if (tmin >= tmax) + tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); + axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); + } + ImGui::Separator(); + if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { + tmin = CombineDateTime(axis.PickerTimeMin, tmin); + if (tmin >= tmax) + tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); + axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); + } + ImGui::EndMenu(); + } + EndDisabledControls(axis.IsLockedMin() || always_locked); + + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); + EndDisabledControls(always_locked); + ImGui::SameLine(); + BeginDisabledControls(axis.IsLockedMax() || always_locked); + if (ImGui::BeginMenu("Max Time")) { + if (ShowTimePicker("maxtime", &tmax)) { + if (tmax <= tmin) + tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); + axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); + } + ImGui::Separator(); + if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { + tmax = CombineDateTime(axis.PickerTimeMax, tmax); + if (tmax <= tmin) + tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); + axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); + } + ImGui::EndMenu(); + } + EndDisabledControls(axis.IsLockedMax() || always_locked); + } + else { + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); + EndDisabledControls(always_locked); + ImGui::SameLine(); + BeginDisabledControls(axis.IsLockedMin() || always_locked); + double temp_min = axis.Range.Min; + if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { + axis.SetMin(temp_min,true); + if (equal_axis != NULL) + equal_axis->SetAspect(axis.GetAspect()); + } + EndDisabledControls(axis.IsLockedMin() || always_locked); + + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); + EndDisabledControls(always_locked); + ImGui::SameLine(); + BeginDisabledControls(axis.IsLockedMax() || always_locked); + double temp_max = axis.Range.Max; + if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { + axis.SetMax(temp_max,true); + if (equal_axis != NULL) + equal_axis->SetAspect(axis.GetAspect()); + } + EndDisabledControls(axis.IsLockedMax() || always_locked); + } + + ImGui::Separator(); + + ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); + BeginDisabledControls(axis.IsTime() && time_allowed); + ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); + EndDisabledControls(axis.IsTime() && time_allowed); + if (time_allowed) { + BeginDisabledControls(axis.IsLog()); + ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); + EndDisabledControls(axis.IsLog()); + } + ImGui::Separator(); + ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); + ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); + ImGui::Separator(); + BeginDisabledControls(axis.LabelOffset == -1); + if (ImGui::Checkbox("Label", &label)) + ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); + EndDisabledControls(axis.LabelOffset == -1); + if (ImGui::Checkbox("Grid Lines", &grid)) + ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); + if (ImGui::Checkbox("Tick Marks", &ticks)) + ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); + if (ImGui::Checkbox("Tick Labels", &labels)) + ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); + +} + +bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible) { + const float s = ImGui::GetFrameHeight(); + bool ret = false; + if (ImGui::Checkbox("Show",&visible)) + ret = true; + if (legend.CanGoInside) + ImGui::CheckboxFlags("Outside",(unsigned int*)&legend.Flags, ImPlotLegendFlags_Outside); + if (ImGui::RadioButton("H", ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) + legend.Flags |= ImPlotLegendFlags_Horizontal; + ImGui::SameLine(); + if (ImGui::RadioButton("V", !ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal))) + legend.Flags &= ~ImPlotLegendFlags_Horizontal; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2,2)); + if (ImGui::Button("NW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthWest; } ImGui::SameLine(); + if (ImGui::Button("N", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_North; } ImGui::SameLine(); + if (ImGui::Button("NE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_NorthEast; } + if (ImGui::Button("W", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_West; } ImGui::SameLine(); + if (ImGui::InvisibleButton("C", ImVec2(1.5f*s,s))) { } ImGui::SameLine(); + if (ImGui::Button("E", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_East; } + if (ImGui::Button("SW",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthWest; } ImGui::SameLine(); + if (ImGui::Button("S", ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_South; } ImGui::SameLine(); + if (ImGui::Button("SE",ImVec2(1.5f*s,s))) { legend.Location = ImPlotLocation_SouthEast; } + ImGui::PopStyleVar(); + return ret; +} + +void ShowSubplotsContextMenu(ImPlotSubplot& subplot) { + if ((ImGui::BeginMenu("Linking"))) { + if (ImGui::MenuItem("Link Rows",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); + if (ImGui::MenuItem("Link Cols",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); + if (ImGui::MenuItem("Link All X",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); + if (ImGui::MenuItem("Link All Y",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); + ImGui::EndMenu(); + } + if ((ImGui::BeginMenu("Settings"))) { + BeginDisabledControls(!subplot.HasTitle); + if (ImGui::MenuItem("Title",NULL,subplot.HasTitle && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle); + EndDisabledControls(!subplot.HasTitle); + if (ImGui::MenuItem("Resizable",NULL,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoResize); + if (ImGui::MenuItem("Align",NULL,!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign); + if (ImGui::MenuItem("Share Items",NULL,ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems))) + ImFlipFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); + ImGui::EndMenu(); + } +} + +void ShowPlotContextMenu(ImPlotPlot& plot) { + const bool owns_legend = GImPlot->CurrentItems == &plot.Items; + const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); + + char buf[16] = {}; + + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (!x_axis.Enabled || !x_axis.HasMenus()) + continue; + ImGui::PushID(i); + ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "X-Axis" : "X-Axis %d", i + 1); + if (ImGui::BeginMenu(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : buf)) { + ShowAxisContextMenu(x_axis, equal ? x_axis.OrthoAxis : NULL, false); + ImGui::EndMenu(); + } + ImGui::PopID(); + } + + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (!y_axis.Enabled || !y_axis.HasMenus()) + continue; + ImGui::PushID(i); + ImFormatString(buf, sizeof(buf) - 1, i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); + if (ImGui::BeginMenu(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : buf)) { + ShowAxisContextMenu(y_axis, equal ? y_axis.OrthoAxis : NULL, false); + ImGui::EndMenu(); + } + ImGui::PopID(); + } + + ImGui::Separator(); + if (!ImHasFlag(GImPlot->CurrentItems->Legend.Flags, ImPlotLegendFlags_NoMenus)) { + if ((ImGui::BeginMenu("Legend"))) { + if (owns_legend) { + if (ShowLegendContextMenu(plot.Items.Legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); + } + else if (GImPlot->CurrentSubplot != NULL) { + if (ShowLegendContextMenu(GImPlot->CurrentSubplot->Items.Legend, !ImHasFlag(GImPlot->CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend))) + ImFlipFlag(GImPlot->CurrentSubplot->Flags, ImPlotSubplotFlags_NoLegend); + } + ImGui::EndMenu(); + } + } + if ((ImGui::BeginMenu("Settings"))) { + if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) + ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased); + if (ImGui::MenuItem("Equal", NULL, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) + ImFlipFlag(plot.Flags, ImPlotFlags_Equal); + if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); + BeginDisabledControls(plot.TitleOffset == -1); + if (ImGui::MenuItem("Title",NULL,plot.HasTitle())) + ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); + EndDisabledControls(plot.TitleOffset == -1); + if (ImGui::MenuItem("Mouse Position",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoMouseText); + if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) + ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); + ImGui::EndMenu(); + } + if (GImPlot->CurrentSubplot != NULL && !ImHasFlag(GImPlot->CurrentPlot->Flags, ImPlotSubplotFlags_NoMenus)) { + ImGui::Separator(); + if ((ImGui::BeginMenu("Subplots"))) { + ShowSubplotsContextMenu(*GImPlot->CurrentSubplot); + ImGui::EndMenu(); + } + } +} + +//----------------------------------------------------------------------------- // Axis Utils //----------------------------------------------------------------------------- -static inline int AxisPrecision(const ImPlotAxis& axis, const ImPlotTickCollection& ticks) { - const double range = ticks.Size > 1 ? (ticks.Ticks[1].PlotPos - ticks.Ticks[0].PlotPos) : axis.Range.Size(); +static inline void DefaultFormatter(double value, char* buff, int size, void* data) { + char* fmt = (char*)data; + ImFormatString(buff, size, fmt, value); +} + +static inline int AxisPrecision(const ImPlotAxis& axis) { + const double range = axis.Ticks.Size > 1 ? (axis.Ticks.Ticks[1].PlotPos - axis.Ticks.Ticks[0].PlotPos) : axis.Range.Size(); return Precision(range); } -static inline double RoundAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value) { - return RoundTo(value, AxisPrecision(axis,ticks)); +static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { + return RoundTo(value, AxisPrecision(axis)); } -int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value, char* buff, int size) { +void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { ImPlotContext& gp = *GImPlot; - if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) { - ImPlotTimeUnit unit = (axis.Orientation == ImPlotOrientation_Horizontal) - ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)) - : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)); - return FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); + if (axis.IsTime()) { + ImPlotTimeUnit unit = axis.Vertical + ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! + : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! + FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); } else { - double range = ticks.Size > 1 ? (ticks.Ticks[1].PlotPos - ticks.Ticks[0].PlotPos) : axis.Range.Size(); - return snprintf(buff, size, "%.*f", Precision(range), value); + if (round) + value = RoundAxisValue(axis, value); + ImPlotFormatter formatter = axis.Formatter ? axis.Formatter : DefaultFormatter; + void* data = (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? (void*)axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT; + formatter(value, buff, size, data); } } -void UpdateAxisColors(int axis_flag, ImPlotAxis* axis) { - const ImVec4 col_label = GetStyleColorVec4(axis_flag); - const ImVec4 col_grid = GetStyleColorVec4(axis_flag + 1); - axis->ColorMaj = ImGui::GetColorU32(col_grid); - axis->ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); - axis->ColorTxt = ImGui::GetColorU32(col_label); +void UpdateAxisColors(ImPlotAxis& axis) { + const ImVec4 col_grid = GetStyleColorVec4(ImPlotCol_AxisGrid); + axis.ColorMaj = ImGui::GetColorU32(col_grid); + axis.ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); + axis.ColorTick = GetStyleColorU32(ImPlotCol_AxisTick); + axis.ColorTxt = GetStyleColorU32(ImPlotCol_AxisText); + axis.ColorBg = GetStyleColorU32(ImPlotCol_AxisBg); + axis.ColorHov = GetStyleColorU32(ImPlotCol_AxisBgHovered); + axis.ColorAct = GetStyleColorU32(ImPlotCol_AxisBgActive); + // axis.ColorHiLi = IM_COL32_BLACK_TRANS; +} + +void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignmentData* align) { + + ImPlotContext& gp = *GImPlot; + + const float T = ImGui::GetTextLineHeight(); + const float P = gp.Style.LabelPadding.y; + const float K = gp.Style.MinorTickLen.x; + + int count_T = 0; + int count_B = 0; + float last_T = plot.AxesRect.Min.y; + float last_B = plot.AxesRect.Max.y; + + for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { // FYI: can iterate forward + ImPlotAxis& axis = plot.XAxis(i); + if (!axis.Enabled) + continue; + const bool label = axis.HasLabel(); + const bool ticks = axis.HasTickLabels(); + const bool opp = axis.IsOpposite(); + const bool time = axis.IsTime(); + if (opp) { + if (count_T++ > 0) + pad_T += K + P; + if (label) + pad_T += T + P; + if (ticks) + pad_T += ImMax(T, axis.Ticks.MaxSize.y) + P + (time ? T + P : 0); + axis.Datum1 = plot.CanvasRect.Min.y + pad_T; + axis.Datum2 = last_T; + last_T = axis.Datum1; + } + else { + if (count_B++ > 0) + pad_B += K + P; + if (label) + pad_B += T + P; + if (ticks) + pad_B += ImMax(T, axis.Ticks.MaxSize.y) + P + (time ? T + P : 0); + axis.Datum1 = plot.CanvasRect.Max.y - pad_B; + axis.Datum2 = last_B; + last_B = axis.Datum1; + } + } + + if (align) { + count_T = count_B = 0; + float delta_T, delta_B; + align->Update(pad_T,pad_B,delta_T,delta_B); + for (int i = IMPLOT_NUM_X_AXES; i-- > 0;) { + ImPlotAxis& axis = plot.XAxis(i); + if (!axis.Enabled) + continue; + if (axis.IsOpposite()) { + axis.Datum1 += delta_T; + axis.Datum2 += count_T++ > 1 ? delta_T : 0; + } + else { + axis.Datum1 -= delta_B; + axis.Datum2 -= count_B++ > 1 ? delta_B : 0; + } + } + } +} + +void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignmentData* align) { + + // [ pad_L ] [ pad_R ] + // .................CanvasRect................ + // :TPWPK.PTPWP _____PlotRect____ PWPTP.KPWPT: + // :A # |- A # |- -| # A -| # A: + // :X | X | | X | x: + // :I # |- I # |- -| # I -| # I: + // :S | S | | S | S: + // :3 # |- 0 # |-_______________-| # 1 -| # 2: + // :.........................................: + // + // T = text height + // P = label padding + // K = minor tick length + // W = label width + + ImPlotContext& gp = *GImPlot; + + const float T = ImGui::GetTextLineHeight(); + const float P = gp.Style.LabelPadding.x; + const float K = gp.Style.MinorTickLen.y; + + int count_L = 0; + int count_R = 0; + float last_L = plot.AxesRect.Min.x; + float last_R = plot.AxesRect.Max.x; + + for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { // FYI: can iterate forward + ImPlotAxis& axis = plot.YAxis(i); + if (!axis.Enabled) + continue; + const bool label = axis.HasLabel(); + const bool ticks = axis.HasTickLabels(); + const bool opp = axis.IsOpposite(); + if (opp) { + if (count_R++ > 0) + pad_R += K + P; + if (label) + pad_R += T + P; + if (ticks) + pad_R += axis.Ticks.MaxSize.x + P; + axis.Datum1 = plot.CanvasRect.Max.x - pad_R; + axis.Datum2 = last_R; + last_R = axis.Datum1; + } + else { + if (count_L++ > 0) + pad_L += K + P; + if (label) + pad_L += T + P; + if (ticks) + pad_L += axis.Ticks.MaxSize.x + P; + axis.Datum1 = plot.CanvasRect.Min.x + pad_L; + axis.Datum2 = last_L; + last_L = axis.Datum1; + } + } + + plot.PlotRect.Min.x = plot.CanvasRect.Min.x + pad_L; + plot.PlotRect.Max.x = plot.CanvasRect.Max.x - pad_R; + + if (align) { + count_L = count_R = 0; + float delta_L, delta_R; + align->Update(pad_L,pad_R,delta_L,delta_R); + for (int i = IMPLOT_NUM_Y_AXES; i-- > 0;) { + ImPlotAxis& axis = plot.YAxis(i); + if (!axis.Enabled) + continue; + if (axis.IsOpposite()) { + axis.Datum1 -= delta_R; + axis.Datum2 -= count_R++ > 1 ? delta_R : 0; + } + else { + axis.Datum1 += delta_L; + axis.Datum2 += count_L++ > 1 ? delta_L : 0; + } + } + } } //----------------------------------------------------------------------------- @@ -1282,6 +1712,8 @@ static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTickCollec col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticks.Size; t++) { const ImPlotTick& xt = ticks.Ticks[t]; + if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) + continue; if (xt.Level == 0) { if (xt.Major) DrawList.AddLine(ImVec2(xt.PixelPos, rect.Min.y), ImVec2(xt.PixelPos, rect.Max.y), col_maj, size_maj); @@ -1298,6 +1730,8 @@ static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTickCollec col_min = ImGui::ColorConvertFloat4ToU32(col_min4); for (int t = 0; t < ticks.Size; t++) { const ImPlotTick& yt = ticks.Ticks[t]; + if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) + continue; if (yt.Major) DrawList.AddLine(ImVec2(rect.Min.x, yt.PixelPos), ImVec2(rect.Max.x, yt.PixelPos), col_maj, size_maj); else if (density < 0.2f) @@ -1313,511 +1747,251 @@ static inline void RenderSelectionRect(ImDrawList& DrawList, const ImVec2& p_min } //----------------------------------------------------------------------------- -// BeginPlot() +// Input Handling //----------------------------------------------------------------------------- -bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, - ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, - const char* y2_label, const char* y3_label) -{ - 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()!"); - IM_ASSERT_USER_ERROR(!(ImHasFlag(x_flags, ImPlotAxisFlags_Time) && ImHasFlag(x_flags, ImPlotAxisFlags_LogScale)), "ImPlotAxisFlags_Time and ImPlotAxisFlags_LogScale cannot be enabled at the same time!"); - IM_ASSERT_USER_ERROR(!ImHasFlag(y1_flags, ImPlotAxisFlags_Time), "Y axes cannot display time formatted labels!"); - - // FRONT MATTER ----------------------------------------------------------- - - ImGuiContext &G = *GImGui; - ImGuiWindow * Window = G.CurrentWindow; - if (Window->SkipItems) { - Reset(GImPlot); - return false; - } - - const ImGuiID ID = Window->GetID(title); - const ImGuiStyle &Style = G.Style; - const ImGuiIO & IO = ImGui::GetIO(); - - bool just_created = gp.Plots.GetByKey(ID) == NULL; - gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); - gp.CurrentPlot->ID = ID; - ImPlotPlot &plot = *gp.CurrentPlot; - - plot.CurrentYAxis = 0; +static const float MOUSE_CURSOR_DRAG_THRESHOLD = 5.0f; +static const float BOX_SELECT_DRAG_THRESHOLD = 4.0f; - if (just_created) { - plot.Flags = flags; - plot.XAxis.Flags = x_flags; - plot.YAxis[0].Flags = y1_flags; - plot.YAxis[1].Flags = y2_flags; - plot.YAxis[2].Flags = y3_flags; - } - else { - // TODO: Check which individual flags changed, and only reset those! - // There's probably an easy bit mask trick I'm not aware of. - if (flags != plot.PreviousFlags) - plot.Flags = flags; - if (x_flags != plot.XAxis.PreviousFlags) - plot.XAxis.Flags = x_flags; - if (y1_flags != plot.YAxis[0].PreviousFlags) - plot.YAxis[0].Flags = y1_flags; - if (y2_flags != plot.YAxis[1].PreviousFlags) - plot.YAxis[1].Flags = y2_flags; - if (y3_flags != plot.YAxis[2].PreviousFlags) - plot.YAxis[2].Flags = y3_flags; - } +bool UpdateInput(ImPlotPlot& plot) { - plot.PreviousFlags = flags; - plot.XAxis.PreviousFlags = x_flags; - plot.YAxis[0].PreviousFlags = y1_flags; - plot.YAxis[1].PreviousFlags = y2_flags; - plot.YAxis[2].PreviousFlags = y3_flags; - - // capture scroll with a child region - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) { - ImGui::BeginChild(title, ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y), false, ImGuiWindowFlags_NoScrollbar); - Window = ImGui::GetCurrentWindow(); - Window->ScrollMax.y = 1.0f; - gp.ChildWindowMade = true; - } - else { - gp.ChildWindowMade = false; - } - - ImDrawList &DrawList = *Window->DrawList; - - // NextPlotData ----------------------------------------------------------- - - // linked axes - plot.XAxis.LinkedMin = gp.NextPlotData.LinkedXmin; - plot.XAxis.LinkedMax = gp.NextPlotData.LinkedXmax; - PullLinkedAxis(plot.XAxis); - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - plot.YAxis[i].LinkedMin = gp.NextPlotData.LinkedYmin[i]; - plot.YAxis[i].LinkedMax = gp.NextPlotData.LinkedYmax[i]; - PullLinkedAxis(plot.YAxis[i]); - } - - if (gp.NextPlotData.HasXRange) { - if (!plot.Initialized || gp.NextPlotData.XRangeCond == ImGuiCond_Always) - plot.XAxis.SetRange(gp.NextPlotData.XRange); - } - - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.NextPlotData.HasYRange[i]) { - if (!plot.Initialized || gp.NextPlotData.YRangeCond[i] == ImGuiCond_Always) - plot.YAxis[i].SetRange(gp.NextPlotData.YRange[i]); - } - } - - // Initialization ------------------------------------------------------------ - - if (!plot.Initialized) { - if (!ImHasFlag(plot.XAxis.Flags,ImPlotAxisFlags_NoInitialFit) && !gp.NextPlotData.HasXRange && !gp.NextPlotData.LinkedXmin && !gp.NextPlotData.LinkedXmax) - gp.FitThisFrame = gp.FitX = true; - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (!ImHasFlag(plot.YAxis[i].Flags,ImPlotAxisFlags_NoInitialFit) && !gp.NextPlotData.HasYRange[i] && !gp.NextPlotData.LinkedYmin[i] && !gp.NextPlotData.LinkedYmax[i]) - gp.FitThisFrame = gp.FitY[i] = true; - } - } - - // AXIS STATES ------------------------------------------------------------ - plot.XAxis.HasRange = gp.NextPlotData.HasXRange; plot.XAxis.RangeCond = gp.NextPlotData.XRangeCond; plot.XAxis.Present = true; - plot.YAxis[0].HasRange = gp.NextPlotData.HasYRange[0]; plot.YAxis[0].RangeCond = gp.NextPlotData.YRangeCond[0]; plot.YAxis[0].Present = true; - plot.YAxis[1].HasRange = gp.NextPlotData.HasYRange[1]; plot.YAxis[1].RangeCond = gp.NextPlotData.YRangeCond[1]; plot.YAxis[1].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis2); - plot.YAxis[2].HasRange = gp.NextPlotData.HasYRange[2]; plot.YAxis[2].RangeCond = gp.NextPlotData.YRangeCond[2]; plot.YAxis[2].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis3); - - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale) && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - gp.Scales[i] = ImPlotScale_LinLin; - else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale) && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - gp.Scales[i] = ImPlotScale_LogLin; - else if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - gp.Scales[i] = ImPlotScale_LinLog; - else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale) && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - gp.Scales[i] = ImPlotScale_LogLog; - } - - // constraints - plot.XAxis.Constrain(); - for (int i = 0; i < IMPLOT_Y_AXES; ++i) - plot.YAxis[i].Constrain(); - - // constrain equal axes for primary x and y if not approximately equal - // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case - if (ImHasFlag(plot.Flags, ImPlotFlags_Equal)) { - double xar = plot.XAxis.GetAspect(); - double yar = plot.YAxis[0].GetAspect(); - if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsInputLocked()) - plot.XAxis.SetAspect(yar); - } + bool changed = false; - // AXIS COLORS ----------------------------------------------------------------- + ImPlotContext& gp = *GImPlot; + ImGuiIO& IO = ImGui::GetIO(); - UpdateAxisColors(ImPlotCol_XAxis, &plot.XAxis); - UpdateAxisColors(ImPlotCol_YAxis, &plot.YAxis[0]); - UpdateAxisColors(ImPlotCol_YAxis2, &plot.YAxis[1]); - UpdateAxisColors(ImPlotCol_YAxis3, &plot.YAxis[2]); + // BUTTON STATE ----------------------------------------------------------- - // BB, PADDING, HOVER ----------------------------------------------------------- + const ImGuiButtonFlags plot_button_flags = ImGuiButtonFlags_AllowItemOverlap + | ImGuiButtonFlags_PressedOnClick + | ImGuiButtonFlags_PressedOnDoubleClick + | ImGuiButtonFlags_MouseButtonLeft + | ImGuiButtonFlags_MouseButtonRight + | ImGuiButtonFlags_MouseButtonMiddle; + const ImGuiButtonFlags axis_button_flags = ImGuiButtonFlags_FlattenChildren + | plot_button_flags; - // frame - ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); - if (frame_size.x < gp.Style.PlotMinSize.x && size.x < 0.0f) - frame_size.x = gp.Style.PlotMinSize.x; - if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) - frame_size.y = gp.Style.PlotMinSize.y; - plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - ImGui::ItemSize(plot.FrameRect); - if (!ImGui::ItemAdd(plot.FrameRect, ID, &plot.FrameRect)) { - Reset(GImPlot); - return false; - } - plot.FrameHovered = ImGui::ItemHoverable(plot.FrameRect, ID); - if (G.HoveredIdPreviousFrame != 0 && G.HoveredIdPreviousFrame != ID) - plot.FrameHovered = false; + const bool plot_clicked = ImGui::ButtonBehavior(plot.PlotRect,plot.ID,&plot.Hovered,&plot.Held,plot_button_flags); ImGui::SetItemAllowOverlap(); - ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); - - // canvas/axes bb - plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); - plot.AxesRect = plot.FrameRect; - // outside legend adjustments - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.GetLegendCount() > 0 && plot.LegendOutside) { - const ImVec2 legend_size = CalcLegendSize(plot, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation); - const bool west = ImHasFlag(plot.LegendLocation, ImPlotLocation_West) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_East); - const bool east = ImHasFlag(plot.LegendLocation, ImPlotLocation_East) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_West); - const bool north = ImHasFlag(plot.LegendLocation, ImPlotLocation_North) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_South); - const bool south = ImHasFlag(plot.LegendLocation, ImPlotLocation_South) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_North); - const bool horz = plot.LegendOrientation == ImPlotOrientation_Horizontal; - if ((west && !horz) || (west && horz && !north && !south)) { - plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); - plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); + if (plot_clicked) { + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && IO.MouseClicked[gp.InputMap.Select] && ImHasFlag(IO.KeyMods, gp.InputMap.SelectMod)) { + plot.Selecting = true; + plot.SelectStart = IO.MousePos; + plot.SelectRect = ImRect(0,0,0,0); } - if ((east && !horz) || (east && horz && !north && !south)) { - plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); - plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); - } - if ((north && horz) || (north && !horz && !west && !east)) { - plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); - plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); - } - if ((south && horz) || (south && !horz && !west && !east)) { - plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); - plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); + if (IO.MouseDoubleClicked[gp.InputMap.Fit]) { + plot.FitThisFrame = true; + for (int i = 0; i < ImAxis_COUNT; ++i) + plot.Axes[i].FitThisFrame = true; } } - gp.RenderX = (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) || - !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks) || - !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickLabels)); - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.RenderY[i] = plot.YAxis[i].Present && - (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) || - !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickMarks) || - !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickLabels)); - } - - // plot bb + const bool can_pan = IO.MouseDown[gp.InputMap.Pan] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod); - // (1) calc top/bot padding and plot height - ImVec2 title_size(0.0f, 0.0f); - const float txt_height = ImGui::GetTextLineHeight(); - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoTitle)){ - title_size = ImGui::CalcTextSize(title, NULL, true); - } + plot.Held = plot.Held && can_pan; - const bool show_x_label = x_label && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoLabel); + bool x_click[IMPLOT_NUM_X_AXES] = {false}; + bool x_held[IMPLOT_NUM_X_AXES] = {false}; + bool x_hov[IMPLOT_NUM_X_AXES] = {false}; - const float pad_top = title_size.x > 0.0f ? txt_height + gp.Style.LabelPadding.y : 0; - const float pad_bot = (plot.XAxis.IsLabeled() ? ImMax(txt_height, gp.XTicks.MaxHeight) + gp.Style.LabelPadding.y + (plot.XAxis.IsTime() ? txt_height + gp.Style.LabelPadding.y : 0) : 0) - + (show_x_label ? txt_height + gp.Style.LabelPadding.y : 0); + bool y_click[IMPLOT_NUM_Y_AXES] = {false}; + bool y_held[IMPLOT_NUM_Y_AXES] = {false}; + bool y_hov[IMPLOT_NUM_Y_AXES] = {false}; - const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; - - // (2) get y tick labels (needed for left/right pad) - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) { - if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - AddTicksLogarithmic(plot.YAxis[i].Range, plot_height, ImPlotOrientation_Vertical, gp.YTicks[i], GetFormatY(i)); - else - AddTicksDefault(plot.YAxis[i].Range, plot_height, ImPlotOrientation_Vertical, gp.YTicks[i], GetFormatY(i)); + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImPlotAxis& xax = plot.XAxis(i); + if (xax.Enabled) { + ImGui::KeepAliveID(xax.ID); + x_click[i] = ImGui::ButtonBehavior(xax.HoverRect,xax.ID,&xax.Hovered,&xax.Held,axis_button_flags); + if (x_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) + plot.FitThisFrame = xax.FitThisFrame = true; + xax.Held = xax.Held && can_pan; + x_hov[i] = xax.Hovered || plot.Hovered; + x_held[i] = xax.Held || plot.Held; } } - // (3) calc left/right pad - const bool show_y1_label = y1_label && !ImHasFlag(plot.YAxis[0].Flags, ImPlotAxisFlags_NoLabel); - const bool show_y2_label = y2_label && !ImHasFlag(plot.YAxis[1].Flags, ImPlotAxisFlags_NoLabel); - const bool show_y3_label = y3_label && !ImHasFlag(plot.YAxis[2].Flags, ImPlotAxisFlags_NoLabel); - - const float pad_left = (show_y1_label ? txt_height + gp.Style.LabelPadding.x : 0) - + (plot.YAxis[0].IsLabeled() ? gp.YTicks[0].MaxWidth + gp.Style.LabelPadding.x : 0); - const float pad_right = ((plot.YAxis[1].Present && plot.YAxis[1].IsLabeled()) ? gp.YTicks[1].MaxWidth + gp.Style.LabelPadding.x : 0) - + ((plot.YAxis[1].Present && show_y2_label) ? txt_height + gp.Style.LabelPadding.x : 0) - + ((plot.YAxis[1].Present && plot.YAxis[2].Present) ? gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y : 0) - + ((plot.YAxis[2].Present && plot.YAxis[2].IsLabeled()) ? gp.YTicks[2].MaxWidth + gp.Style.LabelPadding.x : 0) - + ((plot.YAxis[2].Present && show_y3_label) ? txt_height + gp.Style.LabelPadding.x : 0); - - const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; - - // (4) get x ticks - if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX) { - if (plot.XAxis.IsTime()) - AddTicksTime(plot.XAxis.Range, plot_width, gp.XTicks); - else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) - AddTicksLogarithmic(plot.XAxis.Range, plot_width, ImPlotOrientation_Horizontal, gp.XTicks, GetFormatX()); - else - AddTicksDefault(plot.XAxis.Range, plot_width, ImPlotOrientation_Horizontal, gp.XTicks, GetFormatX()); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { + ImPlotAxis& yax = plot.YAxis(i); + if (yax.Enabled) { + ImGui::KeepAliveID(yax.ID); + y_click[i] = ImGui::ButtonBehavior(yax.HoverRect,yax.ID,&yax.Hovered,&yax.Held,axis_button_flags); + if (y_click[i] && IO.MouseDoubleClicked[gp.InputMap.Fit]) + plot.FitThisFrame = yax.FitThisFrame = true; + yax.Held = yax.Held && can_pan; + y_hov[i] = yax.Hovered || plot.Hovered; + y_held[i] = yax.Held || plot.Held; + } } - // (5) calc plot bb - plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); - plot.PlotHovered = plot.FrameHovered && plot.PlotRect.Contains(IO.MousePos); - - // x axis region bb and hover - plot.XAxis.HoverRect = ImRect(plot.PlotRect.GetBL(), ImVec2(plot.PlotRect.Max.x, plot.AxesRect.Max.y)); - plot.XAxis.ExtHovered = plot.XAxis.HoverRect.Contains(IO.MousePos); - plot.XAxis.AllHovered = plot.XAxis.ExtHovered || plot.PlotHovered; - - // axis label reference - gp.YAxisReference[0] = plot.PlotRect.Min.x; - gp.YAxisReference[1] = plot.PlotRect.Max.x; - gp.YAxisReference[2] = !plot.YAxis[1].Present ? plot.PlotRect.Max.x : gp.YAxisReference[1] - + (plot.YAxis[1].IsLabeled() ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) - + (show_y2_label ? txt_height + gp.Style.LabelPadding.x : 0) - + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y; + // cancel due to DND activity + if (GImGui->DragDropActive || (IO.KeyMods == gp.InputMap.OverrideMod && gp.InputMap.OverrideMod != 0)) + return false; - // y axis regions bb and hover - plot.YAxis[0].HoverRect = ImRect(ImVec2(plot.AxesRect.Min.x, plot.PlotRect.Min.y), ImVec2(plot.PlotRect.Min.x, plot.PlotRect.Max.y)); - plot.YAxis[1].HoverRect = plot.YAxis[2].Present - ? ImRect(plot.PlotRect.GetTR(), ImVec2(gp.YAxisReference[2], plot.PlotRect.Max.y)) - : ImRect(plot.PlotRect.GetTR(), ImVec2(plot.AxesRect.Max.x, plot.PlotRect.Max.y)); + // STATE ------------------------------------------------------------------- - plot.YAxis[2].HoverRect = ImRect(ImVec2(gp.YAxisReference[2], plot.PlotRect.Min.y), ImVec2(plot.AxesRect.Max.x, plot.PlotRect.Max.y)); + const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - plot.YAxis[i].ExtHovered = plot.YAxis[i].Present && plot.YAxis[i].HoverRect.Contains(IO.MousePos); - plot.YAxis[i].AllHovered = plot.YAxis[i].ExtHovered || plot.PlotHovered; - } + const bool any_x_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); + const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); + const bool any_y_hov = plot.Hovered || AnyAxesHovered(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); + const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); + const bool any_hov = any_x_hov || any_y_hov; + const bool any_held = any_x_held || any_y_held; - const bool any_hov_y_axis_region = plot.YAxis[0].AllHovered || plot.YAxis[1].AllHovered || plot.YAxis[2].AllHovered; + const ImVec2 select_drag = ImGui::GetMouseDragDelta(gp.InputMap.Select); + const ImVec2 pan_drag = ImGui::GetMouseDragDelta(gp.InputMap.Pan); + const float select_drag_sq = ImLengthSqr(select_drag); + const float pan_drag_sq = ImLengthSqr(pan_drag); + const bool selecting = plot.Selecting && select_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; + const bool panning = any_held && pan_drag_sq > MOUSE_CURSOR_DRAG_THRESHOLD; - bool hov_query = false; - if (plot.PlotHovered && plot.Queried && !plot.Querying) { - ImRect bb_query = plot.QueryRect; - bb_query.Min += plot.PlotRect.Min; - bb_query.Max += plot.PlotRect.Min; - hov_query = bb_query.Contains(IO.MousePos); - } + // CONTEXT MENU ----------------------------------------------------------- - // AXIS ASPECT RATIOS - plot.XAxis.Pixels = plot.PlotRect.GetWidth(); - for (int i = 0; i < IMPLOT_Y_AXES; ++i) - plot.YAxis[i].Pixels = plot.PlotRect.GetHeight(); + if (IO.MouseReleased[gp.InputMap.Menu] && !plot.ContextLocked) + gp.OpenContextThisFrame = true; - // QUERY DRAG ------------------------------------------------------------- - if (plot.DraggingQuery && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { - plot.DraggingQuery = false; - } - if (plot.DraggingQuery) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - plot.QueryRect.Min += IO.MouseDelta; - plot.QueryRect.Max += IO.MouseDelta; - } - if (plot.PlotHovered && hov_query && !plot.DraggingQuery && !plot.Selecting && !plot.LegendHovered) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; - if (IO.MouseDown[gp.InputMap.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { - plot.DraggingQuery = true; - } - } + if (selecting || panning) + plot.ContextLocked = true; + else if (!(IO.MouseDown[gp.InputMap.Menu] || IO.MouseReleased[gp.InputMap.Menu])) + plot.ContextLocked = false; // DRAG INPUT ------------------------------------------------------------- - const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - - // end drags - if (plot.XAxis.Dragging && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { - plot.XAxis.Dragging = false; - G.IO.MouseDragMaxDistanceSqr[0] = 0; - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].Dragging && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { - plot.YAxis[i].Dragging = false; - G.IO.MouseDragMaxDistanceSqr[0] = 0; - } - } - const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; - bool drag_in_progress = plot.XAxis.Dragging || any_y_dragging; - // do drag - if (drag_in_progress) { - UpdateTransformCache(); - bool equal_dragged = false; - // special case for axis equal and both x and y0 hovered - if (axis_equal && !plot.XAxis.IsInputLocked() && plot.XAxis.Dragging && !plot.YAxis[0].IsInputLocked() && plot.YAxis[0].Dragging) { - ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, 0); - ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, 0); - plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); - plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); - plot.YAxis[0].SetMin(plot.YAxis[0].IsInverted() ? plot_tl.y : plot_br.y); - plot.YAxis[0].SetMax(plot.YAxis[0].IsInverted() ? plot_br.y : plot_tl.y); - double xar = plot.XAxis.GetAspect(); - double yar = plot.YAxis[0].GetAspect(); - if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsInputLocked()) - plot.XAxis.SetAspect(yar); - equal_dragged = true; - } - if (!plot.XAxis.IsInputLocked() && plot.XAxis.Dragging && !equal_dragged) { - ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, 0); - ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, 0); - plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); - plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); - if (axis_equal) - plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!plot.YAxis[i].IsInputLocked() && plot.YAxis[i].Dragging && !(i == 0 && equal_dragged)) { - ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, i); - ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, i); - plot.YAxis[i].SetMin(plot.YAxis[i].IsInverted() ? plot_tl.y : plot_br.y); - plot.YAxis[i].SetMax(plot.YAxis[i].IsInverted() ? plot_br.y : plot_tl.y); - if (i == 0 && axis_equal) - plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); + if (any_held && !plot.Selecting) { + int drag_direction = 0; + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_held[i] && !x_axis.IsInputLocked()) { + drag_direction |= (1 << 1); + const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); + const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); + x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); + x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); + if (axis_equal && x_axis.OrthoAxis != NULL) + x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); + changed = true; } } - // Set the mouse cursor based on which axes are moving. - int direction = 0; - if (!plot.XAxis.IsInputLocked() && plot.XAxis.Dragging) { - direction |= (1 << 1); - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!plot.YAxis[i].Present) { continue; } - if (!plot.YAxis[i].IsInputLocked() && plot.YAxis[i].Dragging) { - direction |= (1 << 2); - break; + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (y_held[i] && !y_axis.IsInputLocked()) { + drag_direction |= (1 << 2); + const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); + const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); + y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); + y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); + if (axis_equal && y_axis.OrthoAxis != NULL) + y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); + changed = true; } } - if (IO.MouseDragMaxDistanceSqr[0] > 5) { - if (direction == 0) - ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - else if (direction == (1 << 1)) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - else if (direction == (1 << 2)) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); - else - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - } - } - // start drag - if (!drag_in_progress && plot.FrameHovered && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !plot.LegendHovered && !hov_query && !plot.DraggingQuery) { - if (plot.XAxis.AllHovered) { - plot.XAxis.Dragging = true; - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].AllHovered) { - plot.YAxis[i].Dragging = true; + if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { + switch (drag_direction) { + case 0 : ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); break; + case (1 << 1) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); break; + case (1 << 2) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); break; + default : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); break; } } } // SCROLL INPUT ----------------------------------------------------------- - if (plot.FrameHovered && (plot.XAxis.AllHovered || any_hov_y_axis_region) && IO.MouseWheel != 0) { - UpdateTransformCache(); - float zoom_rate = IMPLOT_ZOOM_RATE; + if (any_hov && IO.MouseWheel != 0 && ImHasFlag(IO.KeyMods, gp.InputMap.ZoomMod)) { + + float zoom_rate = gp.InputMap.ZoomRate; if (IO.MouseWheel > 0) zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); + ImVec2 rect_size = plot.PlotRect.GetSize(); float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); - bool equal_zoomed = false; - // special case for axis equal and both x and y0 hovered - if (axis_equal && plot.XAxis.AllHovered && !plot.XAxis.IsInputLocked() && plot.YAxis[0].AllHovered && !plot.YAxis[0].IsInputLocked()) { - const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), 0); - const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), 0); - plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); - plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); - plot.YAxis[0].SetMin(plot.YAxis[0].IsInverted() ? plot_tl.y : plot_br.y); - plot.YAxis[0].SetMax(plot.YAxis[0].IsInverted() ? plot_br.y : plot_tl.y); - double xar = plot.XAxis.GetAspect(); - double yar = plot.YAxis[0].GetAspect(); - if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsInputLocked()) - plot.XAxis.SetAspect(yar); - equal_zoomed = true; - } - if (plot.XAxis.AllHovered && !plot.XAxis.IsInputLocked() && !equal_zoomed) { - const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), 0); - const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), 0); - plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); - plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); - if (axis_equal) - plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); + + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + const bool equal_zoom = axis_equal && x_axis.OrthoAxis != NULL; + const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked(); + if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) { + float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; + const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); + const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); + x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); + x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); + if (axis_equal && x_axis.OrthoAxis != NULL) + x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); + changed = true; + } } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].AllHovered && !plot.YAxis[i].IsInputLocked() && !(i == 0 && equal_zoomed)) { - const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), i); - const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), i); - plot.YAxis[i].SetMin(plot.YAxis[i].IsInverted() ? plot_tl.y : plot_br.y); - plot.YAxis[i].SetMax(plot.YAxis[i].IsInverted() ? plot_br.y : plot_tl.y); - if (i == 0 && axis_equal) - plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + const bool equal_zoom = axis_equal && y_axis.OrthoAxis != NULL; + const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked(); + if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) { + float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; + const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); + const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); + y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); + y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); + if (axis_equal && y_axis.OrthoAxis != NULL) + y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); + changed = true; } } } - // BOX-SELECTION AND QUERY ------------------------------------------------ + // BOX-SELECTION ---------------------------------------------------------- - // begin selection - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && plot.PlotHovered && IO.MouseClicked[gp.InputMap.BoxSelectButton] && ImHasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) { - plot.Selecting = true; - plot.SelectStart = IO.MousePos; - plot.SelectRect = ImRect(0,0,0,0); - } - // update selection if (plot.Selecting) { - UpdateTransformCache(); const ImVec2 d = plot.SelectStart - IO.MousePos; - const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImFabs(d.x) > 2; - const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod) && ImFabs(d.y) > 2; + const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImFabs(d.x) > 2; + const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod) && ImFabs(d.y) > 2; // confirm - if (IO.MouseReleased[gp.InputMap.BoxSelectButton] || !IO.MouseDown[gp.InputMap.BoxSelectButton]) { - if (!plot.XAxis.IsInputLocked() && x_can_change) { - ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); - ImPlotPoint p2 = PixelsToPlot(IO.MousePos); - plot.XAxis.SetMin(ImMin(p1.x, p2.x)); - plot.XAxis.SetMax(ImMax(p1.x, p2.x)); + if (IO.MouseReleased[gp.InputMap.Select]) { + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (!x_axis.IsInputLocked() && x_can_change) { + const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); + const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); + x_axis.SetMin(ImMin(p1, p2)); + x_axis.SetMax(ImMax(p1, p2)); + changed = true; + } } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!plot.YAxis[i].IsInputLocked() && y_can_change) { - ImPlotPoint p1 = PixelsToPlot(plot.SelectStart, i); - ImPlotPoint p2 = PixelsToPlot(IO.MousePos, i); - plot.YAxis[i].SetMin(ImMin(p1.y, p2.y)); - plot.YAxis[i].SetMax(ImMax(p1.y, p2.y)); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (!y_axis.IsInputLocked() && y_can_change) { + const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); + const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); + y_axis.SetMin(ImMin(p1, p2)); + y_axis.SetMax(ImMax(p1, p2)); + changed = true; } } - if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod))) - plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; + if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.SelectHorzMod) && ImHasFlag(IO.KeyMods,gp.InputMap.SelectVertMod))) + gp.OpenContextThisFrame = false; plot.Selected = plot.Selecting = false; } // cancel - else if (IO.MouseClicked[gp.InputMap.BoxSelectCancelButton] || IO.MouseDown[gp.InputMap.BoxSelectCancelButton]) { + else if (IO.MouseReleased[gp.InputMap.SelectCancel]) { plot.Selected = plot.Selecting = false; - plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; + gp.OpenContextThisFrame = false; } - else if (ImLengthSqr(d) > 4) { + else if (ImLengthSqr(d) > BOX_SELECT_DRAG_THRESHOLD) { // bad selection if (plot.IsInputLocked()) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); - plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; + gp.OpenContextThisFrame = false; plot.Selected = false; } else { // TODO: Handle only min or max locked cases - plot.SelectRect.Min.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) || plot.XAxis.IsInputLocked() ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); - plot.SelectRect.Max.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) || plot.XAxis.IsInputLocked() ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); - plot.SelectRect.Min.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) || plot.AllYInputLocked() ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); - plot.SelectRect.Max.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) || plot.AllYInputLocked() ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); + const bool full_width = ImHasFlag(IO.KeyMods, gp.InputMap.SelectHorzMod) || AllAxesInputLocked(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); + const bool full_height = ImHasFlag(IO.KeyMods, gp.InputMap.SelectVertMod) || AllAxesInputLocked(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); + plot.SelectRect.Min.x = full_width ? plot.PlotRect.Min.x : ImMin(plot.SelectStart.x, IO.MousePos.x); + plot.SelectRect.Max.x = full_width ? plot.PlotRect.Max.x : ImMax(plot.SelectStart.x, IO.MousePos.x); + plot.SelectRect.Min.y = full_height ? plot.PlotRect.Min.y : ImMin(plot.SelectStart.y, IO.MousePos.y); + plot.SelectRect.Max.y = full_height ? plot.PlotRect.Max.y : ImMax(plot.SelectStart.y, IO.MousePos.y); plot.SelectRect.Min -= plot.PlotRect.Min; plot.SelectRect.Max -= plot.PlotRect.Min; plot.Selected = true; @@ -1827,395 +2001,623 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con plot.Selected = false; } } + return changed; +} - // begin query - if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && plot.PlotHovered && IO.MouseClicked[gp.InputMap.QueryButton] && ImHasFlag(IO.KeyMods, gp.InputMap.QueryMod)) { - plot.Querying = true; - plot.QueryStart = IO.MousePos; - plot.QueryRect = ImRect(0,0,0,0); +//----------------------------------------------------------------------------- +// Next Plot Data (Legacy) +//----------------------------------------------------------------------------- + +void ApplyNextPlotData(ImAxis idx) { + ImPlotContext& gp = *GImPlot; + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + if (!axis.Enabled) + return; + double* npd_lmin = gp.NextPlotData.LinkedMin[idx]; + double* npd_lmax = gp.NextPlotData.LinkedMax[idx]; + bool npd_rngh = gp.NextPlotData.HasRange[idx]; + ImPlotCond npd_rngc = gp.NextPlotData.RangeCond[idx]; + ImPlotRange npd_rngv = gp.NextPlotData.Range[idx]; + axis.LinkedMin = npd_lmin; + axis.LinkedMax = npd_lmax; + axis.PullLinks(); + if (npd_rngh) { + if (!plot.Initialized || npd_rngc == ImPlotCond_Always) + axis.SetRange(npd_rngv); } - // update query - if (plot.Querying) { - UpdateTransformCache(); - // confirm - if (IO.MouseReleased[gp.InputMap.QueryButton] || IO.MouseReleased[gp.InputMap.BoxSelectButton]) { - plot.Querying = false; - if (plot.QueryRect.GetWidth() > 2 && plot.QueryRect.GetHeight() > 2) { - plot.Queried = true; - plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; - } - else - plot.Queried = false; - } - else { - plot.QueryRect.Min.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? plot.PlotRect.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Max.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? plot.PlotRect.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Min.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? plot.PlotRect.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Max.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? plot.PlotRect.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Min -= plot.PlotRect.Min; - plot.QueryRect.Max -= plot.PlotRect.Min; - plot.Queried = plot.QueryRect.GetWidth() > 2 && plot.QueryRect.GetHeight() > 2; - } + axis.HasRange = npd_rngh; + axis.RangeCond = npd_rngc; +} + +//----------------------------------------------------------------------------- +// Setup +//----------------------------------------------------------------------------- + +void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + IM_ASSERT_USER_ERROR(!(ImHasFlag(flags, ImPlotAxisFlags_Time) && ImHasFlag(flags, ImPlotAxisFlags_LogScale)), + "ImPlotAxisFlags_Time and ImPlotAxisFlags_LogScale cannot be enabled at the same time!"); + IM_ASSERT_USER_ERROR(!(ImHasFlag(flags, ImPlotAxisFlags_Time) && idx >= ImAxis_Y1), + "Y axes cannot display time formatted labels!"); + // get plot and axis + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + // set ID + axis.ID = plot.ID + idx + 1; + // check and set flags + if (plot.JustCreated || flags != axis.PreviousFlags) + axis.Flags = flags; + axis.PreviousFlags = flags; + // enable axis + axis.Enabled = true; + // set label + plot.SetAxisLabel(axis,label); + // cache colors + UpdateAxisColors(axis); +} + +void SetupAxisLimits(ImAxis idx, double min_lim, double max_lim, ImPlotCond cond) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); // get plot and axis + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + if (!plot.Initialized || cond == ImPlotCond_Always) + axis.SetRange(min_lim, max_lim); + axis.HasRange = true; + axis.RangeCond = cond; +} + +void SetupAxisFormat(ImAxis idx, const char* fmt) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.HasFormatSpec = fmt != NULL; + if (fmt != NULL) + ImStrncpy(axis.FormatSpec,fmt,sizeof(axis.FormatSpec)); +} + +void SetupAxisLinks(ImAxis idx, double* min_lnk, double* max_lnk) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.LinkedMin = min_lnk; + axis.LinkedMax = max_lnk; + axis.PullLinks(); +} + +void SetupAxisFormat(ImAxis idx, ImPlotFormatter formatter, void* data) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.Formatter = formatter; + axis.FormatterData = data; +} + +void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* const labels[], bool show_default) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.ShowDefaultTicks = show_default; + AddTicksCustom(values, + labels, + n_ticks, + axis.Ticks, + axis.Formatter ? axis.Formatter : DefaultFormatter, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); +} + +void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool show_default) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); + FillRange(GImPlot->TempDouble1, n_ticks, v_min, v_max); + SetupAxisTicks(idx, GImPlot->TempDouble1.Data, n_ticks, labels, show_default); +} + +void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { + SetupAxis(ImAxis_X1, x_label, x_flags); + SetupAxis(ImAxis_Y1, y_label, y_flags); +} + +void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { + SetupAxisLimits(ImAxis_X1, x_min, x_max, cond); + SetupAxisLimits(ImAxis_Y1, y_min, y_max, cond); +} + +void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentItems != NULL, + "SetupLegend() needs to be called within an itemized context!"); + ImPlotLegend& legend = GImPlot->CurrentItems->Legend; + // check and set location + if (location != legend.PreviousLocation) + legend.Location = location; + legend.PreviousLocation = location; + // check and set flags + if (flags != legend.PreviousFlags) + legend.Flags = flags; + legend.PreviousFlags = flags; +} + +void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + GImPlot->CurrentPlot->MouseTextLocation = location; + GImPlot->CurrentPlot->MouseTextFlags = flags; +} + +//----------------------------------------------------------------------------- +// SetNext +//----------------------------------------------------------------------------- + +void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisLimits() 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.HasRange[axis] = true; + gp.NextPlotData.RangeCond[axis] = cond; + gp.NextPlotData.Range[axis].Min = v_min; + gp.NextPlotData.Range[axis].Max = v_max; +} + +void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisLinks() needs to be called before BeginPlot()!"); + gp.NextPlotData.LinkedMin[axis] = link_min; + gp.NextPlotData.LinkedMax[axis] = link_max; +} + +void SetNextAxisToFit(ImAxis axis) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextAxisToFit() needs to be called before BeginPlot()!"); + gp.NextPlotData.Fit[axis] = true; +} + +void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond) { + SetNextAxisLimits(ImAxis_X1, x_min, x_max, cond); + SetNextAxisLimits(ImAxis_Y1, y_min, y_max, cond); +} + +void SetNextAxesToFit() { + for (int i = 0; i < ImAxis_COUNT; ++i) + SetNextAxisToFit(i); +} + +//----------------------------------------------------------------------------- +// BeginPlot +//----------------------------------------------------------------------------- + +bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!"); + + // FRONT MATTER ----------------------------------------------------------- + + if (GImPlot->CurrentSubplot != NULL) + ImGui::PushID(GImPlot->CurrentSubplot->CurrentIdx); + + // get globals + ImPlotContext& gp = *GImPlot; + ImGuiContext &G = *GImGui; + ImGuiWindow* Window = G.CurrentWindow; + + // skip if needed + if (Window->SkipItems && !gp.CurrentSubplot) { + ResetCtxForNextPlot(GImPlot); + return false; } - // switch select to query - if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && ImHasFlag(IO.KeyMods,gp.InputMap.QueryToggleMod)) { - plot.Selecting = plot.Selected = false; - plot.Querying = plot.Queried = true; - plot.QueryStart = plot.SelectStart; - plot.QueryRect = plot.SelectRect; + // ID and age (TODO: keep track of plot age in frames) + const ImGuiID ID = Window->GetID(title_id); + const bool just_created = gp.Plots.GetByKey(ID) == NULL; + gp.CurrentPlot = gp.Plots.GetOrAddByKey(ID); + + ImPlotPlot &plot = *gp.CurrentPlot; + plot.ID = ID; + plot.Items.ID = ID - 1; + plot.JustCreated = just_created; + plot.SetupLocked = false; + + // check flags + if (plot.JustCreated) + plot.Flags = flags; + else if (flags != plot.PreviousFlags) + plot.Flags = flags; + plot.PreviousFlags = flags; + // setup default axes + if (plot.JustCreated) { + SetupAxis(ImAxis_X1); + SetupAxis(ImAxis_Y1); } - // switch query to select - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) && plot.Querying && !ImHasFlag(IO.KeyMods, gp.InputMap.QueryToggleMod) && !IO.MouseDown[gp.InputMap.QueryButton]) { - plot.Selecting = plot.Selected = true; - plot.Querying = plot.Queried = false; - plot.SelectStart = plot.QueryStart; - plot.SelectRect = plot.QueryRect; + + // reset axes + for (int i = 0; i < ImAxis_COUNT; ++i) { + plot.Axes[i].Reset(); + UpdateAxisColors(plot.Axes[i]); } + // ensure first axes enabled + plot.Axes[ImAxis_X1].Enabled = true; + plot.Axes[ImAxis_Y1].Enabled = true; + // set initial axes + plot.CurrentX = ImAxis_X1; + plot.CurrentY = ImAxis_Y1; - // FIT ----------------------------------------------------------- + // process next plot data (legacy) + for (int i = 0; i < ImAxis_COUNT; ++i) + ApplyNextPlotData(i); - // fit from double click - if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && plot.FrameHovered && (plot.XAxis.AllHovered || any_hov_y_axis_region) && !plot.LegendHovered && !hov_query ) { - gp.FitThisFrame = true; - gp.FitX = plot.XAxis.AllHovered; - for (int i = 0; i < IMPLOT_Y_AXES; i++) - gp.FitY[i] = plot.YAxis[i].AllHovered; - } - // fit from FitNextPlotAxes or auto fit - if (gp.NextPlotData.FitX || ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_AutoFit)) { - gp.FitThisFrame = true; - gp.FitX = true; + // capture scroll with a child region + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) { + ImVec2 child_size; + if (gp.CurrentSubplot != NULL) + child_size = gp.CurrentSubplot->CellSize; + else + child_size = ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y); + ImGui::BeginChild(title_id, child_size, false, ImGuiWindowFlags_NoScrollbar); + Window = ImGui::GetCurrentWindow(); + Window->ScrollMax.y = 1.0f; + gp.ChildWindowMade = true; } - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (gp.NextPlotData.FitY[i] || ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_AutoFit)) { - gp.FitThisFrame = true; - gp.FitY[i] = true; - } + else { + gp.ChildWindowMade = false; } - // FOCUS ------------------------------------------------------------------ + // clear text buffers + plot.ClearTextBuffer(); + plot.SetTitle(title_id); - // focus window - if ((IO.MouseClicked[0] || IO.MouseClicked[1] || IO.MouseClicked[2]) && plot.FrameHovered) - ImGui::FocusWindow(ImGui::GetCurrentWindow()); + // set frame size + ImVec2 frame_size; + if (gp.CurrentSubplot != NULL) + frame_size = gp.CurrentSubplot->CellSize; + else + frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); - UpdateTransformCache(); + if (frame_size.x < gp.Style.PlotMinSize.x && (size.x < 0.0f || gp.CurrentSubplot != NULL)) + frame_size.x = gp.Style.PlotMinSize.x; + if (frame_size.y < gp.Style.PlotMinSize.y && (size.y < 0.0f || gp.CurrentSubplot != NULL)) + frame_size.y = gp.Style.PlotMinSize.y; - // set mouse position - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.MousePos[i] = PixelsToPlot(IO.MousePos, i); + plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); + ImGui::ItemSize(plot.FrameRect); + if (!ImGui::ItemAdd(plot.FrameRect, plot.ID, &plot.FrameRect) && !gp.CurrentSubplot) { + ResetCtxForNextPlot(GImPlot); + return false; } - // RENDER ----------------------------------------------------------------- + // setup items (or dont) + if (gp.CurrentItems == NULL) + gp.CurrentItems = &plot.Items; - // grid bg - DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); + return true; +} + +void SetupFinish() { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "SetupFinish needs to be called after BeginPlot!"); - // transform ticks (TODO: Move this into ImPlotTickCollection) - if (gp.RenderX) { - for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks.Ticks[t]; - xt->PixelPos = PlotToPixels(xt->PlotPos, 0, 0).x; + ImPlotContext& gp = *GImPlot; + ImGuiContext& G = *GImGui; + ImDrawList& DrawList = *G.CurrentWindow->DrawList; + const ImGuiStyle& Style = G.Style; + + ImPlotPlot &plot = *gp.CurrentPlot; + + // lock setup + plot.SetupLocked = true; + + // finalize axes + for (int i = 0; i < ImAxis_COUNT; ++i) { + if (plot.Axes[i].Enabled) { + plot.Axes[i].Constrain(); + if (!plot.Initialized && plot.Axes[i].CanInitFit()) + plot.FitThisFrame = plot.Axes[i].FitThisFrame = true; } } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.RenderY[i]) { - for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; - yt->PixelPos = PlotToPixels(0, yt->PlotPos, i).y; - } + + // setup NULL orthogonal axes + const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); + for (int ix = ImAxis_X1, iy = ImAxis_Y1; ix < ImAxis_Y1 || iy < ImAxis_COUNT; ++ix, ++iy) { + ImPlotAxis& x_axis = plot.Axes[ix]; + ImPlotAxis& y_axis = plot.Axes[iy]; + if (x_axis.Enabled && y_axis.Enabled) { + if (x_axis.OrthoAxis == NULL) + x_axis.OrthoAxis = &y_axis; + if (y_axis.OrthoAxis == NULL) + y_axis.OrthoAxis = &x_axis; + } + else if (x_axis.Enabled) + { + if (x_axis.OrthoAxis == NULL && !axis_equal) + x_axis.OrthoAxis = &plot.Axes[ImAxis_Y1]; + } + else if (y_axis.Enabled) { + if (y_axis.OrthoAxis == NULL && !axis_equal) + y_axis.OrthoAxis = &plot.Axes[ImAxis_X1]; } } - // render grid (background) - PushPlotClipRect(gp.Style.PlotBorderSize == 0 ? 1.0f : 0.0f); - if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Foreground)) - RenderGridLinesX(DrawList, gp.XTicks, plot.PlotRect, plot.XAxis.ColorMaj, plot.XAxis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Foreground)) - RenderGridLinesY(DrawList, gp.YTicks[i], plot.PlotRect, plot.YAxis[i].ColorMaj, plot.YAxis[i].ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); - } - PopPlotClipRect(); + // canvas/axes bb + plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); + plot.AxesRect = plot.FrameRect; - // render title - if (title_size.x > 0.0f && !ImHasFlag(plot.Flags, ImPlotFlags_NoTitle)) { - ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); - const char* title_end = ImGui::FindRenderedTextEnd(title); - DrawList.AddText(ImVec2(plot.CanvasRect.GetCenter().x - title_size.x * 0.5f, plot.CanvasRect.Min.y),col,title,title_end); + // outside legend adjustments + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0 && ImHasFlag(plot.Items.Legend.Flags, ImPlotLegendFlags_Outside)) { + ImPlotLegend& legend = plot.Items.Legend; + const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); + const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); + const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); + const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); + const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); + const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); + if ((west && !horz) || (west && horz && !north && !south)) { + plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); + plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); + } + if ((east && !horz) || (east && horz && !north && !south)) { + plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); + plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); + } + if ((north && horz) || (north && !horz && !west && !east)) { + plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); + plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); + } + if ((south && horz) || (south && !horz && !west && !east)) { + plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); + plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); + } } - // render axis labels - if (show_x_label) { - const ImVec2 xLabel_size = ImGui::CalcTextSize(x_label); - const ImVec2 xLabel_pos(plot.PlotRect.GetCenter().x - xLabel_size.x * 0.5f, plot.CanvasRect.Max.y - txt_height); - DrawList.AddText(xLabel_pos, plot.XAxis.ColorTxt, x_label); + // plot bb + float pad_top = 0, pad_bot = 0, pad_left = 0, pad_right = 0; + + // (0) calc top padding form title + ImVec2 title_size(0.0f, 0.0f); + if (plot.HasTitle()) + title_size = ImGui::CalcTextSize(plot.GetTitle(), NULL, true); + if (title_size.x > 0) { + pad_top += title_size.y + gp.Style.LabelPadding.y; + plot.AxesRect.Min.y += gp.Style.PlotPadding.y + pad_top; } - if (show_y1_label) { - const ImVec2 yLabel_size = CalcTextSizeVertical(y1_label); - const ImVec2 yLabel_pos(plot.CanvasRect.Min.x, plot.PlotRect.GetCenter().y + yLabel_size.y * 0.5f); - AddTextVertical(&DrawList, yLabel_pos, plot.YAxis[0].ColorTxt, y1_label); - } + // (1) calc addition top padding and bot padding + PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); - const char* y_labels[] = {y2_label, y3_label}; - for (int i = 1; i < IMPLOT_Y_AXES; i++) { - const char* current_label = y_labels[i-1]; - if (plot.YAxis[i].Present && current_label && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoLabel)) { - const ImVec2 yLabel_size = CalcTextSizeVertical(current_label); - float label_offset = (plot.YAxis[i].IsLabeled() ? gp.YTicks[i].MaxWidth + gp.Style.LabelPadding.x : 0.0f) + gp.Style.LabelPadding.x; - const ImVec2 yLabel_pos(gp.YAxisReference[i] + label_offset, plot.PlotRect.GetCenter().y + yLabel_size.y * 0.5f); - AddTextVertical(&DrawList, yLabel_pos, plot.YAxis[i].ColorTxt, current_label); - } - } - // render tick labels - ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); - if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickLabels)) { - for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks.Ticks[t]; - if (xt->ShowLabel && xt->PixelPos >= plot.PlotRect.Min.x - 1 && xt->PixelPos <= plot.PlotRect.Max.x + 1) - DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, plot.PlotRect.Max.y + gp.Style.LabelPadding.y + xt->Level * (txt_height + gp.Style.LabelPadding.y)), - xt->Major ? plot.XAxis.ColorTxt : plot.XAxis.ColorTxt, gp.XTicks.GetText(t)); - } - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickLabels)) { - for (int t = 0; t < gp.YTicks[i].Size; t++) { - const float x_start = gp.YAxisReference[i] + (i == 0 ? (-gp.Style.LabelPadding.x - gp.YTicks[i].Ticks[t].LabelSize.x) : gp.Style.LabelPadding.x); - ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; - if (yt->ShowLabel && yt->PixelPos >= plot.PlotRect.Min.y - 1 && yt->PixelPos <= plot.PlotRect.Max.y + 1) { - ImVec2 start(x_start, yt->PixelPos - 0.5f * yt->LabelSize.y); - DrawList.AddText(start, yt->Major ? plot.YAxis[i].ColorTxt : plot.YAxis[i].ColorTxt, gp.YTicks[i].GetText(t)); - } - } + + const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; + + // (2) get y tick labels (needed for left/right pad) + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& axis = plot.YAxis(i); + if (axis.WillRender() && axis.ShowDefaultTicks) { + if (axis.IsLog()) + AddTicksLogarithmic(axis.Range, + plot_height, + true, + axis.Ticks, + axis.Formatter ? axis.Formatter : DefaultFormatter, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); + else + AddTicksDefault(axis.Range, + plot_height, + true, + axis.Ticks, + axis.Formatter ? axis.Formatter : DefaultFormatter, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); } } - ImGui::PopClipRect(); - // clear legend - plot.LegendData.Reset(); - // push plot ID into stack - ImGui::PushID(ID); - return true; -} + // (3) calc left/right pad + PadAndDatumAxesY(plot,pad_left,pad_right,gp.CurrentAlignmentV); -//----------------------------------------------------------------------------- -// Context Menu -//----------------------------------------------------------------------------- + const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; -template <typename F> -bool DragFloat(const char*, F*, float, F, F) { - return false; -} + // (4) get x ticks + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& axis = plot.XAxis(i); + if (axis.WillRender() && axis.ShowDefaultTicks) { + if (axis.IsTime()) + AddTicksTime(axis.Range, plot_width, axis.Ticks); + else if (axis.IsLog()) + AddTicksLogarithmic(axis.Range, + plot_width, + false, + axis.Ticks, + axis.Formatter ? axis.Formatter : DefaultFormatter, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); + else + AddTicksDefault(axis.Range, + plot_width, + false, + axis.Ticks, + axis.Formatter ? axis.Formatter : DefaultFormatter, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); + } + } -template <> -bool DragFloat<double>(const char* label, double* v, float v_speed, double v_min, double v_max) { - return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); -} + // (5) calc plot bb + plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); -template <> -bool DragFloat<float>(const char* label, float* v, float v_speed, float v_min, float v_max) { - return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); -} + // HOVER------------------------------------------------------------ -inline void BeginDisabledControls(bool cond) { - if (cond) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); + // axes hover rect, pixel ranges + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImPlotAxis& xax = plot.XAxis(i); + xax.HoverRect = ImRect(ImVec2(plot.PlotRect.Min.x, ImMin(xax.Datum1,xax.Datum2)), + ImVec2(plot.PlotRect.Max.x, ImMax(xax.Datum1,xax.Datum2))); + xax.PixelMin = xax.IsInverted() ? plot.PlotRect.Max.x : plot.PlotRect.Min.x; + xax.PixelMax = xax.IsInverted() ? plot.PlotRect.Min.x : plot.PlotRect.Max.x; + xax.UpdateTransformCache(); } -} -inline void EndDisabledControls(bool cond) { - if (cond) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { + ImPlotAxis& yax = plot.YAxis(i); + yax.HoverRect = ImRect(ImVec2(ImMin(yax.Datum1,yax.Datum2),plot.PlotRect.Min.y), + ImVec2(ImMax(yax.Datum1,yax.Datum2),plot.PlotRect.Max.y)); + yax.PixelMin = yax.IsInverted() ? plot.PlotRect.Min.y : plot.PlotRect.Max.y; + yax.PixelMax = yax.IsInverted() ? plot.PlotRect.Max.y : plot.PlotRect.Min.y; + yax.UpdateTransformCache(); + } + // Equal axis constraint. Must happen after we set Pixels + // constrain equal axes for primary x and y if not approximately equal + // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case + if (axis_equal) { + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_axis.OrthoAxis == NULL) + continue; + double xar = x_axis.GetAspect(); + double yar = x_axis.OrthoAxis->GetAspect(); + // edge case: user has set x range this frame, so fit y to x so that we honor their request for x range + // NB: because of feedback across several frames, the user's x request may not be perfectly honored + if (x_axis.HasRange) + x_axis.OrthoAxis->SetAspect(xar); + else if (!ImAlmostEqual(xar,yar) && !x_axis.OrthoAxis->IsInputLocked()) + x_axis.SetAspect(yar); + } } -} -void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed) { + // INPUT ------------------------------------------------------------------ + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoInputs)) + UpdateInput(plot); - ImGui::PushItemWidth(75); - bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); - bool label = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoLabel); - bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); - bool ticks = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); - bool labels = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); - double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. + // fit from FitNextPlotAxes or auto fit + for (int i = 0; i < ImAxis_COUNT; ++i) { + if (gp.NextPlotData.Fit[i] || plot.Axes[i].IsAutoFitting()) { + plot.FitThisFrame = true; + plot.Axes[i].FitThisFrame = true; + } + } - if (axis.IsTime()) { - ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); - ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); + // RENDER ----------------------------------------------------------------- - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMin() || always_locked); - if (ImGui::BeginMenu("Min Time")) { - if (ShowTimePicker("mintime", &tmin)) { - if (tmin >= tmax) - tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); - axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); - } - ImGui::Separator(); - if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) { - tmin = CombineDateTime(axis.PickerTimeMin, tmin); - if (tmin >= tmax) - tmax = AddTime(tmin, ImPlotTimeUnit_S, 1); - axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); - } - ImGui::EndMenu(); - } - EndDisabledControls(axis.IsLockedMin() || always_locked); + const float txt_height = ImGui::GetTextLineHeight(); - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMax() || always_locked); - if (ImGui::BeginMenu("Max Time")) { - if (ShowTimePicker("maxtime", &tmax)) { - if (tmax <= tmin) - tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); - axis.SetRange(tmin.ToDouble(),tmax.ToDouble()); - } - ImGui::Separator(); - if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) { - tmax = CombineDateTime(axis.PickerTimeMax, tmax); - if (tmax <= tmin) - tmin = AddTime(tmax, ImPlotTimeUnit_S, -1); - axis.SetRange(tmin.ToDouble(), tmax.ToDouble()); + // render frame + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoFrame)) + ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); + + // grid bg + DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); + + // transform ticks + for (int i = 0; i < ImAxis_COUNT; i++) { + ImPlotAxis& axis = plot.Axes[i]; + if (axis.WillRender()) { + for (int t = 0; t < axis.Ticks.Size; t++) { + ImPlotTick& tk = axis.Ticks.Ticks[t]; + tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); } - ImGui::EndMenu(); } - EndDisabledControls(axis.IsLockedMax() || always_locked); } - else { - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMin() || always_locked); - double temp_min = axis.Range.Min; - if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { - axis.SetMin(temp_min,true); - if (equal_axis != NULL) - equal_axis->SetAspect(axis.GetAspect()); - } - EndDisabledControls(axis.IsLockedMin() || always_locked); - BeginDisabledControls(always_locked); - ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(always_locked); - ImGui::SameLine(); - BeginDisabledControls(axis.IsLockedMax() || always_locked); - double temp_max = axis.Range.Max; - if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { - axis.SetMax(temp_max,true); - if (equal_axis != NULL) - equal_axis->SetAspect(axis.GetAspect()); - } - EndDisabledControls(axis.IsLockedMax() || always_locked); + // render grid (background) + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) + RenderGridLinesX(DrawList, x_axis.Ticks, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } - - ImGui::Separator(); - - ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); - ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); - BeginDisabledControls(axis.IsTime() && time_allowed); - ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); - EndDisabledControls(axis.IsTime() && time_allowed); - - if (time_allowed) { - BeginDisabledControls(axis.IsLog()); - ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); - EndDisabledControls(axis.IsLog()); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) + RenderGridLinesY(DrawList, y_axis.Ticks, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } - ImGui::Separator(); - if (ImGui::Checkbox("Label", &label)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); - if (ImGui::Checkbox("Grid Lines", &grid)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); - if (ImGui::Checkbox("Tick Marks", &ticks)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); - if (ImGui::Checkbox("Tick Labels", &labels)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); -} - -void ShowPlotContextMenu(ImPlotPlot& plot) { - const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - if (ImGui::BeginMenu("X-Axis")) { - ImGui::PushID("X"); - ShowAxisContextMenu(plot.XAxis, equal ? &plot.YAxis[0] : NULL, true); - ImGui::PopID(); - ImGui::EndMenu(); - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (i == 1 && !ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)) { + // render x axis button, label, tick labels + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& ax = plot.XAxis(i); + if (!ax.Enabled) continue; + if ((ax.Hovered || ax.Held) && !plot.Held) + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); + else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); + ax.ColorHiLi = IM_COL32_BLACK_TRANS; } - if (i == 2 && !ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)) { - continue; + else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } - char buf[10] = {}; - if (i == 0) { - snprintf(buf, sizeof(buf) - 1, "Y-Axis"); - } else { - snprintf(buf, sizeof(buf) - 1, "Y-Axis %d", i + 1); + const ImPlotTickCollection& tkc = ax.Ticks; + const bool opp = ax.IsOpposite(); + if (ax.HasLabel()) { + const char* label = plot.GetAxisLabel(ax); + const ImVec2 label_size = ImGui::CalcTextSize(label); + const float label_offset = (ax.HasTickLabels() ? ax.Ticks.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) + + (ax.IsTime() ? txt_height + gp.Style.LabelPadding.y : 0) + + gp.Style.LabelPadding.y; + const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, + opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); + DrawList.AddText(label_pos, ax.ColorTxt, label); } - if (ImGui::BeginMenu(buf)) { - ImGui::PushID(i); - ShowAxisContextMenu(plot.YAxis[i], (equal && i == 0) ? &plot.XAxis : NULL, false); - ImGui::PopID(); - ImGui::EndMenu(); + if (ax.HasTickLabels()) { + for (int j = 0; j < tkc.Size; ++j) { + const ImPlotTick& tk = tkc.Ticks[j]; + const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) + : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); + if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { + ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); + DrawList.AddText(start, ax.ColorTxt, tkc.GetText(j)); + } + } } } - ImGui::Separator(); - if ((ImGui::BeginMenu("Settings"))) { - if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) - ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased); - if (ImGui::MenuItem("Equal", NULL, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) - ImFlipFlag(plot.Flags, ImPlotFlags_Equal); - if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); - if (ImGui::MenuItem("Query",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Query))) - ImFlipFlag(plot.Flags, ImPlotFlags_Query); - if (ImGui::MenuItem("Title",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoTitle))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); - if (ImGui::MenuItem("Mouse Position",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoMousePos); - if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) - ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); - if ((ImGui::BeginMenu("Legend"))) { - const float s = ImGui::GetFrameHeight(); - if (ImGui::RadioButton("H", plot.LegendOrientation == ImPlotOrientation_Horizontal)) - plot.LegendOrientation = ImPlotOrientation_Horizontal; - ImGui::SameLine(); - if (ImGui::RadioButton("V", plot.LegendOrientation == ImPlotOrientation_Vertical)) - plot.LegendOrientation = ImPlotOrientation_Vertical; - ImGui::Checkbox("Outside", &plot.LegendOutside); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1,1)); - if (ImGui::Button("##NW",ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_NorthWest; } ImGui::SameLine(); - if (ImGui::Button("##N", ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_North; } ImGui::SameLine(); - if (ImGui::Button("##NE",ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_NorthEast; } - if (ImGui::Button("##W", ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_West; } ImGui::SameLine(); - if (ImGui::Button("##C", ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_Center; } ImGui::SameLine(); - if (ImGui::Button("##E", ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_East; } - if (ImGui::Button("##SW",ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_SouthWest; } ImGui::SameLine(); - if (ImGui::Button("##S", ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_South; } ImGui::SameLine(); - if (ImGui::Button("##SE",ImVec2(1.5f*s,s))) { plot.LegendLocation = ImPlotLocation_SouthEast; } - ImGui::PopStyleVar(); - ImGui::EndMenu(); + // render y axis button, label, tick labels + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& ax = plot.YAxis(i); + if (!ax.Enabled) + continue; + if ((ax.Hovered || ax.Held) && !plot.Held) + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.Held ? ax.ColorAct : ax.ColorHov); + else if (ax.ColorHiLi != IM_COL32_BLACK_TRANS) { + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorHiLi); + ax.ColorHiLi = IM_COL32_BLACK_TRANS; + } + else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { + DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); + } + const ImPlotTickCollection& tkc = ax.Ticks; + const bool opp = ax.IsOpposite(); + if (ax.HasLabel()) { + const char* label = plot.GetAxisLabel(ax); + const ImVec2 label_size = CalcTextSizeVertical(label); + const float label_offset = (ax.HasTickLabels() ? ax.Ticks.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) + + gp.Style.LabelPadding.x; + const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, + plot.PlotRect.GetCenter().y + label_size.y * 0.5f); + AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); + } + if (ax.HasTickLabels()) { + for (int j = 0; j < tkc.Size; ++j) { + const ImPlotTick& tk = tkc.Ticks[j]; + const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); + if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { + ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); + DrawList.AddText(start, ax.ColorTxt, tkc.GetText(j)); + } + } } - ImGui::EndMenu(); - } - if (ImGui::MenuItem("Legend",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) { - ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); } + + + // clear legend (TODO: put elsewhere) + plot.Items.Legend.Reset(); + // push ID to set item hashes (NB: !!!THIS PROBABLY NEEDS TO BE IN BEGIN PLOT!!!!) + ImGui::PushOverrideID(gp.CurrentItems->ID); } //----------------------------------------------------------------------------- @@ -2224,67 +2626,96 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { void EndPlot() { IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "Mismatched BeginPlot()/EndPlot()!"); + + SetupLock(); + ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Mismatched BeginPlot()/EndPlot()!"); ImGuiContext &G = *GImGui; - ImPlotPlot &plot = *gp.CurrentPlot; + ImPlotPlot &plot = *gp.CurrentPlot; ImGuiWindow * Window = G.CurrentWindow; ImDrawList & DrawList = *Window->DrawList; const ImGuiIO & IO = ImGui::GetIO(); - // AXIS STATES ------------------------------------------------------------ + // FINAL RENDER ----------------------------------------------------------- - const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; + const bool render_border = gp.Style.PlotBorderSize > 0 && gp.Style.Colors[ImPlotCol_PlotBorder].w > 0; + const bool any_x_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_X1], IMPLOT_NUM_X_AXES); + const bool any_y_held = plot.Held || AnyAxesHeld(&plot.Axes[ImAxis_Y1], IMPLOT_NUM_Y_AXES); - // FINAL RENDER ----------------------------------------------------------- + ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); // render grid (foreground) - PushPlotClipRect(gp.Style.PlotBorderSize == 0 ? 1.0f : 0.0f); - if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) && ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Foreground)) - RenderGridLinesX(DrawList, gp.XTicks, plot.PlotRect, plot.XAxis.ColorMaj, plot.XAxis.ColorMaj, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Foreground)) - RenderGridLinesY(DrawList, gp.YTicks[i], plot.PlotRect, plot.YAxis[i].ColorMaj, plot.YAxis[i].ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) + RenderGridLinesX(DrawList, x_axis.Ticks, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); + } + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) + RenderGridLinesY(DrawList, y_axis.Ticks, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } - PopPlotClipRect(); - // render x-ticks - PushPlotClipRect(); - if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks)) { - for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks.Ticks[t]; - if (xt->Level == 0) - DrawList.AddLine(ImVec2(xt->PixelPos, plot.PlotRect.Max.y), - ImVec2(xt->PixelPos, plot.PlotRect.Max.y - (xt->Major ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x)), - plot.XAxis.ColorMaj, - xt->Major ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x); - } + + // render title + if (plot.HasTitle()) { + ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); + AddTextCentered(&DrawList,ImVec2(plot.PlotRect.GetCenter().x, plot.CanvasRect.Min.y),col,plot.GetTitle()); } - PopPlotClipRect(); - // render y-ticks - ImGui::PushClipRect(plot.PlotRect.Min, ImVec2(plot.FrameRect.Max.x, plot.PlotRect.Max.y), true); - int axis_count = 0; - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!plot.YAxis[i].Present) { continue; } - axis_count++; - float x_start = gp.YAxisReference[i]; - if (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickMarks)) { - float direction = (i == 0) ? 1.0f : -1.0f; - bool no_major = axis_count >= 3; - for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; - ImVec2 start = ImVec2(x_start, yt->PixelPos); - DrawList.AddLine(start, - start + ImVec2(direction * ((!no_major && yt->Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y), 0), - plot.YAxis[i].ColorMaj, - (!no_major && yt->Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y); + // render x ticks + int count_B = 0, count_T = 0; + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + const ImPlotAxis& ax = plot.XAxis(i); + if (!ax.Enabled) + continue; + const ImPlotTickCollection& tkc = ax.Ticks; + const bool opp = ax.IsOpposite(); + const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); + if (ax.HasTickMarks()) { + const float direction = opp ? 1.0f : -1.0f; + for (int j = 0; j < tkc.Size; ++j) { + const ImPlotTick& tk = tkc.Ticks[j]; + if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) + continue; + const ImVec2 start(tk.PixelPos, ax.Datum1); + const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x; + const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x; + DrawList.AddLine(start, start + ImVec2(0,direction*len), ax.ColorTick, thk); } + if (aux || !render_border) + DrawList.AddLine(ImVec2(plot.PlotRect.Min.x,ax.Datum1), ImVec2(plot.PlotRect.Max.x,ax.Datum1), ax.ColorTick, gp.Style.MinorTickSize.x); } - if (axis_count >= 3) { - // Draw a bar next to the ticks to act as a visual separator. - DrawList.AddLine(ImVec2(x_start, plot.PlotRect.Min.y), ImVec2(x_start, plot.PlotRect.Max.y), GetStyleColorU32(ImPlotCol_YAxisGrid3), 1); + count_B += !opp; + count_T += opp; + } + + // render y ticks + int count_L = 0, count_R = 0; + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + const ImPlotAxis& ax = plot.YAxis(i); + if (!ax.Enabled) + continue; + const ImPlotTickCollection& tkc = ax.Ticks; + const bool opp = ax.IsOpposite(); + const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); + if (ax.HasTickMarks()) { + const float direction = opp ? -1.0f : 1.0f; + for (int j = 0; j < tkc.Size; ++j) { + const ImPlotTick& tk = tkc.Ticks[j]; + if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) + continue; + const ImVec2 start(ax.Datum1, tk.PixelPos); + const float len = (!aux && tk.Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; + const float thk = (!aux && tk.Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; + DrawList.AddLine(start, start + ImVec2(direction*len,0), ax.ColorTick, thk); + } + if (aux || !render_border) + DrawList.AddLine(ImVec2(ax.Datum1, plot.PlotRect.Min.y), ImVec2(ax.Datum1, plot.PlotRect.Max.y), ax.ColorTick, gp.Style.MinorTickSize.y); } + count_L += !opp; + count_R += opp; } ImGui::PopClipRect(); @@ -2331,12 +2762,9 @@ void EndPlot() { // render selection if (plot.Selected) RenderSelectionRect(DrawList, plot.SelectRect.Min + plot.PlotRect.Min, plot.SelectRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Selection)); - // render query - if (plot.Queried) - RenderSelectionRect(DrawList, plot.QueryRect.Min + plot.PlotRect.Min, plot.QueryRect.Max + plot.PlotRect.Min, GetStyleColorVec4(ImPlotCol_Query)); // render crosshairs - if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.PlotHovered && !(plot.XAxis.Dragging || any_y_dragging) && !plot.Selecting && !plot.Querying && !plot.LegendHovered) { + if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.Hovered && !(any_x_held || any_y_held) && !plot.Selecting && !plot.Items.Legend.Hovered) { ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImVec2 xy = IO.MousePos; ImVec2 h1(plot.PlotRect.Min.x, xy.y); @@ -2355,382 +2783,775 @@ void EndPlot() { } // render mouse pos - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos) && plot.PlotHovered) { - char buffer[128] = {}; - ImBufferWriter writer(buffer, sizeof(buffer)); - // x - if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Time)) { - ImPlotTimeUnit unit = GetUnitForRange(plot.XAxis.Range.Size() / (plot.PlotRect.GetWidth() / 100)); - const int written = FormatDateTime(ImPlotTime::FromDouble(gp.MousePos[0].x), &writer.Buffer[writer.Pos], writer.Size - writer.Pos - 1, GetDateTimeFmt(TimeFormatMouseCursor, unit)); - if (written > 0) - writer.Pos += ImMin(written, writer.Size - writer.Pos - 1); - } - else { - writer.Write(GetFormatX(), RoundAxisValue(plot.XAxis, gp.XTicks, gp.MousePos[0].x)); + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMouseText) && (plot.Hovered || ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_ShowAlways))) { + + const bool no_aux = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoAuxAxes); + const bool no_fmt = ImHasFlag(plot.MouseTextFlags, ImPlotMouseTextFlags_NoFormat); + + ImGuiTextBuffer& builder = gp.MousePosStringBuilder; + builder.Buf.shrink(0); + char buff[IMPLOT_LABEL_MAX_SIZE]; + + const int num_x = no_aux ? 1 : IMPLOT_NUM_X_AXES; + for (int i = 0; i < num_x; ++i) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (!x_axis.Enabled) + continue; + if (i > 0) + builder.append(", ("); + double v = x_axis.PixelsToPlot(IO.MousePos.x); + no_fmt ? DefaultFormatter(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT) + : LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); + builder.append(buff); + if (i > 0) + builder.append(")"); } - // y1 - writer.Write(", "); - writer.Write(GetFormatY(0), RoundAxisValue(plot.YAxis[0], gp.YTicks[0], gp.MousePos[0].y)); - // y2 - if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)) { - writer.Write(", ("); - writer.Write(GetFormatY(1), RoundAxisValue(plot.YAxis[1], gp.YTicks[1], gp.MousePos[1].y)); - writer.Write(")"); + builder.append(", "); + const int num_y = no_aux ? 1 : IMPLOT_NUM_Y_AXES; + for (int i = 0; i < num_y; ++i) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (!y_axis.Enabled) + continue; + if (i > 0) + builder.append(", ("); + double v = y_axis.PixelsToPlot(IO.MousePos.y); + no_fmt ? DefaultFormatter(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT) + : LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); + builder.append(buff); + if (i > 0) + builder.append(")"); } - // y3 - if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)) { - writer.Write(", ("); - writer.Write(GetFormatY(2), RoundAxisValue(plot.YAxis[2], gp.YTicks[2], gp.MousePos[2].y)); - writer.Write(")"); + + if (!builder.empty()) { + const ImVec2 size = ImGui::CalcTextSize(builder.c_str()); + const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MouseTextLocation, gp.Style.MousePosPadding); + DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), builder.c_str()); } - const ImVec2 size = ImGui::CalcTextSize(buffer); - const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MousePosLocation, gp.Style.MousePosPadding); - DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), buffer); } PopPlotClipRect(); + // axis side switch + if (!plot.Held) { + ImVec2 mouse_pos = ImGui::GetIO().MousePos; + ImRect trigger_rect = plot.PlotRect; + trigger_rect.Expand(-10); + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_axis.Held && plot.PlotRect.Contains(mouse_pos)) { + const bool opp = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Opposite); + if (!opp) { + ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, + plot.PlotRect.Max.x + 5, plot.PlotRect.Min.y + 5); + if (mouse_pos.y < plot.PlotRect.Max.y - 10) + DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); + if (rect.Contains(mouse_pos)) + x_axis.Flags |= ImPlotAxisFlags_Opposite; + } + else { + ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Max.y - 5, + plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); + if (mouse_pos.y > plot.PlotRect.Min.y + 10) + DrawList.AddRectFilled(rect.Min, rect.Max, x_axis.ColorHov); + if (rect.Contains(mouse_pos)) + x_axis.Flags &= ~ImPlotAxisFlags_Opposite; + } + } + } + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (y_axis.Held && plot.PlotRect.Contains(mouse_pos)) { + const bool opp = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Opposite); + if (!opp) { + ImRect rect(plot.PlotRect.Max.x - 5, plot.PlotRect.Min.y - 5, + plot.PlotRect.Max.x + 5, plot.PlotRect.Max.y + 5); + if (mouse_pos.x > plot.PlotRect.Min.x + 10) + DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); + if (rect.Contains(mouse_pos)) + y_axis.Flags |= ImPlotAxisFlags_Opposite; + } + else { + ImRect rect(plot.PlotRect.Min.x - 5, plot.PlotRect.Min.y - 5, + plot.PlotRect.Min.x + 5, plot.PlotRect.Max.y + 5); + if (mouse_pos.x < plot.PlotRect.Max.x - 10) + DrawList.AddRectFilled(rect.Min, rect.Max, y_axis.ColorHov); + if (rect.Contains(mouse_pos)) + y_axis.Flags &= ~ImPlotAxisFlags_Opposite; + } + } + } + } + // reset legend hovers - plot.LegendHovered = false; - for (int i = 0; i < plot.Items.GetSize(); ++i) - plot.Items.GetByIndex(i)->LegendHovered = false; + plot.Items.Legend.Hovered = false; + for (int i = 0; i < plot.Items.GetItemCount(); ++i) + plot.Items.GetItemByIndex(i)->LegendHovered = false; // render legend - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.GetLegendCount() > 0) { - const ImVec2 legend_size = CalcLegendSize(plot, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation); - const ImVec2 legend_pos = GetLocationPos(plot.LegendOutside ? plot.FrameRect : plot.PlotRect, + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Items.GetLegendCount() > 0) { + ImPlotLegend& legend = plot.Items.Legend; + const bool legend_out = ImHasFlag(legend.Flags, ImPlotLegendFlags_Outside); + const bool legend_horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); + const ImVec2 legend_size = CalcLegendSize(plot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); + const ImVec2 legend_pos = GetLocationPos(legend_out ? plot.FrameRect : plot.PlotRect, legend_size, - plot.LegendLocation, - plot.LegendOutside ? gp.Style.PlotPadding : gp.Style.LegendPadding); - plot.LegendRect = ImRect(legend_pos, legend_pos + legend_size); + legend.Location, + legend_out ? gp.Style.PlotPadding : gp.Style.LegendPadding); + legend.Rect = ImRect(legend_pos, legend_pos + legend_size); // test hover - plot.LegendHovered = plot.FrameHovered && plot.LegendRect.Contains(IO.MousePos); + legend.Hovered = ImGui::IsWindowHovered() && legend.Rect.Contains(IO.MousePos); - if (plot.LegendOutside) + if (legend_out) ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); else PushPlotClipRect(); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); - DrawList.AddRectFilled(plot.LegendRect.Min, plot.LegendRect.Max, col_bg); - DrawList.AddRect(plot.LegendRect.Min, plot.LegendRect.Max, col_bd); - ShowLegendEntries(plot, plot.LegendRect, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList); + DrawList.AddRectFilled(legend.Rect.Min, legend.Rect.Max, col_bg); + DrawList.AddRect(legend.Rect.Min, legend.Rect.Max, col_bd); + bool legend_contextable = ShowLegendEntries(plot.Items, legend.Rect, legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) + && !ImHasFlag(legend.Flags, ImPlotLegendFlags_NoMenus); + + // main ctx menu + if (gp.OpenContextThisFrame && legend_contextable && !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus)) + ImGui::OpenPopup("##LegendContext"); ImGui::PopClipRect(); + if (ImGui::BeginPopup("##LegendContext")) { + ImGui::Text("Legend"); ImGui::Separator(); + if (ShowLegendContextMenu(legend, !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); + ImGui::EndPopup(); + } } else { - plot.LegendRect = ImRect(); - } - if (plot.LegendFlipSideNextFrame) { - plot.LegendOutside = !plot.LegendOutside; - plot.LegendFlipSideNextFrame = false; + plot.Items.Legend.Rect = ImRect(); } // render border - if (gp.Style.PlotBorderSize > 0) + if (render_border) DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawFlags_RoundCornersAll, gp.Style.PlotBorderSize); + // render tags + for (int i = 0; i < gp.Tags.Size; ++i) { + ImPlotTag& tag = gp.Tags.Tags[i]; + ImPlotAxis& axis = plot.Axes[tag.Axis]; + if (!axis.Enabled || !axis.Range.Contains(tag.Value)) + continue; + const char* txt = gp.Tags.GetText(i); + ImVec2 text_size = ImGui::CalcTextSize(txt); + ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; + ImVec2 pos; + axis.Ticks.OverrideSizeLate(size); + float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); + if (axis.Vertical) { + if (axis.IsOpposite()) { + pos = ImVec2(axis.Datum1 + gp.Style.LabelPadding.x, pix - size.y * 0.5f); + DrawList.AddTriangleFilled(ImVec2(axis.Datum1,pix), pos, pos + ImVec2(0,size.y), tag.ColorBg); + } + else { + pos = ImVec2(axis.Datum1 - size.x - gp.Style.LabelPadding.x, pix - size.y * 0.5f); + DrawList.AddTriangleFilled(pos + ImVec2(size.x,0), ImVec2(axis.Datum1,pix), pos+size, tag.ColorBg); + } + } + else { + if (axis.IsOpposite()) { + pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 - size.y - gp.Style.LabelPadding.y ); + DrawList.AddTriangleFilled(pos + ImVec2(0,size.y), pos + size, ImVec2(pix,axis.Datum1), tag.ColorBg); + } + else { + pos = ImVec2(pix - size.x * 0.5f, axis.Datum1 + gp.Style.LabelPadding.y); + DrawList.AddTriangleFilled(pos, ImVec2(pix,axis.Datum1), pos + ImVec2(size.x, 0), tag.ColorBg); + } + } + DrawList.AddRectFilled(pos,pos+size,tag.ColorBg); + DrawList.AddText(pos+gp.Style.AnnotationPadding,tag.ColorFg,txt); + } + // FIT DATA -------------------------------------------------------------- const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); - if (gp.FitThisFrame && (gp.VisibleItemCount > 0 || plot.Queried)) { - if (gp.FitX) { - const double ext_size = gp.ExtentsX.Size() * 0.5; - gp.ExtentsX.Min -= ext_size * gp.Style.FitPadding.x; - gp.ExtentsX.Max += ext_size * gp.Style.FitPadding.x; - if (!plot.XAxis.IsLockedMin() && !ImNanOrInf(gp.ExtentsX.Min)) - plot.XAxis.Range.Min = (gp.ExtentsX.Min); - if (!plot.XAxis.IsLockedMax() && !ImNanOrInf(gp.ExtentsX.Max)) - plot.XAxis.Range.Max = (gp.ExtentsX.Max); - if (ImAlmostEqual(plot.XAxis.Range.Max, plot.XAxis.Range.Min)) { - plot.XAxis.Range.Max += 0.5; - plot.XAxis.Range.Min -= 0.5; + if (plot.FitThisFrame) { + for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { + ImPlotAxis& x_axis = plot.XAxis(i); + if (x_axis.FitThisFrame) { + x_axis.ApplyFit(gp.Style.FitPadding.x); + if (axis_equal && x_axis.OrthoAxis != NULL) { + double aspect = x_axis.GetAspect(); + ImPlotAxis& y_axis = *x_axis.OrthoAxis; + if (y_axis.FitThisFrame) { + y_axis.ApplyFit(gp.Style.FitPadding.y); + y_axis.FitThisFrame = false; + aspect = ImMax(aspect, y_axis.GetAspect()); + } + x_axis.SetAspect(aspect); + y_axis.SetAspect(aspect); + } } - plot.XAxis.Constrain(); - if (axis_equal && !gp.FitY[0]) - plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.FitY[i]) { - const double ext_size = gp.ExtentsY[i].Size() * 0.5; - gp.ExtentsY[i].Min -= ext_size * gp.Style.FitPadding.y; - gp.ExtentsY[i].Max += ext_size * gp.Style.FitPadding.y; - if (!plot.YAxis[i].IsLockedMin() && !ImNanOrInf(gp.ExtentsY[i].Min)) - plot.YAxis[i].Range.Min = (gp.ExtentsY[i].Min); - if (!plot.YAxis[i].IsLockedMax() && !ImNanOrInf(gp.ExtentsY[i].Max)) - plot.YAxis[i].Range.Max = (gp.ExtentsY[i].Max); - if (ImAlmostEqual(plot.YAxis[i].Range.Max, plot.YAxis[i].Range.Min)) { - plot.YAxis[i].Range.Max += 0.5; - plot.YAxis[i].Range.Min -= 0.5; + for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { + ImPlotAxis& y_axis = plot.YAxis(i); + if (y_axis.FitThisFrame) { + y_axis.ApplyFit(gp.Style.FitPadding.y); + if (axis_equal && y_axis.OrthoAxis != NULL) { + double aspect = y_axis.GetAspect(); + ImPlotAxis& x_axis = *y_axis.OrthoAxis; + if (x_axis.FitThisFrame) { + x_axis.ApplyFit(gp.Style.FitPadding.x); + x_axis.FitThisFrame = false; + aspect = ImMax(x_axis.GetAspect(), aspect); + } + x_axis.SetAspect(aspect); + y_axis.SetAspect(aspect); } - plot.YAxis[i].Constrain(); - if (i == 0 && axis_equal && !gp.FitX) - plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); } } - if (axis_equal && gp.FitX && gp.FitY[0]) { - double aspect = ImMax(plot.XAxis.GetAspect(), plot.YAxis[0].GetAspect()); - plot.XAxis.SetAspect(aspect); - plot.YAxis[0].SetAspect(aspect); - } + plot.FitThisFrame = false; } // CONTEXT MENUS ----------------------------------------------------------- + ImGui::PushOverrideID(plot.ID); + + const bool can_ctx = gp.OpenContextThisFrame && + !ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && + !plot.Items.Legend.Hovered; + + + // main ctx menu - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.PlotHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) + if (can_ctx && plot.Hovered) ImGui::OpenPopup("##PlotContext"); if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); ImGui::EndPopup(); } - // x-axis ctx menu - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.XAxis.ExtHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) - ImGui::OpenPopup("##XContext"); - if (ImGui::BeginPopup("##XContext")) { - ImGui::Text("X-Axis"); ImGui::Separator(); - ShowAxisContextMenu(plot.XAxis, ImHasFlag(plot.Flags, ImPlotFlags_Equal) ? &plot.YAxis[0] : NULL, true); - ImGui::EndPopup(); + // axes ctx menus + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImGui::PushID(i); + ImPlotAxis& x_axis = plot.XAxis(i); + if (can_ctx && x_axis.Hovered && x_axis.HasMenus()) + ImGui::OpenPopup("##XContext"); + if (ImGui::BeginPopup("##XContext")) { + ImGui::Text(x_axis.HasLabel() ? plot.GetAxisLabel(x_axis) : i == 0 ? "X-Axis" : "X-Axis %d", i + 1); + ImGui::Separator(); + ShowAxisContextMenu(x_axis, axis_equal ? x_axis.OrthoAxis : NULL, true); + ImGui::EndPopup(); + } + ImGui::PopID(); } - - // y-axes ctx menus - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { ImGui::PushID(i); - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.YAxis[i].ExtHovered && IO.MouseReleased[gp.InputMap.ContextMenuButton] && !plot.LegendHovered && !plot.ContextLocked) + ImPlotAxis& y_axis = plot.YAxis(i); + if (can_ctx && y_axis.Hovered && y_axis.HasMenus()) ImGui::OpenPopup("##YContext"); if (ImGui::BeginPopup("##YContext")) { - if (i == 0) { - ImGui::Text("Y-Axis"); ImGui::Separator(); - } - else { - ImGui::Text("Y-Axis %d", i + 1); ImGui::Separator(); - } - ShowAxisContextMenu(plot.YAxis[i], (i == 0 && ImHasFlag(plot.Flags, ImPlotFlags_Equal)) ? &plot.XAxis : NULL, false); + ImGui::Text(y_axis.HasLabel() ? plot.GetAxisLabel(y_axis) : i == 0 ? "Y-Axis" : "Y-Axis %d", i + 1); + ImGui::Separator(); + ShowAxisContextMenu(y_axis, axis_equal ? y_axis.OrthoAxis : NULL, false); ImGui::EndPopup(); } ImGui::PopID(); } - + ImGui::PopID(); // LINKED AXES ------------------------------------------------------------ - PushLinkedAxis(plot.XAxis); - for (int i = 0; i < IMPLOT_Y_AXES; ++i) - PushLinkedAxis(plot.YAxis[i]); + for (int i = 0; i < ImAxis_COUNT; ++i) + plot.Axes[i].PushLinks(); - // CLEANUP ---------------------------------------------------------------- - - // resset context locked flag - if (plot.ContextLocked && IO.MouseReleased[gp.InputMap.BoxSelectButton]) - plot.ContextLocked = false; + // CLEANUP ---------------------------------------------------------------- + // remove items + if (gp.CurrentItems == &plot.Items) + gp.CurrentItems = NULL; // reset the plot items for the next frame - for (int i = 0; i < plot.Items.GetSize(); ++i) { - plot.Items.GetByIndex(i)->SeenThisFrame = false; + for (int i = 0; i < plot.Items.GetItemCount(); ++i) { + plot.Items.GetItemByIndex(i)->SeenThisFrame = false; } // mark the plot as initialized, i.e. having made it through one frame completely plot.Initialized = true; - // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // Reset context for next plot - Reset(GImPlot); + ResetCtxForNextPlot(GImPlot); + + // setup next subplot + if (gp.CurrentSubplot != NULL) { + ImGui::PopID(); + SubplotNextCell(); + } } //----------------------------------------------------------------------------- -// MISC API +// BEGIN/END SUBPLOT //----------------------------------------------------------------------------- -ImPlotInputMap& GetInputMap() { - return GImPlot->InputMap; -} +static const float SUBPLOT_BORDER_SIZE = 1.0f; +static const float SUBPLOT_SPLITTER_HALF_THICKNESS = 4.0f; +static const float SUBPLOT_SPLITTER_FEEDBACK_TIMER = 0.06f; -void SetNextPlotLimits(double x_min, double x_max, double y_min, double y_max, ImGuiCond cond) { - IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot == NULL, "SetNextPlotLimits() needs to be called before BeginPlot()!"); - SetNextPlotLimitsX(x_min, x_max, cond); - SetNextPlotLimitsY(y_min, y_max, cond); -} +void SubplotSetCell(int row, int col) { + ImPlotContext& gp = *GImPlot; + ImPlotSubplot& subplot = *gp.CurrentSubplot; + if (row >= subplot.Rows || col >= subplot.Cols) + return; + float xoff = 0; + float yoff = 0; + for (int c = 0; c < col; ++c) + xoff += subplot.ColRatios[c]; + for (int r = 0; r < row; ++r) + yoff += subplot.RowRatios[r]; + const ImVec2 grid_size = subplot.GridRect.GetSize(); + ImVec2 cpos = subplot.GridRect.Min + ImVec2(xoff*grid_size.x,yoff*grid_size.y); + cpos.x = IM_ROUND(cpos.x); + cpos.y = IM_ROUND(cpos.y); + ImGui::GetCurrentWindow()->DC.CursorPos = cpos; + // set cell size + subplot.CellSize.x = IM_ROUND(subplot.GridRect.GetWidth() * subplot.ColRatios[col]); + subplot.CellSize.y = IM_ROUND(subplot.GridRect.GetHeight() * subplot.RowRatios[row]); + // setup links + const bool lx = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllX); + const bool ly = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkAllY); + const bool lr = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkRows); + const bool lc = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_LinkCols); -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; - gp.NextPlotData.XRangeCond = cond; - gp.NextPlotData.XRange.Min = x_min; - gp.NextPlotData.XRange.Max = x_max; + SetNextAxisLinks(ImAxis_X1, lx ? &subplot.ColLinkData[0].Min : lc ? &subplot.ColLinkData[col].Min : NULL, + lx ? &subplot.ColLinkData[0].Max : lc ? &subplot.ColLinkData[col].Max : NULL); + SetNextAxisLinks(ImAxis_Y1, ly ? &subplot.RowLinkData[0].Min : lr ? &subplot.RowLinkData[row].Min : NULL, + ly ? &subplot.RowLinkData[0].Max : lr ? &subplot.RowLinkData[row].Max : NULL); + // setup alignment + if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoAlign)) { + gp.CurrentAlignmentH = &subplot.RowAlignmentData[row]; + gp.CurrentAlignmentV = &subplot.ColAlignmentData[col]; + } + // set idx + if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) + subplot.CurrentIdx = col * subplot.Rows + row; + else + subplot.CurrentIdx = row * subplot.Cols + col; } -void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, ImPlotYAxis 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 < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES"); - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - gp.NextPlotData.HasYRange[y_axis] = true; - gp.NextPlotData.YRangeCond[y_axis] = cond; - gp.NextPlotData.YRange[y_axis].Min = y_min; - gp.NextPlotData.YRange[y_axis].Max = y_max; +void SubplotSetCell(int idx) { + ImPlotContext& gp = *GImPlot; + ImPlotSubplot& subplot = *gp.CurrentSubplot; + if (idx >= subplot.Rows * subplot.Cols) + return; + int row = 0, col = 0; + if (ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ColMajor)) { + row = idx % subplot.Rows; + col = idx / subplot.Rows; + } + else { + row = idx / subplot.Cols; + col = idx % subplot.Cols; + } + return SubplotSetCell(row, col); } -void LinkNextPlotLimits(double* xmin, double* xmax, double* ymin, double* ymax, double* ymin2, double* ymax2, double* ymin3, double* ymax3) { - ImPlotContext& gp = *GImPlot; - gp.NextPlotData.LinkedXmin = xmin; - gp.NextPlotData.LinkedXmax = xmax; - gp.NextPlotData.LinkedYmin[0] = ymin; - gp.NextPlotData.LinkedYmax[0] = ymax; - gp.NextPlotData.LinkedYmin[1] = ymin2; - gp.NextPlotData.LinkedYmax[1] = ymax2; - gp.NextPlotData.LinkedYmin[2] = ymin3; - gp.NextPlotData.LinkedYmax[2] = ymax3; +void SubplotNextCell() { + ImPlotContext& gp = *GImPlot; + ImPlotSubplot& subplot = *gp.CurrentSubplot; + SubplotSetCell(++subplot.CurrentIdx); } -void FitNextPlotAxes(bool x, bool y, bool y2, bool y3) { +bool BeginSubplots(const char* title, int rows, int cols, const ImVec2& size, ImPlotSubplotFlags flags, float* row_sizes, float* col_sizes) { + IM_ASSERT_USER_ERROR(rows > 0 && cols > 0, "Invalid sizing arguments!"); + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentSubplot == NULL, "Mismatched BeginSubplots()/EndSubplots()!"); ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "FitNextPlotAxes() needs to be called before BeginPlot()!"); - gp.NextPlotData.FitX = x; - gp.NextPlotData.FitY[0] = y; - gp.NextPlotData.FitY[1] = y2; - gp.NextPlotData.FitY[2] = y3; + ImGuiContext &G = *GImGui; + ImGuiWindow * Window = G.CurrentWindow; + if (Window->SkipItems) + return false; + const ImGuiID ID = Window->GetID(title); + bool just_created = gp.Subplots.GetByKey(ID) == NULL; + gp.CurrentSubplot = gp.Subplots.GetOrAddByKey(ID); + ImPlotSubplot& subplot = *gp.CurrentSubplot; + subplot.ID = ID; + subplot.Items.ID = ID - 1; + subplot.HasTitle = ImGui::FindRenderedTextEnd(title, NULL) != title; + // push ID + ImGui::PushID(ID); + + if (just_created) + subplot.Flags = flags; + else if (flags != subplot.PreviousFlags) + subplot.Flags = flags; + subplot.PreviousFlags = flags; + + // check for change in rows and cols + if (subplot.Rows != rows || subplot.Cols != cols) { + subplot.RowAlignmentData.resize(rows); + subplot.RowLinkData.resize(rows); + subplot.RowRatios.resize(rows); + for (int r = 0; r < rows; ++r) { + subplot.RowAlignmentData[r].Reset(); + subplot.RowLinkData[r] = ImPlotRange(0,1); + subplot.RowRatios[r] = 1.0f / rows; + } + subplot.ColAlignmentData.resize(cols); + subplot.ColLinkData.resize(cols); + subplot.ColRatios.resize(cols); + for (int c = 0; c < cols; ++c) { + subplot.ColAlignmentData[c].Reset(); + subplot.ColLinkData[c] = ImPlotRange(0,1); + subplot.ColRatios[c] = 1.0f / cols; + } + } + // check incoming size requests + float row_sum = 0, col_sum = 0; + if (row_sizes != NULL) { + row_sum = ImSum(row_sizes, rows); + for (int r = 0; r < rows; ++r) + subplot.RowRatios[r] = row_sizes[r] / row_sum; + } + if (col_sizes != NULL) { + col_sum = ImSum(col_sizes, cols); + for (int c = 0; c < cols; ++c) + subplot.ColRatios[c] = col_sizes[c] / col_sum; + } + subplot.Rows = rows; + subplot.Cols = cols; + + // calc plot frame sizes + ImVec2 title_size(0.0f, 0.0f); + if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoTitle)) + title_size = ImGui::CalcTextSize(title, NULL, true); + const float pad_top = title_size.x > 0.0f ? title_size.y + gp.Style.LabelPadding.y : 0; + const ImVec2 half_pad = gp.Style.PlotPadding/2; + const ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y); + subplot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); + subplot.GridRect.Min = subplot.FrameRect.Min + half_pad + ImVec2(0,pad_top); + subplot.GridRect.Max = subplot.FrameRect.Max - half_pad; + subplot.FrameHovered = subplot.FrameRect.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); + + // outside legend adjustments (TODO: make function) + const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); + if (share_items) + gp.CurrentItems = &subplot.Items; + if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { + ImPlotLegend& legend = subplot.Items.Legend; + const bool horz = ImHasFlag(legend.Flags, ImPlotLegendFlags_Horizontal); + const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !horz); + const bool west = ImHasFlag(legend.Location, ImPlotLocation_West) && !ImHasFlag(legend.Location, ImPlotLocation_East); + const bool east = ImHasFlag(legend.Location, ImPlotLocation_East) && !ImHasFlag(legend.Location, ImPlotLocation_West); + const bool north = ImHasFlag(legend.Location, ImPlotLocation_North) && !ImHasFlag(legend.Location, ImPlotLocation_South); + const bool south = ImHasFlag(legend.Location, ImPlotLocation_South) && !ImHasFlag(legend.Location, ImPlotLocation_North); + if ((west && !horz) || (west && horz && !north && !south)) + subplot.GridRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); + if ((east && !horz) || (east && horz && !north && !south)) + subplot.GridRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); + if ((north && horz) || (north && !horz && !west && !east)) + subplot.GridRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); + if ((south && horz) || (south && !horz && !west && !east)) + subplot.GridRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); + } + + // render single background frame + ImGui::RenderFrame(subplot.FrameRect.Min, subplot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, ImGui::GetStyle().FrameRounding); + // render title + if (title_size.x > 0.0f && !ImHasFlag(subplot.Flags, ImPlotFlags_NoTitle)) { + const ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); + AddTextCentered(ImGui::GetWindowDrawList(),ImVec2(subplot.GridRect.GetCenter().x, subplot.GridRect.Min.y - pad_top + half_pad.y),col,title); + } + + // render splitters + if (!ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoResize)) { + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + const ImU32 hov_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorHovered]); + const ImU32 act_col = ImGui::ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); + float xpos = subplot.GridRect.Min.x; + float ypos = subplot.GridRect.Min.y; + int separator = 1; + // bool pass = false; + for (int r = 0; r < subplot.Rows-1; ++r) { + ypos += subplot.RowRatios[r] * subplot.GridRect.GetHeight(); + const ImGuiID sep_id = subplot.ID + separator; + ImGui::KeepAliveID(sep_id); + const ImRect sep_bb = ImRect(subplot.GridRect.Min.x, ypos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.x, ypos+SUBPLOT_SPLITTER_HALF_THICKNESS); + bool sep_hov = false, sep_hld = false; + const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { + if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { + float p = (subplot.RowRatios[r] + subplot.RowRatios[r+1])/2; + subplot.RowRatios[r] = subplot.RowRatios[r+1] = p; + } + if (sep_clk) { + subplot.TempSizes[0] = subplot.RowRatios[r]; + subplot.TempSizes[1] = subplot.RowRatios[r+1]; + } + if (sep_hld) { + float dp = ImGui::GetMouseDragDelta(0).y / subplot.GridRect.GetHeight(); + if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { + subplot.RowRatios[r] = subplot.TempSizes[0] + dp; + subplot.RowRatios[r+1] = subplot.TempSizes[1] - dp; + } + } + DrawList.AddLine(ImVec2(IM_ROUND(subplot.GridRect.Min.x),IM_ROUND(ypos)), + ImVec2(IM_ROUND(subplot.GridRect.Max.x),IM_ROUND(ypos)), + sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + } + separator++; + } + for (int c = 0; c < subplot.Cols-1; ++c) { + xpos += subplot.ColRatios[c] * subplot.GridRect.GetWidth(); + const ImGuiID sep_id = subplot.ID + separator; + ImGui::KeepAliveID(sep_id); + const ImRect sep_bb = ImRect(xpos-SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Min.y, xpos+SUBPLOT_SPLITTER_HALF_THICKNESS, subplot.GridRect.Max.y); + bool sep_hov = false, sep_hld = false; + const bool sep_clk = ImGui::ButtonBehavior(sep_bb, sep_id, &sep_hov, &sep_hld, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + if ((sep_hov && G.HoveredIdTimer > SUBPLOT_SPLITTER_FEEDBACK_TIMER) || sep_hld) { + if (sep_clk && ImGui::IsMouseDoubleClicked(0)) { + float p = (subplot.ColRatios[c] + subplot.ColRatios[c+1])/2; + subplot.ColRatios[c] = subplot.ColRatios[c+1] = p; + } + if (sep_clk) { + subplot.TempSizes[0] = subplot.ColRatios[c]; + subplot.TempSizes[1] = subplot.ColRatios[c+1]; + } + if (sep_hld) { + float dp = ImGui::GetMouseDragDelta(0).x / subplot.GridRect.GetWidth(); + if (subplot.TempSizes[0] + dp > 0.1f && subplot.TempSizes[1] - dp > 0.1f) { + subplot.ColRatios[c] = subplot.TempSizes[0] + dp; + subplot.ColRatios[c+1] = subplot.TempSizes[1] - dp; + } + } + DrawList.AddLine(ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Min.y)), + ImVec2(IM_ROUND(xpos),IM_ROUND(subplot.GridRect.Max.y)), + sep_hld ? act_col : hov_col, SUBPLOT_BORDER_SIZE); + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + separator++; + } + } + + // set outgoing sizes + if (row_sizes != NULL) { + for (int r = 0; r < rows; ++r) + row_sizes[r] = subplot.RowRatios[r] * row_sum; + } + if (col_sizes != NULL) { + for (int c = 0; c < cols; ++c) + col_sizes[c] = subplot.ColRatios[c] * col_sum; + } + + // push styling + PushStyleColor(ImPlotCol_FrameBg, IM_COL32_BLACK_TRANS); + PushStyleVar(ImPlotStyleVar_PlotPadding, half_pad); + PushStyleVar(ImPlotStyleVar_PlotMinSize, ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize,0); + + // set initial cursor pos + Window->DC.CursorPos = subplot.GridRect.Min; + // begin alignments + for (int r = 0; r < subplot.Rows; ++r) + subplot.RowAlignmentData[r].Begin(); + for (int c = 0; c < subplot.Cols; ++c) + subplot.ColAlignmentData[c].Begin(); + // clear legend data + subplot.Items.Legend.Reset(); + // Setup first subplot + SubplotSetCell(0,0); + return true; } -void SetNextPlotTicksX(const double* values, int n_ticks, const char* const labels[], bool show_default) { +void EndSubplots() { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentSubplot != NULL, "Mismatched BeginSubplots()/EndSubplots()!"); ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksX() needs to be called before BeginPlot()!"); - gp.NextPlotData.ShowDefaultTicksX = show_default; - AddTicksCustom(values, labels, n_ticks, gp.XTicks, GetFormatX()); -} + ImPlotSubplot& subplot = *GImPlot->CurrentSubplot; + // set alignments + for (int r = 0; r < subplot.Rows; ++r) + subplot.RowAlignmentData[r].End(); + for (int c = 0; c < subplot.Cols; ++c) + subplot.ColAlignmentData[c].End(); + // pop styling + PopStyleColor(); + PopStyleVar(); + PopStyleVar(); + ImGui::PopStyleVar(); + // legend + subplot.Items.Legend.Hovered = false; + for (int i = 0; i < subplot.Items.GetItemCount(); ++i) + subplot.Items.GetItemByIndex(i)->LegendHovered = false; + // render legend + const bool share_items = ImHasFlag(subplot.Flags, ImPlotSubplotFlags_ShareItems); + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + if (share_items && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoLegend) && subplot.Items.GetLegendCount() > 0) { + const bool legend_horz = ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_Horizontal); + const ImVec2 legend_size = CalcLegendSize(subplot.Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz); + const ImVec2 legend_pos = GetLocationPos(subplot.FrameRect, legend_size, subplot.Items.Legend.Location, gp.Style.PlotPadding); + subplot.Items.Legend.Rect = ImRect(legend_pos, legend_pos + legend_size); + subplot.Items.Legend.Hovered = subplot.FrameHovered && subplot.Items.Legend.Rect.Contains(ImGui::GetIO().MousePos); + ImGui::PushClipRect(subplot.FrameRect.Min, subplot.FrameRect.Max, true); + ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); + ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder); + DrawList.AddRectFilled(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bg); + DrawList.AddRect(subplot.Items.Legend.Rect.Min, subplot.Items.Legend.Rect.Max, col_bd); + bool legend_contextable = ShowLegendEntries(subplot.Items, subplot.Items.Legend.Rect, subplot.Items.Legend.Hovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, !legend_horz, DrawList) + && !ImHasFlag(subplot.Items.Legend.Flags, ImPlotLegendFlags_NoMenus); + if (legend_contextable && !ImHasFlag(subplot.Flags, ImPlotSubplotFlags_NoMenus) && ImGui::GetIO().MouseReleased[gp.InputMap.Menu]) + ImGui::OpenPopup("##LegendContext"); + ImGui::PopClipRect(); + if (ImGui::BeginPopup("##LegendContext")) { + ImGui::Text("Legend"); ImGui::Separator(); + if (ShowLegendContextMenu(subplot.Items.Legend, !ImHasFlag(subplot.Flags, ImPlotFlags_NoLegend))) + ImFlipFlag(subplot.Flags, ImPlotFlags_NoLegend); + ImGui::EndPopup(); + } + } + else { + subplot.Items.Legend.Rect = ImRect(); + } + // remove items + if (gp.CurrentItems == &subplot.Items) + gp.CurrentItems = NULL; + // reset the plot items for the next frame (TODO: put this elswhere) + for (int i = 0; i < subplot.Items.GetItemCount(); ++i) { + subplot.Items.GetItemByIndex(i)->SeenThisFrame = false; + } + // pop id + ImGui::PopID(); + // set DC back correctly + GImGui->CurrentWindow->DC.CursorPos = subplot.FrameRect.Min; + ImGui::Dummy(subplot.FrameRect.GetSize()); + ResetCtxForNextSubplot(GImPlot); -void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* const labels[], bool show_default) { - IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); - static ImVector<double> buffer; - FillRange(buffer, n_ticks, x_min, x_max); - SetNextPlotTicksX(&buffer[0], n_ticks, labels, show_default); } -void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[], bool show_default, ImPlotYAxis y_axis) { +//----------------------------------------------------------------------------- +// [SECTION] Plot Utils +//----------------------------------------------------------------------------- + +void SetAxis(ImAxis 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 < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES"); - gp.NextPlotData.ShowDefaultTicksY[y_axis] = show_default; - AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis], GetFormatY(y_axis)); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetAxis() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(axis >= ImAxis_X1 && axis < ImAxis_COUNT, "Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[axis].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + SetupLock(); + if (axis < ImAxis_Y1) + gp.CurrentPlot->CurrentX = axis; + else + gp.CurrentPlot->CurrentY = axis; } -void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[], bool show_default, ImPlotYAxis y_axis) { - IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); - static ImVector<double> buffer; - FillRange(buffer, n_ticks, y_min, y_max); - SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis); +void SetAxes(ImAxis x_idx, ImAxis y_idx) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetAxes() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1, "X-Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT, "Y-Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[x_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot->Axes[y_idx].Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + SetupLock(); + gp.CurrentPlot->CurrentX = x_idx; + gp.CurrentPlot->CurrentY = y_idx; } -void SetNextPlotFormatX(const char* fmt){ +ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotFormatX() needs to be called before BeginPlot()!"); - gp.NextPlotData.HasFmtX = true; - ImStrncpy(gp.NextPlotData.FmtX, fmt, 16); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); + SetupLock(); + ImPlotPlot& plot = *gp.CurrentPlot; + ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; + ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; + return ImPlotPoint( x_axis.PixelsToPlot(x), y_axis.PixelsToPlot(y) ); } -void SetNextPlotFormatY(const char* fmt, ImPlotYAxis y_axis) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotFormatY() needs to be called before BeginPlot()!"); - IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES"); - gp.NextPlotData.HasFmtY[y_axis] = true; - ImStrncpy(gp.NextPlotData.FmtY[y_axis], fmt, 16); +ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_idx, ImAxis y_idx) { + return PixelsToPlot(pix.x, pix.y, x_idx, y_idx); } -void SetPlotYAxis(ImPlotYAxis y_axis) { +ImVec2 PlotToPixels(double x, double y, ImAxis x_idx, ImAxis y_idx) { 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 < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES"); - gp.CurrentPlot->CurrentYAxis = y_axis; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); + SetupLock(); + ImPlotPlot& plot = *gp.CurrentPlot; + ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; + ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; + return ImVec2( x_axis.PlotToPixels(x), y_axis.PlotToPixels(y) ); +} + +ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_idx, ImAxis y_idx) { + return PlotToPixels(plt.x, plt.y, x_idx, y_idx); } ImVec2 GetPlotPos() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); return gp.CurrentPlot->PlotRect.Min; } ImVec2 GetPlotSize() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); return gp.CurrentPlot->PlotRect.GetSize(); } -ImDrawList* GetPlotDrawList() { - return ImGui::GetWindowDrawList(); +ImPlotPoint GetPlotMousePos(ImAxis x_idx, ImAxis y_idx) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + return PixelsToPlot(ImGui::GetMousePos(), x_idx, y_idx); } -void PushPlotClipRect(float expand) { +ImPlotRect GetPlotLimits(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; - ImRect rect = gp.CurrentPlot->PlotRect; - rect.Expand(expand); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); - ImGui::PushClipRect(rect.Min, rect.Max, true); -} - -void PopPlotClipRect() { - ImGui::PopClipRect(); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(x_idx == IMPLOT_AUTO || (x_idx >= ImAxis_X1 && x_idx < ImAxis_Y1), "X-Axis index out of bounds!"); + IM_ASSERT_USER_ERROR(y_idx == IMPLOT_AUTO || (y_idx >= ImAxis_Y1 && y_idx < ImAxis_COUNT), "Y-Axis index out of bounds!"); + SetupLock(); + ImPlotPlot& plot = *gp.CurrentPlot; + ImPlotAxis& x_axis = x_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentX] : plot.Axes[x_idx]; + ImPlotAxis& y_axis = y_idx == IMPLOT_AUTO ? plot.Axes[plot.CurrentY] : plot.Axes[y_idx]; + ImPlotRect limits; + limits.X = x_axis.Range; + limits.Y = y_axis.Range; + return limits; } bool IsPlotHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); - return gp.CurrentPlot->PlotHovered; + SetupLock(); + return gp.CurrentPlot->Hovered; } -bool IsPlotXAxisHovered() { +bool IsAxisHovered(ImAxis axis) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); - return gp.CurrentPlot->XAxis.ExtHovered; + SetupLock(); + return gp.CurrentPlot->Axes[axis].Hovered; } -bool IsPlotYAxisHovered(ImPlotYAxis y_axis_in) { +bool IsSubplotsHovered() { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotYAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); - const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; - return gp.CurrentPlot->YAxis[y_axis].ExtHovered; -} - -ImPlotPoint GetPlotMousePos(ImPlotYAxis y_axis_in) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); - const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; - return gp.MousePos[y_axis]; -} - - -ImPlotLimits GetPlotLimits(ImPlotYAxis y_axis_in) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); - const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; - - ImPlotPlot& plot = *gp.CurrentPlot; - ImPlotLimits limits; - limits.X = plot.XAxis.Range; - limits.Y = plot.YAxis[y_axis].Range; - return limits; + IM_ASSERT_USER_ERROR(gp.CurrentSubplot != NULL, "IsSubplotsHovered() needs to be called between BeginSubplots() and EndSubplots()!"); + return gp.CurrentSubplot->FrameHovered; } bool IsPlotSelected() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotSelected() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); return gp.CurrentPlot->Selected; } -ImPlotLimits GetPlotSelection(ImPlotYAxis y_axis) { +ImPlotRect GetPlotSelection(ImAxis x_idx, ImAxis y_idx) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis >= -1 && y_axis < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); ImPlotPlot& plot = *gp.CurrentPlot; - y_axis = y_axis >= 0 ? y_axis : gp.CurrentPlot->CurrentYAxis; if (!plot.Selected) - return ImPlotLimits(0,0,0,0); - UpdateTransformCache(); - ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, y_axis); - ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, y_axis); - ImPlotLimits result; + return ImPlotRect(0,0,0,0); + ImPlotPoint p1 = PixelsToPlot(plot.SelectRect.Min + plot.PlotRect.Min, x_idx, y_idx); + ImPlotPoint p2 = PixelsToPlot(plot.SelectRect.Max + plot.PlotRect.Min, x_idx, y_idx); + ImPlotRect result; result.X.Min = ImMin(p1.x, p2.x); result.X.Max = ImMax(p1.x, p2.x); result.Y.Min = ImMin(p1.y, p2.y); @@ -2738,423 +3559,411 @@ ImPlotLimits GetPlotSelection(ImPlotYAxis y_axis) { return result; } -bool IsPlotQueried() { +void CancelPlotSelection() { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() needs to be called between BeginPlot() and EndPlot()!"); - return gp.CurrentPlot->Queried; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "CancelPlotSelection() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + ImPlotPlot& plot = *gp.CurrentPlot; + if (plot.Selected) + plot.Selected = plot.Selecting = false; } -ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis) { +void HideNextItem(bool hidden, ImPlotCond cond) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis >= -1 && y_axis < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!"); - ImPlotPlot& plot = *gp.CurrentPlot; - y_axis = y_axis >= 0 ? y_axis : gp.CurrentPlot->CurrentYAxis; - if (!plot.Queried) - return ImPlotLimits(0,0,0,0); - UpdateTransformCache(); - ImPlotPoint p1 = PixelsToPlot(plot.QueryRect.Min + plot.PlotRect.Min, y_axis); - ImPlotPoint p2 = PixelsToPlot(plot.QueryRect.Max + plot.PlotRect.Min, y_axis); - ImPlotLimits result; - result.X.Min = ImMin(p1.x, p2.x); - result.X.Max = ImMax(p1.x, p2.x); - result.Y.Min = ImMin(p1.y, p2.y); - result.Y.Max = ImMax(p1.y, p2.y); - return result; + gp.NextItemData.HasHidden = true; + gp.NextItemData.Hidden = hidden; + gp.NextItemData.HiddenCond = cond; } -void SetPlotQuery(const ImPlotLimits& query, ImPlotYAxis y_axis) { +//----------------------------------------------------------------------------- +// [SECTION] Plot Tools +//----------------------------------------------------------------------------- + +void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, bool round) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(y_axis >= -1 && y_axis < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!"); - ImPlotPlot& plot = *gp.CurrentPlot; - y_axis = y_axis >= 0 ? y_axis : gp.CurrentPlot->CurrentYAxis; - UpdateTransformCache(); - ImVec2 p1 = PlotToPixels(query.Min(),y_axis); - ImVec2 p2 = PlotToPixels(query.Max(),y_axis); - plot.Queried = true; - plot.Querying = false; - plot.QueryRect = ImRect(ImMin(p1,p2)-plot.PlotRect.Min, ImMax(p1,p2)-plot.PlotRect.Min); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + char x_buff[IMPLOT_LABEL_MAX_SIZE]; + char y_buff[IMPLOT_LABEL_MAX_SIZE]; + ImPlotAxis& x_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; + ImPlotAxis& y_axis = gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX]; + LabelAxisValue(x_axis, x, x_buff, sizeof(x_buff), round); + LabelAxisValue(y_axis, y, y_buff, sizeof(y_buff), round); + Annotation(x,y,col,offset,clamp,"%s, %s",x_buff,y_buff); } -void AnnotateEx(double x, double y, bool clamp, const ImVec4& col, const ImVec2& off, const char* fmt, va_list args) { +void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, va_list args) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotate() needs to be called between BeginPlot() and EndPlot()!"); - ImVec2 pos = PlotToPixels(x,y); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotation() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + ImVec2 pos = PlotToPixels(x,y,IMPLOT_AUTO,IMPLOT_AUTO); ImU32 bg = ImGui::GetColorU32(col); ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); - gp.Annotations.AppendV(pos, off, bg, fg, clamp, fmt, args); -} - -void AnnotateV(double x, double y, const ImVec2& offset, const char* fmt, va_list args) { - AnnotateEx(x,y,false,ImVec4(0,0,0,0),offset,fmt,args); + gp.Annotations.AppendV(pos, offset, bg, fg, clamp, fmt, args); } -void Annotate(double x, double y, const ImVec2& offset, const char* fmt, ...) { +void Annotation(double x, double y, const ImVec4& col, const ImVec2& offset, bool clamp, const char* fmt, ...) { va_list args; va_start(args, fmt); - AnnotateV(x,y,offset,fmt,args); + AnnotationV(x,y,col,offset,clamp,fmt,args); va_end(args); } -void AnnotateV(double x, double y, const ImVec2& offset, const ImVec4& col, const char* fmt, va_list args) { - AnnotateEx(x,y,false,col,offset,fmt,args); +void TagV(ImAxis axis, double v, const ImVec4& col, const char* fmt, va_list args) { + ImPlotContext& gp = *GImPlot; + SetupLock(); + ImU32 bg = ImGui::GetColorU32(col); + ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_AxisText) : CalcTextColor(col); + gp.Tags.AppendV(axis,v,bg,fg,fmt,args); } -void Annotate(double x, double y, const ImVec2& offset, const ImVec4& col, const char* fmt, ...) { +void Tag(ImAxis axis, double v, const ImVec4& col, const char* fmt, ...) { va_list args; va_start(args, fmt); - AnnotateV(x,y,offset,col,fmt,args); + TagV(axis,v,col,fmt,args); va_end(args); } -void AnnotateClampedV(double x, double y, const ImVec2& offset, const char* fmt, va_list args) { - AnnotateEx(x,y,true,ImVec4(0,0,0,0),offset,fmt,args); +void Tag(ImAxis axis, double v, const ImVec4& color, bool round) { + ImPlotContext& gp = *GImPlot; + SetupLock(); + char buff[IMPLOT_LABEL_MAX_SIZE]; + ImPlotAxis& ax = gp.CurrentPlot->Axes[axis]; + LabelAxisValue(ax, v, buff, sizeof(buff), round); + Tag(axis,v,color,"%s",buff); } -void AnnotateClamped(double x, double y, const ImVec2& offset, const char* fmt, ...) { +IMPLOT_API void TagX(double x, const ImVec4& color, bool round) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); + Tag(GImPlot->CurrentPlot->CurrentX, x, color, round); +} + +IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); - AnnotateClampedV(x,y,offset,fmt,args); + TagV(GImPlot->CurrentPlot->CurrentX,x,color,fmt,args); va_end(args); } -void AnnotateClampedV(double x, double y, const ImVec2& offset, const ImVec4& col, const char* fmt, va_list args) { - AnnotateEx(x,y,true,col,offset,fmt,args); +IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagX() needs to be called between BeginPlot() and EndPlot()!"); + TagV(GImPlot->CurrentPlot->CurrentX, x, color, fmt, args); +} + +IMPLOT_API void TagY(double y, const ImVec4& color, bool round) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); + Tag(GImPlot->CurrentPlot->CurrentY, y, color, round); } -void AnnotateClamped(double x, double y, const ImVec2& offset, const ImVec4& col, const char* fmt, ...) { +IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); va_list args; va_start(args, fmt); - AnnotateClampedV(x,y,offset,col,fmt,args); + TagV(GImPlot->CurrentPlot->CurrentY,y,color,fmt,args); va_end(args); } -bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col, float thickness) { +IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "TagY() needs to be called between BeginPlot() and EndPlot()!"); + TagV(GImPlot->CurrentPlot->CurrentY, y, color, fmt, args); +} + +static const float DRAG_GRAB_HALF_SIZE = 4.0f; + +bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, ImPlotDragToolFlags flags) { + ImGui::PushID("#IMPLOT_DRAG_POINT"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + + if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { + FitPoint(ImPlotPoint(*x,*y)); + } + + const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); + const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); + const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); + const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, radius); + const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; + const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); + + ImVec2 pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); + const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); + ImRect rect(pos.x-grab_half_size,pos.y-grab_half_size,pos.x+grab_half_size,pos.y+grab_half_size); + bool hovered = false, held = false; + + if (input) + ImGui::ButtonBehavior(rect,id,&hovered,&held); + + bool dragging = false; + if (held && ImGui::IsMouseDragging(0)) { + *x = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; + *y = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; + dragging = true; + } + + PushPlotClipRect(); + ImDrawList& DrawList = *GetPlotDrawList(); + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + if (dragging && no_delay) + pos = PlotToPixels(*x,*y,IMPLOT_AUTO,IMPLOT_AUTO); + DrawList.AddCircleFilled(pos, radius, col32); + PopPlotClipRect(); + + ImGui::PopID(); + return dragging; +} + +bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { + ImGui::PushID("#IMPLOT_DRAG_LINE_X"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(5.0f, thickness); + SetupLock(); + + if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { + FitPointX(*value); + } + + const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); + const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); + const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); + const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float yt = gp.CurrentPlot->PlotRect.Min.y; float yb = gp.CurrentPlot->PlotRect.Max.y; - float x = IM_ROUND(PlotToPixels(*value,0).x); - const bool outside = x < (gp.CurrentPlot->PlotRect.Min.x - grab_size / 2) || x > (gp.CurrentPlot->PlotRect.Max.x + grab_size / 2); - if (outside) - return false; + float x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); + const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); + ImRect rect(x-grab_half_size,yt,x+grab_half_size,yb); + bool hovered = false, held = false; + + if (input) + ImGui::ButtonBehavior(rect,id,&hovered,&held); + + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + float len = gp.Style.MajorTickLen.x; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - ImDrawList& DrawList = *GetPlotDrawList(); + + bool dragging = false; + if (held && ImGui::IsMouseDragging(0)) { + *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; + dragging = true; + } + PushPlotClipRect(); - DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); + ImDrawList& DrawList = *GetPlotDrawList(); + if (dragging && no_delay) + x = IM_ROUND(PlotToPixels(*value,0,IMPLOT_AUTO,IMPLOT_AUTO).x); + DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yb), col32, thickness); DrawList.AddLine(ImVec2(x,yt), ImVec2(x,yt+len), col32, 3*thickness); DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); PopPlotClipRect(); - if (gp.CurrentPlot->Selecting || gp.CurrentPlot->Querying) - return false; - ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); - ImVec2 new_cursor_pos = ImVec2(x - grab_size / 2.0f, yt); - ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; - ImGui::InvisibleButton(id, ImVec2(grab_size, yb-yt)); - ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - gp.CurrentPlot->PlotHovered = false; - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - if (show_label) { - char buff[32]; - LabelAxisValue(gp.CurrentPlot->XAxis, gp.XTicks, *value, buff, 32); - gp.Annotations.Append(ImVec2(x,yb),ImVec2(0,0),col32,CalcTextColor(color),true,"%s = %s", id, buff); - } - } - bool dragging = false; - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { - *value = ImPlot::GetPlotMousePos().x; - *value = ImClamp(*value, gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max); - dragging = true; - } + + ImGui::PopID(); return dragging; } -bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col, float thickness) { +bool DragLineY(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { + ImGui::PushID("#IMPLOT_DRAG_LINE_Y"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(5.0f, thickness); + SetupLock(); + + if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { + FitPointY(*value); + } + + const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); + const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); + const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); + const float grab_half_size = ImMax(DRAG_GRAB_HALF_SIZE, thickness/2); float xl = gp.CurrentPlot->PlotRect.Min.x; float xr = gp.CurrentPlot->PlotRect.Max.x; - float y = IM_ROUND(PlotToPixels(0, *value).y); - const bool outside = y < (gp.CurrentPlot->PlotRect.Min.y - grab_size / 2) || y > (gp.CurrentPlot->PlotRect.Max.y + grab_size / 2); - if (outside) - return false; + float y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); + + const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); + ImRect rect(xl,y-grab_half_size,xr,y+grab_half_size); + bool hovered = false, held = false; + + if (input) + ImGui::ButtonBehavior(rect,id,&hovered,&held); + + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + float len = gp.Style.MajorTickLen.y; ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - ImDrawList& DrawList = *GetPlotDrawList(); - PushPlotClipRect(); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); - DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); - DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); - PopPlotClipRect(); - if (gp.CurrentPlot->Selecting || gp.CurrentPlot->Querying) - return false; - ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); - ImVec2 new_cursor_pos = ImVec2(xl, y - grab_size / 2.0f); - ImGui::SetItemAllowOverlap(); - ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; - ImGui::InvisibleButton(id, ImVec2(xr - xl, grab_size)); - ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; - int yax = GetCurrentYAxis(); - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - gp.CurrentPlot->PlotHovered = false; - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); - if (show_label) { - char buff[32]; - LabelAxisValue(gp.CurrentPlot->YAxis[yax], gp.YTicks[yax], *value, buff, 32); - gp.Annotations.Append(ImVec2(yax == 0 ? xl : xr,y), ImVec2(0,0), col32, CalcTextColor(color), true, "%s = %s", id, buff); - } - } + bool dragging = false; - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { - *value = ImPlot::GetPlotMousePos().y; - *value = ImClamp(*value, gp.CurrentPlot->YAxis[yax].Range.Min, gp.CurrentPlot->YAxis[yax].Range.Max); + if (held && ImGui::IsMouseDragging(0)) { + *value = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; dragging = true; } - return dragging; -} -bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVec4& col, float radius) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragPoint() needs to be called between BeginPlot() and EndPlot()!"); - const float grab_size = ImMax(5.0f, 2*radius); - const bool outside = !GetPlotLimits().Contains(*x,*y); - if (outside) - return false; - const ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; - const ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); - ImDrawList& DrawList = *GetPlotDrawList(); - const ImVec2 pos = PlotToPixels(*x,*y); - int yax = GetCurrentYAxis(); - ImVec2 old_cursor_pos = ImGui::GetCursorScreenPos(); - ImVec2 new_cursor_pos = ImVec2(pos - ImVec2(grab_size,grab_size)*0.5f); - ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; - ImGui::InvisibleButton(id, ImVec2(grab_size, grab_size)); - ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; PushPlotClipRect(); - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - DrawList.AddCircleFilled(pos, 1.5f*radius, (col32)); - gp.CurrentPlot->PlotHovered = false; - if (show_label) { - ImVec2 label_pos = pos + ImVec2(16 * GImGui->Style.MouseCursorScale, 8 * GImGui->Style.MouseCursorScale); - char buff1[32]; - char buff2[32]; - LabelAxisValue(gp.CurrentPlot->XAxis, gp.XTicks, *x, buff1, 32); - LabelAxisValue(gp.CurrentPlot->YAxis[yax], gp.YTicks[yax], *y, buff2, 32); - gp.Annotations.Append(label_pos, ImVec2(0.0001f,0.00001f), col32, CalcTextColor(color), true, "%s = %s,%s", id, buff1, buff2); - } - } - else { - DrawList.AddCircleFilled(pos, radius, col32); - } + ImDrawList& DrawList = *GetPlotDrawList(); + if (dragging && no_delay) + y = IM_ROUND(PlotToPixels(0, *value,IMPLOT_AUTO,IMPLOT_AUTO).y); + DrawList.AddLine(ImVec2(xl,y), ImVec2(xr,y), col32, thickness); + DrawList.AddLine(ImVec2(xl,y), ImVec2(xl+len,y), col32, 3*thickness); + DrawList.AddLine(ImVec2(xr,y), ImVec2(xr-len,y), col32, 3*thickness); PopPlotClipRect(); - - bool dragging = false; - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { - *x = ImPlot::GetPlotMousePos().x; - *y = ImPlot::GetPlotMousePos().y; - *x = ImClamp(*x, gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max); - *y = ImClamp(*y, gp.CurrentPlot->YAxis[yax].Range.Min, gp.CurrentPlot->YAxis[yax].Range.Max); - dragging = true; - } + ImGui::PopID(); return dragging; } -//----------------------------------------------------------------------------- - -#define IMPLOT_ID_PLT 10030910 -#define IMPLOT_ID_LEG 10030911 -#define IMPLOT_ID_XAX 10030912 -#define IMPLOT_ID_YAX 10030913 - -bool BeginDragDropTargetEx(int id, const ImRect& rect) { - ImGuiContext& G = *GImGui; - const ImGuiID ID = G.CurrentWindow->GetID(id); - if (ImGui::ItemAdd(rect, ID, &rect) && - ImGui::BeginDragDropTarget()) - return true; - return false; -} - -bool BeginDragDropTarget() { - return BeginDragDropTargetEx(IMPLOT_ID_PLT, GImPlot->CurrentPlot->PlotRect); -} - -bool BeginDragDropTargetX() { - return BeginDragDropTargetEx(IMPLOT_ID_XAX, GImPlot->CurrentPlot->XAxis.HoverRect); -} +bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags) { + ImGui::PushID("#IMPLOT_DRAG_RECT"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "DragRect() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); -bool BeginDragDropTargetY(ImPlotYAxis axis) { - return BeginDragDropTargetEx(IMPLOT_ID_YAX + axis, GImPlot->CurrentPlot->YAxis[axis].HoverRect); -} - -bool BeginDragDropTargetLegend() { - return !ImHasFlag(GImPlot->CurrentPlot->Flags,ImPlotFlags_NoLegend) && - BeginDragDropTargetEx(IMPLOT_ID_LEG, GImPlot->CurrentPlot->LegendRect); -} - -void EndDragDropTarget() { - ImGui::EndDragDropTarget(); -} - -bool BeginDragDropSourceEx(ImGuiID source_id, bool is_hovered, ImGuiDragDropFlags flags, ImGuiKeyModFlags key_mods) { - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; - - if (g.IO.MouseDown[mouse_button] == false) { - if (g.ActiveId == source_id) - ImGui::ClearActiveID(); - return false; + if (!ImHasFlag(flags,ImPlotDragToolFlags_NoFit) && FitThisFrame()) { + FitPoint(ImPlotPoint(*x_min,*y_min)); + FitPoint(ImPlotPoint(*x_max,*y_max)); } - if (is_hovered && g.IO.MouseClicked[mouse_button] && g.IO.KeyMods == key_mods) { - ImGui::SetActiveID(source_id, window); - ImGui::FocusWindow(window); - } + const bool input = !ImHasFlag(flags, ImPlotDragToolFlags_NoInputs); + const bool show_curs = !ImHasFlag(flags, ImPlotDragToolFlags_NoCursors); + const bool no_delay = !ImHasFlag(flags, ImPlotDragToolFlags_Delayed); + bool h[] = {true,false,true,false}; + double* x[] = {x_min,x_max,x_max,x_min}; + double* y[] = {y_min,y_min,y_max,y_max}; + ImVec2 p[4]; + for (int i = 0; i < 4; ++i) + p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); + ImRect rect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); + ImRect rect_grab = rect; rect_grab.Expand(DRAG_GRAB_HALF_SIZE); - if (g.ActiveId != source_id) { - return false; + ImGuiMouseCursor cur[4]; + if (show_curs) { + cur[0] = (rect.Min.x == p[0].x && rect.Min.y == p[0].y) || (rect.Max.x == p[0].x && rect.Max.y == p[0].y) ? ImGuiMouseCursor_ResizeNWSE : ImGuiMouseCursor_ResizeNESW; + cur[1] = cur[0] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; + cur[2] = cur[1] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; + cur[3] = cur[2] == ImGuiMouseCursor_ResizeNWSE ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; } - g.ActiveIdAllowOverlap = is_hovered; - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + ImVec4 color = IsColorAuto(col) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : col; + ImU32 col32 = ImGui::ColorConvertFloat4ToU32(color); + color.w *= 0.25f; + ImU32 col32_a = ImGui::ColorConvertFloat4ToU32(color); + const ImGuiID id = ImGui::GetCurrentWindow()->GetID(n_id); - if (ImGui::IsMouseDragging(mouse_button)) { + bool dragging = false; + bool hovered = false, held = false; + ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); - if (!g.DragDropActive) { - ImGui::ClearDragDrop(); - ImGuiPayload& payload = g.DragDropPayload; - payload.SourceId = source_id; - payload.SourceParentId = 0; - g.DragDropActive = true; - g.DragDropSourceFlags = 0; - g.DragDropMouseButton = mouse_button; - } - g.DragDropSourceFrameCount = g.FrameCount; - g.DragDropWithinSource = true; + if (input) + ImGui::ButtonBehavior(b_rect,id,&hovered,&held); - if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - ImGui::BeginTooltip(); - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { - ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; - tooltip_window->HiddenFramesCanSkipItems = 1; - } + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + if (held && ImGui::IsMouseDragging(0)) { + for (int i = 0; i < 4; ++i) { + ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); + *y[i] = pp.y; + *x[i] = pp.x; } - return true; - } - return false; -} - -bool BeginDragDropSource(ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { - if (ImGui::GetIO().KeyMods == key_mods) { - GImPlot->CurrentPlot->XAxis.Dragging = false; - for (int i = 0; i < IMPLOT_Y_AXES; ++i) - GImPlot->CurrentPlot->YAxis[i].Dragging = false; + dragging = true; } - const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_PLT); - ImRect rect = GImPlot->CurrentPlot->PlotRect; - return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->PlotHovered, flags, key_mods); -} -bool BeginDragDropSourceX(ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { - if (ImGui::GetIO().KeyMods == key_mods) - GImPlot->CurrentPlot->XAxis.Dragging = false; - const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_XAX); - ImRect rect = GImPlot->CurrentPlot->XAxis.HoverRect; - return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->XAxis.ExtHovered, flags, key_mods); -} - -bool BeginDragDropSourceY(ImPlotYAxis axis, ImGuiKeyModFlags key_mods, ImGuiDragDropFlags flags) { - if (ImGui::GetIO().KeyMods == key_mods) - GImPlot->CurrentPlot->YAxis[axis].Dragging = false; - const ImGuiID ID = GImGui->CurrentWindow->GetID(IMPLOT_ID_YAX + axis); - ImRect rect = GImPlot->CurrentPlot->YAxis[axis].HoverRect; - return ImGui::ItemAdd(rect, ID, &rect) && BeginDragDropSourceEx(ID, GImPlot->CurrentPlot->YAxis[axis].ExtHovered, flags, key_mods); -} + for (int i = 0; i < 4; ++i) { + // points + b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); + ImGuiID p_id = id + i + 1; + ImGui::KeepAliveID(p_id); + if (input) + ImGui::ButtonBehavior(b_rect,p_id,&hovered,&held); + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(cur[i]); -bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "BeginDragDropSourceItem() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID source_id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(source_id); - bool is_hovered = item && item->LegendHovered; - return BeginDragDropSourceEx(source_id, is_hovered, flags, ImGuiKeyModFlags_None); -} + if (held && ImGui::IsMouseDragging(0)) { + *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; + *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; + dragging = true; + } -void EndDragDropSource() { - ImGui::EndDragDropSource(); -} + // edges + ImVec2 e_min = ImMin(p[i],p[(i+1)%4]); + ImVec2 e_max = ImMax(p[i],p[(i+1)%4]); + b_rect = h[i] ? ImRect(e_min.x + DRAG_GRAB_HALF_SIZE, e_min.y - DRAG_GRAB_HALF_SIZE, e_max.x - DRAG_GRAB_HALF_SIZE, e_max.y + DRAG_GRAB_HALF_SIZE) + : ImRect(e_min.x - DRAG_GRAB_HALF_SIZE, e_min.y + DRAG_GRAB_HALF_SIZE, e_max.x + DRAG_GRAB_HALF_SIZE, e_max.y - DRAG_GRAB_HALF_SIZE); + ImGuiID e_id = id + i + 5; + ImGui::KeepAliveID(e_id); + if (input) + ImGui::ButtonBehavior(b_rect,e_id,&hovered,&held); + if ((hovered || held) && show_curs) + h[i] ? ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS) : ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + if (held && ImGui::IsMouseDragging(0)) { + if (h[i]) + *y[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).y; + else + *x[i] = ImPlot::GetPlotMousePos(IMPLOT_AUTO,IMPLOT_AUTO).x; + dragging = true; + } + if (hovered && ImGui::IsMouseDoubleClicked(0)) + { + ImPlotRect b = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); + if (h[i]) + *y[i] = ((y[i] == y_min && *y_min < *y_max) || (y[i] == y_max && *y_max < *y_min)) ? b.Y.Min : b.Y.Max; + else + *x[i] = ((x[i] == x_min && *x_min < *x_max) || (x[i] == x_max && *x_max < *x_min)) ? b.X.Min : b.X.Max; + dragging = true; + } + } -void ItemIcon(const ImVec4& col) { - ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); -} -void ItemIcon(ImU32 col) { - const float txt_size = ImGui::GetTextLineHeight(); - ImVec2 size(txt_size-4,txt_size); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImVec2 pos = window->DC.CursorPos; - ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); - ImGui::Dummy(size); + PushPlotClipRect(); + ImDrawList& DrawList = *GetPlotDrawList(); + if (dragging && no_delay) { + for (int i = 0; i < 4; ++i) + p[i] = PlotToPixels(*x[i],*y[i],IMPLOT_AUTO,IMPLOT_AUTO); + pc = PlotToPixels((*x_min+*x_max)/2,(*y_min+*y_max)/2,IMPLOT_AUTO,IMPLOT_AUTO); + rect = ImRect(ImMin(p[0],p[2]),ImMax(p[0],p[2])); + } + DrawList.AddRectFilled(rect.Min, rect.Max, col32_a); + DrawList.AddRect(rect.Min, rect.Max, col32); + if (input && (dragging || rect_grab.Contains(ImGui::GetMousePos()))) { + DrawList.AddCircleFilled(pc,DRAG_GRAB_HALF_SIZE,col32); + for (int i = 0; i < 4; ++i) + DrawList.AddCircleFilled(p[i],DRAG_GRAB_HALF_SIZE,col32); + } + PopPlotClipRect(); + ImGui::PopID(); + return dragging; } -void ColormapIcon(ImPlotColormap cmap) { - ImPlotContext& gp = *GImPlot; - const float txt_size = ImGui::GetTextLineHeight(); - ImVec2 size(txt_size-4,txt_size); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImVec2 pos = window->DC.CursorPos; - ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); - ImGui::Dummy(size); +bool DragRect(int id, ImPlotRect* bounds, const ImVec4& col, ImPlotDragToolFlags flags) { + return DragRect(id, &bounds->X.Min, &bounds->Y.Min,&bounds->X.Max, &bounds->Y.Max, col, flags); } //----------------------------------------------------------------------------- - -void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetLegendLocation() needs to be called between BeginPlot() and EndPlot()!"); - gp.CurrentPlot->LegendLocation = location; - gp.CurrentPlot->LegendOrientation = orientation; - if (gp.CurrentPlot->LegendOutside != outside) - gp.CurrentPlot->LegendFlipSideNextFrame = true; -} - -void SetMousePosLocation(ImPlotLocation location) { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetMousePosLocation() needs to be called between BeginPlot() and EndPlot()!"); - gp.CurrentPlot->MousePosLocation = location; -} +// [SECTION] Legend Utils and Tools +//----------------------------------------------------------------------------- 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); + IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "IsPlotItemHighlight() needs to be called within an itemized context!"); + SetupLock(); + ImGuiID id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); + ImPlotItem* item = gp.CurrentItems->GetItem(id); return item && item->LegendHovered; } bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "BeginLegendPopup() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "BeginLegendPopup() needs to be called within an itemized context!"); + SetupLock(); ImGuiWindow* window = GImGui->CurrentWindow; if (window->SkipItems) return false; - ImGuiID id = ImGui::GetID(label_id); + ImGuiID id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); if (ImGui::IsMouseReleased(mouse_button)) { - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); + ImPlotItem* item = gp.CurrentItems->GetItem(id); if (item && item->LegendHovered) ImGui::OpenPopupEx(id); } @@ -3162,10 +3971,11 @@ bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button) { } void EndLegendPopup() { + SetupLock(); ImGui::EndPopup(); } -void ShowAltLegend(const char* title_id, ImPlotOrientation orientation, const ImVec2 size, bool interactable) { +void ShowAltLegend(const char* title_id, bool vertical, const ImVec2 size, bool interactable) { ImPlotContext& gp = *GImPlot; ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; @@ -3176,7 +3986,7 @@ void ShowAltLegend(const char* title_id, ImPlotOrientation orientation, const Im ImVec2 legend_size; ImVec2 default_size = gp.Style.LegendPadding * 2; if (plot != NULL) { - legend_size = CalcLegendSize(*plot, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, orientation); + legend_size = CalcLegendSize(plot->Items, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical); default_size = legend_size + gp.Style.LegendPadding * 2; } ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y); @@ -3196,16 +4006,116 @@ void ShowAltLegend(const char* title_id, ImPlotOrientation orientation, const Im DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg); DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd); // render entries - ShowLegendEntries(*plot, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, orientation, DrawList); + ShowLegendEntries(plot->Items, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, vertical, DrawList); } DrawList.PopClipRect(); } //----------------------------------------------------------------------------- -// STYLING +// [SECTION] Drag and Drop Utils +//----------------------------------------------------------------------------- + +bool BeginDragDropTargetPlot() { + SetupLock(); + ImRect rect = GImPlot->CurrentPlot->PlotRect; + return ImGui::BeginDragDropTargetCustom(rect, GImPlot->CurrentPlot->ID); +} + +bool BeginDragDropTargetAxis(ImAxis axis) { + SetupLock(); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& ax = plot.Axes[axis]; + ImRect rect = ax.HoverRect; + rect.Expand(-3.5f); + return ImGui::BeginDragDropTargetCustom(rect, ax.ID); +} + +bool BeginDragDropTargetLegend() { + SetupLock(); + ImPlotItemGroup& items = *GImPlot->CurrentItems; + ImRect rect = items.Legend.Rect; + return ImGui::BeginDragDropTargetCustom(rect, items.ID); +} + +void EndDragDropTarget() { + SetupLock(); + ImGui::EndDragDropTarget(); +} + +bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags) { + SetupLock(); + ImPlotPlot* plot = GImPlot->CurrentPlot; + if (GImGui->IO.KeyMods == GImPlot->InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == plot->ID) + return ImGui::ItemAdd(plot->PlotRect, plot->ID) && ImGui::BeginDragDropSource(flags); + return false; +} + +bool BeginDragDropSourceAxis(ImAxis idx, ImGuiDragDropFlags flags) { + SetupLock(); + ImPlotAxis& axis = GImPlot->CurrentPlot->Axes[idx]; + if (GImGui->IO.KeyMods == GImPlot->InputMap.OverrideMod || GImGui->DragDropPayload.SourceId == axis.ID) + return ImGui::ItemAdd(axis.HoverRect, axis.ID) && ImGui::BeginDragDropSource(flags); + return false; +} + +bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags) { + SetupLock(); + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "BeginDragDropSourceItem() needs to be called within an itemized context!"); + ImGuiID item_id = ImGui::GetIDWithSeed(label_id, NULL, gp.CurrentItems->ID); + ImPlotItem* item = gp.CurrentItems->GetItem(item_id); + if (item != NULL) { + return ImGui::ItemAdd(item->LegendHoverRect, item->ID) && ImGui::BeginDragDropSource(flags); + } + return false; +} + +void EndDragDropSource() { + SetupLock(); + ImGui::EndDragDropSource(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Aligned Plots +//----------------------------------------------------------------------------- + +bool BeginAlignedPlots(const char* group_id, bool vertical) { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentAlignmentH == NULL && GImPlot->CurrentAlignmentV == NULL, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); + ImPlotContext& gp = *GImPlot; + ImGuiContext &G = *GImGui; + ImGuiWindow * Window = G.CurrentWindow; + if (Window->SkipItems) + return false; + const ImGuiID ID = Window->GetID(group_id); + ImPlotAlignmentData* alignment = gp.AlignmentData.GetOrAddByKey(ID); + if (vertical) + gp.CurrentAlignmentV = alignment; + else + gp.CurrentAlignmentH = alignment; + if (alignment->Vertical != vertical) + alignment->Reset(); + alignment->Vertical = vertical; + alignment->Begin(); + return true; +} + +void EndAlignedPlots() { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + IM_ASSERT_USER_ERROR(GImPlot->CurrentAlignmentH != NULL || GImPlot->CurrentAlignmentV != NULL, "Mismatched BeginAlignedPlots()/EndAlignedPlots()!"); + ImPlotContext& gp = *GImPlot; + ImPlotAlignmentData* alignment = gp.CurrentAlignmentH != NULL ? gp.CurrentAlignmentH : (gp.CurrentAlignmentV != NULL ? gp.CurrentAlignmentV : NULL); + if (alignment) + alignment->End(); + ResetCtxForNextAlignedPlots(GImPlot); +} + +//----------------------------------------------------------------------------- +// [SECTION] Plot and Item Styling //----------------------------------------------------------------------------- ImPlotStyle& GetStyle() { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; return gp.Style; } @@ -3307,7 +4217,7 @@ void PopStyleVar(int count) { } //------------------------------------------------------------------------------ -// COLORMAPS +// [Section] Colormaps //------------------------------------------------------------------------------ ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { @@ -3370,10 +4280,10 @@ void PopColormap(int count) { ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); - int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); + IM_ASSERT_USER_ERROR(gp.CurrentItems != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); + int idx = gp.CurrentItems->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); - gp.CurrentPlot->ColormapIdx++; + gp.CurrentItems->ColormapIdx++; return col; } @@ -3385,7 +4295,7 @@ int GetColormapSize(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); - return gp.ColormapData.GetKeyCount(gp.Style.Colormap); + return gp.ColormapData.GetKeyCount(cmap); } ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { @@ -3468,10 +4378,10 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const ImPlotRange range(scale_min,scale_max); gp.CTicks.Reset(); - AddTicksDefault(range, frame_size.y, ImPlotOrientation_Vertical, gp.CTicks, fmt); + AddTicksDefault(range, frame_size.y, true, gp.CTicks, DefaultFormatter, (void*)fmt); const float txt_off = gp.Style.LabelPadding.x; - const float pad_right = txt_off + gp.CTicks.MaxWidth + (label_size.x > 0 ? txt_off + label_size.y : 0); + const float pad_right = txt_off + gp.CTicks.MaxSize.x + (label_size.x > 0 ? txt_off + label_size.y : 0); float bar_w = 20; if (frame_size.x == 0) @@ -3493,7 +4403,7 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, true, !gp.ColormapData.IsQual(cmap)); - const ImU32 col_tick = GetStyleColorU32(ImPlotCol_YAxis); + const ImU32 col_tick = GetStyleColorU32(ImPlotCol_AxisText); const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); for (int i = 0; i < gp.CTicks.Size; ++i) { const float ypos = ImRemap((float)gp.CTicks.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); @@ -3504,7 +4414,7 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -gp.CTicks.Ticks[i].LabelSize.y * 0.5f), col_text, gp.CTicks.GetText(i)); } if (label_size.x > 0) { - ImVec2 label_pos(bb_grad.Max.x - 1 + 2*txt_off + gp.CTicks.MaxWidth, bb_grad.GetCenter().y + label_size.x*0.5f ); + ImVec2 label_pos(bb_grad.Max.x - 1 + 2*txt_off + gp.CTicks.MaxSize.x, bb_grad.GetCenter().y + label_size.x*0.5f ); const char* label_end = ImGui::FindRenderedTextEnd(label); AddTextVertical(&DrawList,label_pos,col_text,label,label_end); } @@ -3575,11 +4485,95 @@ bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cm return pressed; } +//----------------------------------------------------------------------------- +// [Section] Miscellaneous +//----------------------------------------------------------------------------- + +ImPlotInputMap& GetInputMap() { + IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); + ImPlotContext& gp = *GImPlot; + return gp.InputMap; +} + +void MapInputDefault(ImPlotInputMap* dst) { + ImPlotInputMap& map = dst ? *dst : GetInputMap(); + map.Pan = ImGuiMouseButton_Left; + map.PanMod = ImGuiKeyModFlags_None; + map.Fit = ImGuiMouseButton_Left; + map.Menu = ImGuiMouseButton_Right; + map.Select = ImGuiMouseButton_Right; + map.SelectMod = ImGuiKeyModFlags_None; + map.SelectCancel = ImGuiMouseButton_Left; + map.SelectHorzMod = ImGuiKeyModFlags_Alt; + map.SelectVertMod = ImGuiKeyModFlags_Shift; + map.OverrideMod = ImGuiKeyModFlags_Ctrl; + map.ZoomMod = ImGuiKeyModFlags_None; + map.ZoomRate = 0.1f; +} + +void MapInputReverse(ImPlotInputMap* dst) { + ImPlotInputMap& map = dst ? *dst : GetInputMap(); + map.Pan = ImGuiMouseButton_Right; + map.PanMod = ImGuiKeyModFlags_None; + map.Fit = ImGuiMouseButton_Left; + map.Menu = ImGuiMouseButton_Right; + map.Select = ImGuiMouseButton_Left; + map.SelectMod = ImGuiKeyModFlags_None; + map.SelectCancel = ImGuiMouseButton_Right; + map.SelectHorzMod = ImGuiKeyModFlags_Alt; + map.SelectVertMod = ImGuiKeyModFlags_Shift; + map.OverrideMod = ImGuiKeyModFlags_Ctrl; + map.ZoomMod = ImGuiKeyModFlags_None; + map.ZoomRate = 0.1f; +} //----------------------------------------------------------------------------- -// Style Editor etc. +// [Section] Miscellaneous //----------------------------------------------------------------------------- +void ItemIcon(const ImVec4& col) { + ItemIcon(ImGui::ColorConvertFloat4ToU32(col)); +} + +void ItemIcon(ImU32 col) { + const float txt_size = ImGui::GetTextLineHeight(); + ImVec2 size(txt_size-4,txt_size); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImVec2 pos = window->DC.CursorPos; + ImGui::GetWindowDrawList()->AddRectFilled(pos + ImVec2(0,2), pos + size - ImVec2(0,2), col); + ImGui::Dummy(size); +} + +void ColormapIcon(ImPlotColormap cmap) { + ImPlotContext& gp = *GImPlot; + const float txt_size = ImGui::GetTextLineHeight(); + ImVec2 size(txt_size-4,txt_size); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImVec2 pos = window->DC.CursorPos; + ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); + ImGui::Dummy(size); +} + +ImDrawList* GetPlotDrawList() { + return ImGui::GetWindowDrawList(); +} + +void PushPlotClipRect(float expand) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); + ImRect rect = gp.CurrentPlot->PlotRect; + rect.Expand(expand); + ImGui::PushClipRect(rect.Min, rect.Max, true); +} + +void PopPlotClipRect() { + SetupLock(); + ImGui::PopClipRect(); +} + static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { @@ -3625,6 +4619,21 @@ bool ShowColormapSelector(const char* label) { return set; } +bool ShowInputMapSelector(const char* label) { + static int map_idx = -1; + if (ImGui::Combo(label, &map_idx, "Default\0Reversed\0")) + { + switch (map_idx) + { + case 0: MapInputDefault(); break; + case 1: MapInputReverse(); break; + } + return true; + } + return false; +} + + void ShowStyleEditor(ImPlotStyle* ref) { ImPlotContext& gp = *GImPlot; ImPlotStyle& style = GetStyle(); @@ -3899,21 +4908,35 @@ void ShowUserGuide() { ImGui::BulletText("Click legend label icons to show/hide plot items."); } -void ShowAxisMetrics(ImPlotAxis* axis, bool show_axis_rects) { - ImGui::Bullet(); ImGui::Text("Flags: %d", axis->Flags); - ImGui::Bullet(); ImGui::Text("Range: [%f,%f]",axis->Range.Min, axis->Range.Max); - ImGui::Bullet(); ImGui::Text("Pixels: %f", axis->Pixels); - ImGui::Bullet(); ImGui::Text("Aspect: %f", axis->GetAspect()); - ImGui::Bullet(); ImGui::Text("Dragging: %s", axis->Dragging ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("ExtHovered: %s", axis->ExtHovered ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("AllHovered: %s", axis->AllHovered ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("Present: %s", axis->Present ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("HasRange: %s", axis->HasRange ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("LinkedMin: %p", (void*)axis->LinkedMin); - ImGui::Bullet(); ImGui::Text("LinkedMax: %p", (void*)axis->LinkedMax); - if (show_axis_rects) { - ImDrawList& fg = *ImGui::GetForegroundDrawList(); - fg.AddRect(axis->HoverRect.Min, axis->HoverRect.Max, IM_COL32(0,255,0,255)); +void ShowTicksMetrics(const ImPlotTickCollection& ticks) { + ImGui::BulletText("Size: %d", ticks.Size); + ImGui::BulletText("MaxSize: [%f,%f]", ticks.MaxSize.x, ticks.MaxSize.y); +} + +void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { + ImGui::BulletText("Label: %s", axis.LabelOffset == -1 ? "[none]" : plot.GetAxisLabel(axis)); + ImGui::BulletText("Flags: 0x%08X", axis.Flags); + ImGui::BulletText("Range: [%f,%f]",axis.Range.Min, axis.Range.Max); + ImGui::BulletText("Pixels: %f", axis.PixelSize()); + ImGui::BulletText("Aspect: %f", axis.GetAspect()); + ImGui::BulletText(axis.OrthoAxis == NULL ? "OrtherAxis: NULL" : "OrthoAxis: 0x%08X", axis.OrthoAxis->ID); + ImGui::BulletText("LinkedMin: %p", (void*)axis.LinkedMin); + ImGui::BulletText("LinkedMax: %p", (void*)axis.LinkedMax); + ImGui::BulletText("HasRange: %s", axis.HasRange ? "true" : "false"); + ImGui::BulletText("Hovered: %s", axis.Hovered ? "true" : "false"); + ImGui::BulletText("Held: %s", axis.Held ? "true" : "false"); + + if (ImGui::TreeNode("Transform")) { + ImGui::BulletText("PixelMin: %f", axis.PixelMin); + ImGui::BulletText("PixelMax: %f", axis.PixelMax); + ImGui::BulletText("LinM: %f", axis.LinM); + ImGui::BulletText("LogD: %f", axis.LogD); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Ticks")) { + ShowTicksMetrics(axis.Ticks); + ImGui::TreePop(); } } @@ -3921,6 +4944,11 @@ void ShowMetricsWindow(bool* p_popen) { static bool show_plot_rects = false; static bool show_axes_rects = false; + static bool show_axis_rects = false; + static bool show_canvas_rects = false; + static bool show_frame_rects = false; + static bool show_subplot_frame_rects = false; + static bool show_subplot_grid_rects = false; ImDrawList& fg = *ImGui::GetForegroundDrawList(); @@ -3930,6 +4958,7 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::Begin("ImPlot Metrics", p_popen); ImGui::Text("ImPlot " IMPLOT_VERSION); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::Text("Mouse Position: [%.0f,%.0f]", io.MousePos.x, io.MousePos.y); ImGui::Separator(); if (ImGui::TreeNode("Tools")) { if (ImGui::Button("Bust Plot Cache")) @@ -3937,66 +4966,131 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::SameLine(); if (ImGui::Button("Bust Item Cache")) BustItemCache(); - ImGui::Checkbox("Show Plot Rects", &show_plot_rects); - ImGui::Checkbox("Show Axes Rects", &show_axes_rects); + ImGui::Checkbox("Show Frame Rects", &show_frame_rects); + ImGui::Checkbox("Show Canvas Rects",&show_canvas_rects); + ImGui::Checkbox("Show Plot Rects", &show_plot_rects); + ImGui::Checkbox("Show Axes Rects", &show_axes_rects); + ImGui::Checkbox("Show Axis Rects", &show_axis_rects); + ImGui::Checkbox("Show Subplot Frame Rects", &show_subplot_frame_rects); + ImGui::Checkbox("Show Subplot Grid Rects", &show_subplot_grid_rects); ImGui::TreePop(); } - const int n_plots = gp.Plots.GetSize(); + const int n_plots = gp.Plots.GetBufSize(); + const int n_subplots = gp.Subplots.GetBufSize(); + // render rects + for (int p = 0; p < n_plots; ++p) { + ImPlotPlot* plot = gp.Plots.GetByIndex(p); + if (show_frame_rects) + fg.AddRect(plot->FrameRect.Min, plot->FrameRect.Max, IM_COL32(255,0,255,255)); + if (show_canvas_rects) + fg.AddRect(plot->CanvasRect.Min, plot->CanvasRect.Max, IM_COL32(0,255,255,255)); + if (show_plot_rects) + fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); + if (show_axes_rects) + fg.AddRect(plot->AxesRect.Min, plot->AxesRect.Max, IM_COL32(0,255,128,255)); + if (show_axis_rects) { + for (int i = 0; i < ImAxis_COUNT; ++i) { + if (plot->Axes[i].Enabled) + fg.AddRect(plot->Axes[i].HoverRect.Min, plot->Axes[i].HoverRect.Max, IM_COL32(0,255,0,255)); + } + } + } + for (int p = 0; p < n_subplots; ++p) { + ImPlotSubplot* subplot = gp.Subplots.GetByIndex(p); + if (show_subplot_frame_rects) + fg.AddRect(subplot->FrameRect.Min, subplot->FrameRect.Max, IM_COL32(255,0,0,255)); + if (show_subplot_grid_rects) + fg.AddRect(subplot->GridRect.Min, subplot->GridRect.Max, IM_COL32(0,0,255,255)); + } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { // plot - ImPlotPlot* plot = gp.Plots.GetByIndex(p); + ImPlotPlot& plot = *gp.Plots.GetByIndex(p); ImGui::PushID(p); - if (ImGui::TreeNode("Plot", "Plot [ID=%u]", plot->ID)) { - int n_items = plot->Items.GetSize(); + if (ImGui::TreeNode("Plot", "Plot [0x%08X]", plot.ID)) { + int n_items = plot.Items.GetItemCount(); if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { for (int i = 0; i < n_items; ++i) { - ImPlotItem* item = plot->Items.GetByIndex(i); + ImPlotItem* item = plot.Items.GetItemByIndex(i); ImGui::PushID(i); - if (ImGui::TreeNode("Item", "Item [ID=%u]", item->ID)) { + if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) item->Color = ImGui::ColorConvertFloat4ToU32(temp); - ImGui::Bullet(); ImGui::Text("NameOffset: %d",item->NameOffset); - ImGui::Bullet(); ImGui::Text("Name: %s", item->NameOffset != -1 ? plot->LegendData.Labels.Buf.Data + item->NameOffset : "N/A"); - ImGui::Bullet(); ImGui::Text("Hovered: %s",item->LegendHovered ? "true" : "false"); + ImGui::BulletText("NameOffset: %d",item->NameOffset); + ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); + ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } - if (ImGui::TreeNode("X-Axis")) { - ShowAxisMetrics(&plot->XAxis, show_axes_rects); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Y-Axis")) { - ShowAxisMetrics(&plot->YAxis[0], show_axes_rects); - ImGui::TreePop(); + char buff[16]; + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + ImFormatString(buff,16,"X-Axis %d", i+1); + if (plot.XAxis(i).Enabled && ImGui::TreeNode(buff, "X-Axis %d [0x%08X]", i+1, plot.XAxis(i).ID)) { + ShowAxisMetrics(plot, plot.XAxis(i)); + ImGui::TreePop(); + } } - if (ImHasFlag(plot->Flags, ImPlotFlags_YAxis2) && ImGui::TreeNode("Y-Axis 2")) { - ShowAxisMetrics(&plot->YAxis[1], show_axes_rects); - ImGui::TreePop(); + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { + ImFormatString(buff,16,"Y-Axis %d", i+1); + if (plot.YAxis(i).Enabled && ImGui::TreeNode(buff, "Y-Axis %d [0x%08X]", i+1, plot.YAxis(i).ID)) { + ShowAxisMetrics(plot, plot.YAxis(i)); + ImGui::TreePop(); + } } - if (ImHasFlag(plot->Flags, ImPlotFlags_YAxis3) && ImGui::TreeNode("Y-Axis 3")) { - ShowAxisMetrics(&plot->YAxis[2], show_axes_rects); + ImGui::BulletText("Title: %s", plot.HasTitle() ? plot.GetTitle() : "none"); + ImGui::BulletText("Flags: 0x%08X", plot.Flags); + ImGui::BulletText("Initialized: %s", plot.Initialized ? "true" : "false"); + ImGui::BulletText("Selecting: %s", plot.Selecting ? "true" : "false"); + ImGui::BulletText("Selected: %s", plot.Selected ? "true" : "false"); + ImGui::BulletText("Hovered: %s", plot.Hovered ? "true" : "false"); + ImGui::BulletText("Held: %s", plot.Held ? "true" : "false"); + ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); + ImGui::BulletText("ContextLocked: %s", plot.ContextLocked ? "true" : "false"); + ImGui::TreePop(); + } + ImGui::PopID(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Subplots","Subplots (%d)", n_subplots)) { + for (int p = 0; p < n_subplots; ++p) { + // plot + ImPlotSubplot& plot = *gp.Subplots.GetByIndex(p); + ImGui::PushID(p); + if (ImGui::TreeNode("Subplot", "Subplot [0x%08X]", plot.ID)) { + int n_items = plot.Items.GetItemCount(); + if (ImGui::TreeNode("Items", "Items (%d)", n_items)) { + for (int i = 0; i < n_items; ++i) { + ImPlotItem* item = plot.Items.GetItemByIndex(i); + ImGui::PushID(i); + if (ImGui::TreeNode("Item", "Item [0x%08X]", item->ID)) { + ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); + ImGui::Bullet(); + ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); + if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) + item->Color = ImGui::ColorConvertFloat4ToU32(temp); + + ImGui::BulletText("NameOffset: %d",item->NameOffset); + ImGui::BulletText("Name: %s", item->NameOffset != -1 ? plot.Items.Legend.Labels.Buf.Data + item->NameOffset : "N/A"); + ImGui::BulletText("Hovered: %s",item->LegendHovered ? "true" : "false"); + ImGui::TreePop(); + } + ImGui::PopID(); + } ImGui::TreePop(); } - ImGui::Bullet(); ImGui::Text("Flags: %d", plot->Flags); - ImGui::Bullet(); ImGui::Text("Initialized: %s", plot->Initialized ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("Selecting: %s", plot->Selecting ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("Selected: %s", plot->Selected ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("Querying: %s", plot->Querying ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("Queried: %s", plot->Queried ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("FrameHovered: %s", plot->FrameHovered ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("PlotHovered: %s", plot->PlotHovered ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("LegendHovered: %s", plot->LegendHovered ? "true" : "false"); + ImGui::BulletText("Flags: 0x%08X", plot.Flags); + ImGui::BulletText("FrameHovered: %s", plot.FrameHovered ? "true" : "false"); + ImGui::BulletText("LegendHovered: %s", plot.Items.Legend.Hovered ? "true" : "false"); ImGui::TreePop(); - if (show_plot_rects) - fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); } ImGui::PopID(); } @@ -4101,7 +5195,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* GetTime(t_first_mo,&Tm); const int first_wd = Tm.tm_wday; // month year - snprintf(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); + ImFormatString(buff, 32, "%s %d", MONTH_NAMES[this_mon], this_year); if (ImGui::Button(buff)) *level = 1; ImGui::SameLine(5*cell_size.x); @@ -4127,10 +5221,12 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* for (int i = 0; i < 6; ++i) { for (int j = 0; j < 7; ++j) { if (mo == 0 && day > days_last_mo) { - mo = 1; day = 1; + mo = 1; + day = 1; } else if (mo == 1 && day > days_this_mo) { - mo = 2; day = 1; + mo = 2; + day = 1; } const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year); const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon); @@ -4147,7 +5243,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* ImGui::PushStyleColor(ImGuiCol_Text, col_txt); } ImGui::PushID(i*7+j); - snprintf(buff,32,"%d",day); + ImFormatString(buff,32,"%d",day); if (now_yr == min_yr-1 || now_yr == max_yr+1) { ImGui::Dummy(cell_size); } @@ -4171,7 +5267,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* *t = FloorTime(*t, ImPlotTimeUnit_Mo); GetTime(*t, &Tm); int this_yr = Tm.tm_year + 1900; - snprintf(buff, 32, "%d", this_yr); + ImFormatString(buff, 32, "%d", this_yr); if (ImGui::Button(buff)) *level = 2; BeginDisabledControls(this_yr <= min_yr); @@ -4211,7 +5307,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* int this_yr = GetYear(*t); int yr = this_yr - this_yr % 20; ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - snprintf(buff,32,"%d-%d",yr,yr+19); + ImFormatString(buff,32,"%d-%d",yr,yr+19); ImGui::Button(buff); ImGui::PopItemFlag(); ImGui::SameLine(5*cell_size.x); @@ -4232,7 +5328,7 @@ bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* const bool t1_or_t2 = (t1 != NULL && t1_yr == yr) || (t2 != NULL && t2_yr == yr); if (t1_or_t2) ImGui::PushStyleColor(ImGuiCol_Button, col_btn); - snprintf(buff,32,"%d",yr); + ImFormatString(buff,32,"%d",yr); if (yr<1970||yr>3000) { ImGui::Dummy(cell_size); } @@ -4329,7 +5425,7 @@ bool ShowTimePicker(const char* id, ImPlotTime* t) { } if (!hour24) { ImGui::SameLine(); - if (ImGui::Button(am_pm[ap],ImVec2(height,height))) { + if (ImGui::Button(am_pm[ap],ImVec2(0,height))) { ap = 1 - ap; changed = true; } @@ -4371,16 +5467,13 @@ void StyleColorsAuto(ImPlotStyle* dst) { colors[ImPlotCol_TitleText] = IMPLOT_AUTO_COL; colors[ImPlotCol_InlayText] = IMPLOT_AUTO_COL; colors[ImPlotCol_PlotBorder] = IMPLOT_AUTO_COL; - colors[ImPlotCol_XAxis] = IMPLOT_AUTO_COL; - colors[ImPlotCol_XAxisGrid] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxis] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxisGrid] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxis2] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxisGrid2] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxis3] = IMPLOT_AUTO_COL; - colors[ImPlotCol_YAxisGrid3] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisText] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisGrid] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; + colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; colors[ImPlotCol_Selection] = IMPLOT_AUTO_COL; - colors[ImPlotCol_Query] = IMPLOT_AUTO_COL; colors[ImPlotCol_Crosshairs] = IMPLOT_AUTO_COL; } @@ -4403,16 +5496,13 @@ void StyleColorsClassic(ImPlotStyle* dst) { colors[ImPlotCol_LegendText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_XAxis] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_XAxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); - colors[ImPlotCol_YAxis] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_YAxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); - colors[ImPlotCol_YAxis2] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_YAxisGrid2] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); - colors[ImPlotCol_YAxis3] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImPlotCol_YAxisGrid3] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); + colors[ImPlotCol_AxisText] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); + colors[ImPlotCol_AxisGrid] = ImVec4(0.90f, 0.90f, 0.90f, 0.25f); + colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.97f, 0.97f, 0.39f, 1.00f); - colors[ImPlotCol_Query] = ImVec4(0.00f, 1.00f, 0.59f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.50f, 0.50f, 0.50f, 0.75f); } @@ -4435,16 +5525,13 @@ void StyleColorsDark(ImPlotStyle* dst) { colors[ImPlotCol_LegendText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_XAxis] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_XAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); - colors[ImPlotCol_YAxis] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); - colors[ImPlotCol_YAxis2] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxisGrid2] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); - colors[ImPlotCol_YAxis3] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxisGrid3] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); + colors[ImPlotCol_AxisText] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f); + colors[ImPlotCol_AxisTick] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImPlotCol_Query] = ImVec4(0.00f, 1.00f, 0.44f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); } @@ -4467,17 +5554,37 @@ void StyleColorsLight(ImPlotStyle* dst) { colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_XAxis] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_XAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxis] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxis2] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid2] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); - colors[ImPlotCol_YAxis3] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid3] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); + colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImPlotCol_AxisTick] = ImVec4(0.00f, 0.00f, 0.00f, 0.25f); + colors[ImPlotCol_AxisBg] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgHovered] = IMPLOT_AUTO_COL; // TODO + colors[ImPlotCol_AxisBgActive] = IMPLOT_AUTO_COL; // TODO colors[ImPlotCol_Selection] = ImVec4(0.82f, 0.64f, 0.03f, 1.00f); - colors[ImPlotCol_Query] = ImVec4(0.00f, 0.84f, 0.37f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.00f, 0.00f, 0.00f, 0.50f); } +//----------------------------------------------------------------------------- +// [SECTION] Obsolete Functions/Types +//----------------------------------------------------------------------------- + +#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS + +bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, + ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, + const char* y2_label, const char* y3_label) +{ + if (!BeginPlot(title, size, flags)) + return false; + SetupAxis(ImAxis_X1, x_label, x_flags); + SetupAxis(ImAxis_Y1, y1_label, y1_flags); + if (ImHasFlag(flags, ImPlotFlags_YAxis2)) + SetupAxis(ImAxis_Y2, y2_label, y2_flags); + if (ImHasFlag(flags, ImPlotFlags_YAxis3)) + SetupAxis(ImAxis_Y3, y3_label, y3_flags); + return true; +} + +#endif + } // namespace ImPlot diff --git a/3rdparty/implot/implot.h b/3rdparty/implot/implot.h index e486748..8226eda 100644 --- a/3rdparty/implot/implot.h +++ b/3rdparty/implot/implot.h @@ -20,13 +20,35 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.10 WIP +// ImPlot v0.13 WIP + +// Table of Contents: +// +// [SECTION] Macros and Defines +// [SECTION] Enums and Types +// [SECTION] Callbacks +// [SECTION] Contexts +// [SECTION] Begin/End Plot +// [SECTION] Begin/End Subplot +// [SECTION] Setup +// [SECTION] SetNext +// [SECTION] Plot Items +// [SECTION] Plot Tools +// [SECTION] Plot Utils +// [SECTION] Legend Utils +// [SECTION] Drag and Drop +// [SECTION] Styling +// [SECTION] Colormaps +// [SECTION] Input Mapping +// [SECTION] Miscellaneous +// [SECTION] Demo +// [SECTION] Obsolete API #pragma once #include "imgui.h" //----------------------------------------------------------------------------- -// Macros and Defines +// [SECTION] Macros and Defines //----------------------------------------------------------------------------- // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -37,69 +59,150 @@ #define IMPLOT_API #endif -// ImPlot version string -#define IMPLOT_VERSION "0.10 WIP" +// ImPlot version string. +#define IMPLOT_VERSION "0.13 WIP" // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. #define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) +// Macro for templated plotting functions; keeps header clean. +#define IMPLOT_TMP template <typename T> IMPLOT_API //----------------------------------------------------------------------------- -// Forward Declarations and Basic Types +// [SECTION] Enums and Types //----------------------------------------------------------------------------- // Forward declarations -struct ImPlotContext; // ImPlot context (opaque struct, see implot_internal.h) +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_ -typedef int ImPlotLocation; // -> enum ImPlotLocation_ -typedef int ImPlotOrientation; // -> enum ImPlotOrientation_ -typedef int ImPlotYAxis; // -> enum ImPlotYAxis_; -typedef int ImPlotBin; // -> enum ImPlotBin_ +typedef int ImAxis; // -> enum ImAxis_ +typedef int ImPlotFlags; // -> enum ImPlotFlags_ +typedef int ImPlotAxisFlags; // -> enum ImPlotAxisFlags_ +typedef int ImPlotSubplotFlags; // -> enum ImPlotSubplotFlags_ +typedef int ImPlotLegendFlags; // -> enum ImPlotLegendFlags_ +typedef int ImPlotMouseTextFlags; // -> enum ImPlotMouseTextFlags_ +typedef int ImPlotDragToolFlags; // -> ImPlotDragToolFlags_ +typedef int ImPlotBarGroupsFlags; // -> ImPlotBarGroupsFlags_ + +typedef int ImPlotCond; // -> enum ImPlotCond_ +typedef int ImPlotCol; // -> enum ImPlotCol_ +typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ +typedef int ImPlotMarker; // -> enum ImPlotMarker_ +typedef int ImPlotColormap; // -> enum ImPlotColormap_ +typedef int ImPlotLocation; // -> enum ImPlotLocation_ +typedef int ImPlotBin; // -> enum ImPlotBin_ + +// Axis indices. The values assigned may change; NEVER hardcode these. +enum ImAxis_ { + // horizontal axes + ImAxis_X1 = 0, // enabled by default + ImAxis_X2, // disabled by default + ImAxis_X3, // disabled by default + // vertical axes + ImAxis_Y1, // enabled by default + ImAxis_Y2, // disabled by default + ImAxis_Y3, // disabled by default + // bookeeping + ImAxis_COUNT +}; -// Options for plots. +// Options for plots (see BeginPlot). enum ImPlotFlags_ { ImPlotFlags_None = 0, // default ImPlotFlags_NoTitle = 1 << 0, // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot") ImPlotFlags_NoLegend = 1 << 1, // the legend will not be displayed - ImPlotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click - ImPlotFlags_NoBoxSelect = 1 << 3, // the user will not be able to box-select with right-click drag - ImPlotFlags_NoMousePos = 1 << 4, // the mouse position, in plot coordinates, will not be displayed inside of the plot - ImPlotFlags_NoHighlight = 1 << 5, // plot items will not be highlighted when their legend entry is hovered + ImPlotFlags_NoMouseText = 1 << 2, // the mouse position, in plot coordinates, will not be displayed inside of the plot + ImPlotFlags_NoInputs = 1 << 3, // the user will not be able to interact with the plot + ImPlotFlags_NoMenus = 1 << 4, // the user will not be able to open context menus + ImPlotFlags_NoBoxSelect = 1 << 5, // the user will not be able to box-select ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_Equal = 1 << 7, // primary x and y axes will be constrained to have the same units/pixel (does not apply to auxiliary y-axes) - ImPlotFlags_YAxis2 = 1 << 8, // enable a 2nd y-axis on the right side - ImPlotFlags_YAxis3 = 1 << 9, // enable a 3rd y-axis on the right side - ImPlotFlags_Query = 1 << 10, // the user will be able to draw query rects with middle-mouse or CTRL + right-click drag - ImPlotFlags_Crosshairs = 1 << 11, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_AntiAliased = 1 << 12, // plot lines will be software anti-aliased (not recommended for high density plots, prefer MSAA) - ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMousePos + ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered + ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel + ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered + ImPlotFlags_AntiAliased = 1 << 10, // plot items will be software anti-aliased (not recommended for high density plots, prefer MSAA) + ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText }; -// Options for plot axes (X and Y). +// Options for plot axes (see SetupAxis). enum ImPlotAxisFlags_ { ImPlotAxisFlags_None = 0, // default ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels also hidden if the supplied string name is NULL) ImPlotAxisFlags_NoGridLines = 1 << 1, // no grid lines will be displayed ImPlotAxisFlags_NoTickMarks = 1 << 2, // no tick marks will be displayed ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed - ImPlotAxisFlags_Foreground = 1 << 4, // grid lines will be displayed in the foreground (i.e. on top of data) in stead of the background - ImPlotAxisFlags_LogScale = 1 << 5, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) - ImPlotAxisFlags_Time = 1 << 6, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) - ImPlotAxisFlags_Invert = 1 << 7, // the axis will be inverted - ImPlotAxisFlags_NoInitialFit = 1 << 8, // axis will not be initially fit to data extents on the first rendered frame (also the case if SetNextPlotLimits explicitly called) - ImPlotAxisFlags_AutoFit = 1 << 9, // axis will be auto-fitting to data extents - ImPlotAxisFlags_RangeFit = 1 << 10, // axis will only fit points if the point is in the visible range of the **orthoganol** axis - ImPlotAxisFlags_LockMin = 1 << 11, // the axis minimum value will be locked when panning/zooming - ImPlotAxisFlags_LockMax = 1 << 12, // the axis maximum value will be locked when panning/zooming + ImPlotAxisFlags_NoInitialFit = 1 << 4, // axis will not be initially fit to data extents on the first rendered frame + ImPlotAxisFlags_NoMenus = 1 << 5, // the user will not be able to open context menus with right-click + ImPlotAxisFlags_Opposite = 1 << 6, // axis ticks and labels will be rendered on conventionally opposite side (i.e, right or top) + ImPlotAxisFlags_Foreground = 1 << 7, // grid lines will be displayed in the foreground (i.e. on top of data) in stead of the background + ImPlotAxisFlags_LogScale = 1 << 8, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) + ImPlotAxisFlags_Time = 1 << 9, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) + ImPlotAxisFlags_Invert = 1 << 10, // the axis will be inverted + ImPlotAxisFlags_AutoFit = 1 << 11, // axis will be auto-fitting to data extents + ImPlotAxisFlags_RangeFit = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis + ImPlotAxisFlags_LockMin = 1 << 13, // the axis minimum value will be locked when panning/zooming + ImPlotAxisFlags_LockMax = 1 << 14, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, - ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels + ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels, + ImPlotAxisFlags_AuxDefault = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite +}; + +// Options for subplots (see BeginSubplot). +enum ImPlotSubplotFlags_ { + ImPlotSubplotFlags_None = 0, // default + ImPlotSubplotFlags_NoTitle = 1 << 0, // the subplot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MySubplot") + ImPlotSubplotFlags_NoLegend = 1 << 1, // the legend will not be displayed (only applicable if ImPlotSubplotFlags_ShareItems is enabled) + ImPlotSubplotFlags_NoMenus = 1 << 2, // the user will not be able to open context menus with right-click + ImPlotSubplotFlags_NoResize = 1 << 3, // resize splitters between subplot cells will be not be provided + ImPlotSubplotFlags_NoAlign = 1 << 4, // subplot edges will not be aligned vertically or horizontally + ImPlotSubplotFlags_ShareItems = 1 << 5, // items across all subplots will be shared and rendered into a single legend entry + ImPlotSubplotFlags_LinkRows = 1 << 6, // link the y-axis limits of all plots in each row (does not apply to auxiliary axes) + ImPlotSubplotFlags_LinkCols = 1 << 7, // link the x-axis limits of all plots in each column (does not apply to auxiliary axes) + ImPlotSubplotFlags_LinkAllX = 1 << 8, // link the x-axis limits in every plot in the subplot (does not apply to auxiliary axes) + ImPlotSubplotFlags_LinkAllY = 1 << 9, // link the y-axis limits in every plot in the subplot (does not apply to auxiliary axes) + ImPlotSubplotFlags_ColMajor = 1 << 10 // subplots are added in column major order instead of the default row major order +}; + +// Options for legends (see SetupLegend) +enum ImPlotLegendFlags_ { + ImPlotLegendFlags_None = 0, // default + ImPlotLegendFlags_NoButtons = 1 << 0, // legend icons will not function as hide/show buttons + ImPlotLegendFlags_NoHighlightItem = 1 << 1, // plot items will not be highlighted when their legend entry is hovered + ImPlotLegendFlags_NoHighlightAxis = 1 << 2, // axes will not be highlighted when legend entries are hovered (only relevant if x/y-axis count > 1) + ImPlotLegendFlags_NoMenus = 1 << 3, // the user will not be able to open context menus with right-click + ImPlotLegendFlags_Outside = 1 << 4, // legend will be rendered outside of the plot area + ImPlotLegendFlags_Horizontal = 1 << 5, // legend entries will be displayed horizontally +}; + +// Options for mouse hover text (see SetupMouseText) +enum ImPlotMouseTextFlags_ { + ImPlotMouseTextFlags_None = 0, // default + ImPlotMouseTextFlags_NoAuxAxes = 1 << 0, // only show the mouse position for primary axes + ImPlotMouseTextFlags_NoFormat = 1 << 1, // axes label formatters won't be used to render text + ImPlotMouseTextFlags_ShowAlways = 1 << 2, // always display mouse position even if plot not hovered +}; + +// Options for DragPoint, DragLine, DragRect +enum ImPlotDragToolFlags_ { + ImPlotDragToolFlags_None = 0, // default + ImPlotDragToolFlags_NoCursors = 1 << 0, // drag tools won't change cursor icons when hovered or held + ImPlotDragToolFlags_NoFit = 1 << 1, // the drag tool won't be considered for plot fits + ImPlotDragToolFlags_NoInputs = 1 << 2, // lock the tool from user inputs + ImPlotDragToolFlags_Delayed = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints +}; + +// Flags for ImPlot::PlotBarGroups +enum ImPlotBarGroupsFlags_ { + ImPlotBarGroupsFlags_None = 0, // default + ImPlotBarGroupsFlags_Stacked = 1 << 0, // items in a group will be stacked on top of each other +}; + +// Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums) +enum ImPlotCond_ +{ + ImPlotCond_None = ImGuiCond_None, // No condition (always set the variable), same as _Always + ImPlotCond_Always = ImGuiCond_Always, // No condition (always set the variable) + ImPlotCond_Once = ImGuiCond_Once, // Set the variable once per runtime session (only the first call will succeed) }; // Plot styling colors. @@ -119,16 +222,13 @@ enum ImPlotCol_ { ImPlotCol_LegendText, // legend text color (defaults to ImPlotCol_InlayText) ImPlotCol_TitleText, // plot title text color (defaults to ImGuiCol_Text) ImPlotCol_InlayText, // color of text appearing inside of plots (defaults to ImGuiCol_Text) - ImPlotCol_XAxis, // x-axis label and tick lables color (defaults to ImGuiCol_Text) - ImPlotCol_XAxisGrid, // x-axis grid color (defaults to 25% ImPlotCol_XAxis) - ImPlotCol_YAxis, // y-axis label and tick labels color (defaults to ImGuiCol_Text) - ImPlotCol_YAxisGrid, // y-axis grid color (defaults to 25% ImPlotCol_YAxis) - ImPlotCol_YAxis2, // 2nd y-axis label and tick labels color (defaults to ImGuiCol_Text) - ImPlotCol_YAxisGrid2, // 2nd y-axis grid/label color (defaults to 25% ImPlotCol_YAxis2) - ImPlotCol_YAxis3, // 3rd y-axis label and tick labels color (defaults to ImGuiCol_Text) - ImPlotCol_YAxisGrid3, // 3rd y-axis grid/label color (defaults to 25% ImPlotCol_YAxis3) + ImPlotCol_AxisText, // axis label and tick lables color (defaults to ImGuiCol_Text) + ImPlotCol_AxisGrid, // axis grid color (defaults to 25% ImPlotCol_AxisText) + ImPlotCol_AxisTick, // axis tick color (defaults to AxisGrid) + ImPlotCol_AxisBg, // background color of axis hover region (defaults to transparent) + ImPlotCol_AxisBgHovered, // axis hover color (defaults to ImGuiCol_ButtonHovered) + ImPlotCol_AxisBgActive, // axis active color (defaults to ImGuiCol_ButtonActive) ImPlotCol_Selection, // box-selection color (defaults to yellow) - ImPlotCol_Query, // box-query color (defaults to green) ImPlotCol_Crosshairs, // crosshairs color (defaults to ImPlotCol_PlotBorder) ImPlotCol_COUNT }; @@ -216,19 +316,6 @@ enum ImPlotLocation_ { ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East // bottom-right }; -// Used to orient items on a plot (e.g. legends, labels, etc.) -enum ImPlotOrientation_ { - ImPlotOrientation_Horizontal, // left/right - ImPlotOrientation_Vertical // up/down -}; - -// Enums for different y-axes. -enum ImPlotYAxis_ { - ImPlotYAxis_1 = 0, // left (default) - ImPlotYAxis_2 = 1, // first on right side - ImPlotYAxis_3 = 2 // second on right side -}; - // Enums for different automatic histogram binning methods (k = bin count or w = bin width) enum ImPlotBin_ { ImPlotBin_Sqrt = -1, // k = sqrt(n) @@ -240,8 +327,8 @@ enum ImPlotBin_ { // Double precision version of ImVec2 used by ImPlot. Extensible by end users. struct ImPlotPoint { double x, y; - ImPlotPoint() { x = y = 0.0; } - ImPlotPoint(double _x, double _y) { x = _x; y = _y; } + ImPlotPoint() { x = y = 0.0; } + ImPlotPoint(double _x, double _y) { x = _x; y = _y; } ImPlotPoint(const ImVec2& p) { x = p.x; y = p.y; } double operator[] (size_t idx) const { return (&x)[idx]; } double& operator[] (size_t idx) { return (&x)[idx]; } @@ -251,24 +338,28 @@ struct ImPlotPoint { #endif }; -// A range defined by a min/max value. Used for plot axes ranges. +// Range defined by a min/max value. struct ImPlotRange { double Min, Max; - ImPlotRange() { Min = 0; Max = 0; } - ImPlotRange(double _min, double _max) { Min = _min; Max = _max; } - bool Contains(double value) const { return value >= Min && value <= Max; }; - double Size() const { return Max - Min; }; + ImPlotRange() { Min = 0; Max = 0; } + ImPlotRange(double _min, double _max) { Min = _min; Max = _max; } + bool Contains(double value) const { return value >= Min && value <= Max; } + double Size() const { return Max - Min; } + double Clamp(double value) const { return (value < Min) ? Min : (value > Max) ? Max : value; } }; -// Combination of two ranges for X and Y axes. -struct ImPlotLimits { +// Combination of two range limits for X and Y axes. Also an AABB defined by Min()/Max(). +struct ImPlotRect { ImPlotRange X, Y; - ImPlotLimits() { } - ImPlotLimits(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; } - 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); } - ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } - ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } + ImPlotRect() { } + ImPlotRect(double x_min, double x_max, double y_min, double y_max) { X.Min = x_min; X.Max = x_max; Y.Min = y_min; Y.Max = y_max; } + 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); } + ImPlotPoint Size() const { return ImPlotPoint(X.Size(), Y.Size()); } + ImPlotPoint Clamp(const ImPlotPoint& p) { return Clamp(p.x, p.y); } + ImPlotPoint Clamp(double x, double y) { return ImPlotPoint(X.Clamp(x),Y.Clamp(y)); } + ImPlotPoint Min() const { return ImPlotPoint(X.Min, Y.Min); } + ImPlotPoint Max() const { return ImPlotPoint(X.Max, Y.Max); } }; // Plot style structure @@ -301,7 +392,7 @@ struct ImPlotStyle { ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot - ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk + ImVec2 PlotMinSize; // = 200,150 minimum size plot frame can be when shrunk // style colors ImVec4 Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums. // colormap @@ -314,14 +405,37 @@ struct ImPlotStyle { IMPLOT_API ImPlotStyle(); }; +// Input mapping structure. Default values listed. See also MapInputDefault, MapInputReverse. +struct ImPlotInputMap { + ImGuiMouseButton Pan; // LMB enables panning when held, + ImGuiKeyModFlags PanMod; // none optional modifier that must be held for panning/fitting + ImGuiMouseButton Fit; // LMB initiates fit when double clicked + ImGuiMouseButton Select; // RMB begins box selection when pressed and confirms selection when released + ImGuiMouseButton SelectCancel; // LMB cancels active box selection when pressed; cannot be same as Select + ImGuiKeyModFlags SelectMod; // none optional modifier that must be held for box selection + ImGuiKeyModFlags SelectHorzMod; // Alt expands active box selection horizontally to plot edge when held + ImGuiKeyModFlags SelectVertMod; // Shift expands active box selection vertically to plot edge when held + ImGuiMouseButton Menu; // RMB opens context menus (if enabled) when clicked + ImGuiKeyModFlags OverrideMod; // Ctrl when held, all input is ignored; used to enable axis/plots as DND sources + ImGuiKeyModFlags ZoomMod; // none optional modifier that must be held for scroll wheel zooming + float ZoomRate; // 0.1f zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click); make negative to invert + IMPLOT_API ImPlotInputMap(); +}; + //----------------------------------------------------------------------------- -// ImPlot End-User API +// [SECTION] Callbacks //----------------------------------------------------------------------------- +// Callback signature for axis tick label formatter. +typedef void (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); + +// Callback signature for data getter. +typedef ImPlotPoint (*ImPlotGetter)(void* user_data, int idx); + namespace ImPlot { //----------------------------------------------------------------------------- -// ImPlot Context +// [SECTION] Contexts //----------------------------------------------------------------------------- // Creates a new ImPlot context. Call this after ImGui::CreateContext. @@ -340,14 +454,14 @@ IMPLOT_API void SetCurrentContext(ImPlotContext* ctx); IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); //----------------------------------------------------------------------------- -// Begin/End Plot +// [SECTION] Begin/End Plot //----------------------------------------------------------------------------- // Starts a 2D plotting context. If this function returns true, EndPlot() MUST // be called! You are encouraged to use the following convention: // // if (BeginPlot(...)) { -// ImPlot::PlotLine(...); +// PlotLine(...); // ... // EndPlot(); // } @@ -357,34 +471,178 @@ IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); // - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID // collisions or don't want to display a title in the plot, use double hashes // (e.g. "MyPlot##HiddenIdText" or "##NoTitle"). -// - If #x_label and/or #y_label are provided, axes labels will be displayed. // - #size is the **frame** size of the plot widget, not the plot area. The default -// size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle -// (default is 400x300 px). -// - Auxiliary y-axes must be enabled with ImPlotFlags_YAxis2/3 to be displayed. -// - See ImPlotFlags and ImPlotAxisFlags for more available options. - -IMPLOT_API bool BeginPlot(const char* title_id, - const char* x_label = NULL, - const char* y_label = NULL, - const ImVec2& size = ImVec2(-1,0), - ImPlotFlags flags = ImPlotFlags_None, - ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, - ImPlotAxisFlags y_flags = ImPlotAxisFlags_None, - ImPlotAxisFlags y2_flags = ImPlotAxisFlags_NoGridLines, - ImPlotAxisFlags y3_flags = ImPlotAxisFlags_NoGridLines, - const char* y2_label = NULL, - const char* y3_label = NULL); +// size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle. +IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size = ImVec2(-1,0), ImPlotFlags flags = ImPlotFlags_None); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end // of an if statement conditioned on BeginPlot(). See example above. IMPLOT_API void EndPlot(); //----------------------------------------------------------------------------- -// Plot Items +// [SECTION] Begin/End Subplots +//----------------------------------------------------------------------------- + +// Starts a subdivided plotting context. If the function returns true, +// EndSubplots() MUST be called! Call BeginPlot/EndPlot AT MOST [rows*cols] +// times in between the begining and end of the subplot context. Plots are +// added in row major order. +// +// Example: +// +// if (BeginSubplots("My Subplot",2,3,ImVec2(800,400)) { +// for (int i = 0; i < 6; ++i) { +// if (BeginPlot(...)) { +// ImPlot::PlotLine(...); +// ... +// EndPlot(); +// } +// } +// EndSubplots(); +// } +// +// Produces: +// +// [0] | [1] | [2] +// ----|-----|---- +// [3] | [4] | [5] +// +// Important notes: +// +// - #title_id must be unique to the current ImGui ID scope. If you need to avoid ID +// collisions or don't want to display a title in the plot, use double hashes +// (e.g. "MySubplot##HiddenIdText" or "##NoTitle"). +// - #rows and #cols must be greater than 0. +// - #size is the size of the entire grid of subplots, not the individual plots +// - #row_ratios and #col_ratios must have AT LEAST #rows and #cols elements, +// respectively. These are the sizes of the rows and columns expressed in ratios. +// If the user adjusts the dimensions, the arrays are updated with new ratios. +// +// Important notes regarding BeginPlot from inside of BeginSubplots: +// +// - The #title_id parameter of _BeginPlot_ (see above) does NOT have to be +// unique when called inside of a subplot context. Subplot IDs are hashed +// for your convenience so you don't have call PushID or generate unique title +// strings. Simply pass an empty string to BeginPlot unless you want to title +// each subplot. +// - The #size parameter of _BeginPlot_ (see above) is ignored when inside of a +// subplot context. The actual size of the subplot will be based on the +// #size value you pass to _BeginSubplots_ and #row/#col_ratios if provided. + +IMPLOT_API bool BeginSubplots(const char* title_id, + int rows, + int cols, + const ImVec2& size, + ImPlotSubplotFlags flags = ImPlotSubplotFlags_None, + float* row_ratios = NULL, + float* col_ratios = NULL); + +// Only call EndSubplots() if BeginSubplots() returns true! Typically called at the end +// of an if statement conditioned on BeginSublots(). See example above. +IMPLOT_API void EndSubplots(); + +//----------------------------------------------------------------------------- +// [SECTION] Setup +//----------------------------------------------------------------------------- + +// The following API allows you to setup and customize various aspects of the +// current plot. The functions should be called immediately after BeginPlot +// and before any other API calls. Typical usage is as follows: + +// if (BeginPlot(...)) { 1) begin a new plot +// SetupAxis(ImAxis_X1, "My X-Axis"); 2) make Setup calls +// SetupAxis(ImAxis_Y1, "My Y-Axis"); +// SetupLegend(ImPlotLocation_North); +// ... +// SetupFinish(); 3) [optional] explicitly finish setup +// PlotLine(...); 4) plot items +// ... +// EndPlot(); 5) end the plot +// } +// +// Important notes: +// +// - Always call Setup code at the top of your BeginPlot conditional statement. +// - Setup is locked once you start plotting or explicitly call SetupFinish. +// Do NOT call Setup code after you begin plotting or after you make +// any non-Setup API calls (e.g. utils like PlotToPixels also lock Setup) +// - Calling SetupFinish is OPTIONAL, but probably good practice. If you do not +// call it yourself, then the first subsequent plotting or utility function will +// call it for you. + +// Enables an axis or sets the label and/or flags for an existing axis. Leave #label = NULL for no label. +IMPLOT_API void SetupAxis(ImAxis axis, const char* label = NULL, ImPlotAxisFlags flags = ImPlotAxisFlags_None); +// Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. +IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); +// Links an axis range limits to external values. Set to NULL for no linkage. The pointer data must remain valid until EndPlot. +IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); +// Sets the format of numeric axis labels via formater specifier (default="%g"). Formated values will be double (i.e. use %f). +IMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt); +// Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data. +IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data = NULL); +// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true. +IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); +// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. +IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); + +// Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis). +IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, ImPlotAxisFlags y_flags = ImPlotAxisFlags_None); +// Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). +IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); + +// Sets up the plot legend. +IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags = ImPlotLegendFlags_None); +// Set the location of the current plot's mouse position text (default = South|East). +IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags = ImPlotMouseTextFlags_None); + +// Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot! +// Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call. +IMPLOT_API void SetupFinish(); + +//----------------------------------------------------------------------------- +// [SECTION] SetNext +//----------------------------------------------------------------------------- + +// Though you should default to the `Setup` API above, there are some scenarios +// where (re)configuring a plot or axis before `BeginPlot` is needed (e.g. if +// using a preceding button or slider widget to change the plot limits). In +// this case, you can use the `SetNext` API below. While this is not as feature +// rich as the Setup API, most common needs are provided. These functions can be +// called anwhere except for inside of `Begin/EndPlot`. For example: + +// if (ImGui::Button("Center Plot")) +// ImPlot::SetNextPlotLimits(-1,1,-1,1); +// if (ImPlot::BeginPlot(...)) { +// ... +// ImPlot::EndPlot(); +// } +// +// Important notes: +// +// - You must still enable non-default axes with SetupAxis for these functions +// to work properly. + +// Sets an upcoming axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. +IMPLOT_API void SetNextAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); +// Links an upcoming axis range limits to external values. Set to NULL for no linkage. The pointer data must remain valid until EndPlot! +IMPLOT_API void SetNextAxisLinks(ImAxis axis, double* link_min, double* link_max); +// Set an upcoming axis to auto fit to its data. +IMPLOT_API void SetNextAxisToFit(ImAxis axis); + +// Sets the upcoming primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). +IMPLOT_API void SetNextAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); +// Sets all upcoming axes to auto fit to their data. +IMPLOT_API void SetNextAxesToFit(); + +//----------------------------------------------------------------------------- +// [SECTION] Plot Items //----------------------------------------------------------------------------- -// The template functions below are explicitly instantiated in implot_items.cpp. +// The main plotting API is provied below. Call these functions between +// Begin/EndPlot and after any Setup API calls. Each plots data on the current +// x and y axes, which can be changed with `SetAxis/Axes`. +// +// The templated functions are explicitly instantiated in implot_items.cpp. // They are not intended to be used generically with custom types. You will get // a linker error if you try! All functions support the following scalar types: // @@ -429,71 +687,77 @@ IMPLOT_API void EndPlot(); // if you try plotting extremely large 64-bit integral types. Proceed with caution! // Plots a standard 2D line plot. -template <typename T> IMPLOT_API void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotLine(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotLineG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset=0); +IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count); // Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. -template <typename T> IMPLOT_API void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotScatterG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset=0); +IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count); // Plots a a stairstep graph. The y value is continued constantly from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i]. -template <typename T> IMPLOT_API void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotStairsG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset=0); +IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count); -// Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set y_ref to +/-INFINITY for infinite fill extents. -template <typename T> IMPLOT_API void PlotShaded(const char* label_id, const T* values, int count, double y_ref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double y_ref=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotShadedG(const char* label_id, ImPlotPoint (*getter1)(void* data, int idx), void* data1, ImPlotPoint (*getter2)(void* data, int idx), void* data2, int count, int offset=0); +// Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents. +IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count); -// Plots a vertical bar graph. #width and #shift are in X units. -template <typename T> IMPLOT_API void PlotBars(const char* label_id, const T* values, int count, double width=0.67, double shift=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double width, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotBarsG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, double width, int offset=0); +// Plots a vertical bar graph. #bar_width and #x0 are in X units. +IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_width=0.67, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_width, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_width); -// Plots a horizontal bar graph. #height and #shift are in Y units. -template <typename T> IMPLOT_API void PlotBarsH(const char* label_id, const T* values, int count, double height=0.67, double shift=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double height, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, double height, int offset=0); +// Plots a horizontal bar graph. #bar_height and #y0 are in Y units. +IMPLOT_TMP void PlotBarsH(const char* label_id, const T* values, int count, double bar_height=0.67, double y0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double bar_height, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_height); + +// Plots a group of vertical bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. +IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_width=0.67, double x0=0, ImPlotBarGroupsFlags flags=ImPlotBarGroupsFlags_None); + +// Plots a group of horizontal bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. +IMPLOT_TMP void PlotBarGroupsH(const char* const label_ids[], const T* values, int item_count, int group_count, double group_height=0.67, double y0=0, ImPlotBarGroupsFlags flags=ImPlotBarGroupsFlags_None); // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. -template <typename T> IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); // Plots horizontal error bars. The label_id should be the same as the label_id of the associated line or bar plot. -template <typename T> IMPLOT_API void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); /// Plots vertical stems. -template <typename T> IMPLOT_API void PlotStems(const char* label_id, const T* values, int count, double y_ref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double y_ref=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double yref=0, int offset=0, int stride=sizeof(T)); /// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). -template <typename T> IMPLOT_API void PlotVLines(const char* label_id, const T* xs, int count, int offset=0, int stride=sizeof(T)); -template <typename T> IMPLOT_API void PlotHLines(const char* label_id, const T* ys, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotVLines(const char* label_id, const T* xs, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotHLines(const char* label_id, const T* ys, int count, int offset=0, int stride=sizeof(T)); // Plots a pie chart. If the sum of values > 1 or normalize is true, each value will be normalized. Center and radius are in plot units. #label_fmt can be set to NULL for no labels. -template <typename T> IMPLOT_API void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, bool normalize=false, const char* label_fmt="%.1f", double angle0=90); +IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, bool normalize=false, const char* label_fmt="%.1f", double angle0=90); // Plots a 2D heatmap chart. Values are expected to be in row-major order. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to NULL for no labels. -template <typename T> IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #cumulative is true, each bin contains its count plus the counts of all previous bins. // If #density is true, the PDF is visualized. If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. // If #range is specified, outlier values outside of the range are not binned. However, outliers still count toward normalizing and cumulative counts unless #outliers is false. The largest bin count or density is returned. -template <typename T> IMPLOT_API double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), bool outliers=true, double bar_scale=1.0); +IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), bool outliers=true, double bar_scale=1.0); // Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #density is true, the PDF is visualized. -// If #range is left unspecified, the min/max of #xs an #ys will be used as the ranges. If #range is specified, outlier values outside of range are not binned. +// If #bounds is left unspecified, the min/max of #xs an #ys will be used as the ranges. If #bounds is specified, outlier values outside of range are not binned. // However, outliers still count toward the normalizing count for density plots unless #outliers is false. The largest bin count or density is returned. -template <typename T> IMPLOT_API double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, bool density=false, ImPlotLimits range=ImPlotLimits(), bool outliers=true); +IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, bool density=false, ImPlotRect range=ImPlotRect(), bool outliers=true); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. -template <typename T> IMPLOT_API void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); - IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset=0); +IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count); // Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down). IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1)); @@ -505,129 +769,111 @@ IMPLOT_API void PlotText(const char* text, double x, double y, bool vertical=fal IMPLOT_API void PlotDummy(const char* label_id); //----------------------------------------------------------------------------- -// Plot Utils +// [SECTION] Plot Tools //----------------------------------------------------------------------------- -// The following functions MUST be called BEFORE BeginPlot! +// The following can be used to render interactive elements and/or annotations. +// Like the item plotting functions above, they apply to the current x and y +// axes, which can be changed with `SetAxis/SetAxes`. -// Set the axes range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axes limits will be locked. -IMPLOT_API void SetNextPlotLimits(double xmin, double xmax, double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once); -// Set the X axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the X axis limits will be locked. -IMPLOT_API void SetNextPlotLimitsX(double xmin, double xmax, ImGuiCond cond = ImGuiCond_Once); -// Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked. -IMPLOT_API void SetNextPlotLimitsY(double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once, ImPlotYAxis y_axis = ImPlotYAxis_1); -// Links the next plot limits to external values. Set to NULL for no linkage. The pointer data must remain valid until the matching call to EndPlot. -IMPLOT_API void LinkNextPlotLimits(double* xmin, double* xmax, double* ymin, double* ymax, double* ymin2 = NULL, double* ymax2 = NULL, double* ymin3 = NULL, double* ymax3 = NULL); -// Fits the next plot axes to all plotted data if they are unlocked (equivalent to double-clicks). -IMPLOT_API void FitNextPlotAxes(bool x = true, bool y = true, bool y2 = true, bool y3 = true); +// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. +IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +// Shows a draggable and resizeable rectangle. +IMPLOT_API bool DragRect(int id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); + +// Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. +IMPLOT_API void Annotation(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, bool round = false); +IMPLOT_API void Annotation(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); +IMPLOT_API void AnnotationV(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); + +// Shows a x-axis tag at the specified coordinate value. +IMPLOT_API void TagX(double x, const ImVec4& color, bool round = false); +IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(3); + +// Shows a y-axis tag at the specified coordinate value. +IMPLOT_API void TagY(double y, const ImVec4& color, bool round = false); +IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(3); -// Set the X axis ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetNextPlotTicksX(const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); -IMPLOT_API void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); -// Set the Y axis ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false, ImPlotYAxis y_axis = ImPlotYAxis_1); -IMPLOT_API void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false, ImPlotYAxis y_axis = ImPlotYAxis_1); +//----------------------------------------------------------------------------- +// [SECTION] Plot Utils +//----------------------------------------------------------------------------- -// Set the format for numeric X axis labels (default="%g"). Formated values will be doubles (i.e. don't supply %d, %i, etc.). Not applicable if ImPlotAxisFlags_Time enabled. -IMPLOT_API void SetNextPlotFormatX(const char* fmt); -// Set the format for numeric Y axis labels (default="%g"). Formated values will be doubles (i.e. don't supply %d, %i, etc.). -IMPLOT_API void SetNextPlotFormatY(const char* fmt, ImPlotYAxis y_axis=ImPlotYAxis_1); +// Select which axis/axes will be used for subsequent plot elements. +IMPLOT_API void SetAxis(ImAxis axis); +IMPLOT_API void SetAxes(ImAxis x_axis, ImAxis y_axis); -// The following functions MUST be called BETWEEN Begin/EndPlot! +// Convert pixels to a position in the current plot's coordinate system. Passing IMPLOT_AUTO uses the current axes. +IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); +IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -// Select which Y axis will be used for subsequent plot elements. The default is ImPlotYAxis_1, or the first (left) Y axis. Enable 2nd and 3rd axes with ImPlotFlags_YAxisX. -IMPLOT_API void SetPlotYAxis(ImPlotYAxis y_axis); -// Hides or shows the next plot item (i.e. as if it were toggled from the legend). Use ImGuiCond_Always if you need to forcefully set this every frame. -IMPLOT_API void HideNextItem(bool hidden = true, ImGuiCond cond = ImGuiCond_Once); +// Convert a position in the current plot's coordinate system to pixels. Passing IMPLOT_AUTO uses the current axes. +IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); +IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); -// Convert pixels to a position in the current plot's coordinate system. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially). -IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImPlotYAxis y_axis = IMPLOT_AUTO); -IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImPlotYAxis y_axis = IMPLOT_AUTO); -// Convert a position in the current plot's coordinate system to pixels. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially). -IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImPlotYAxis y_axis = IMPLOT_AUTO); -IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImPlotYAxis y_axis = IMPLOT_AUTO); // Get the current Plot position (top-left) in pixels. IMPLOT_API ImVec2 GetPlotPos(); // Get the curent Plot size in pixels. IMPLOT_API ImVec2 GetPlotSize(); + +// Returns the mouse position in x,y coordinates of the current plot. Passing IMPLOT_AUTO uses the current axes. +IMPLOT_API ImPlotPoint GetPlotMousePos(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); +// Returns the current plot axis range. +IMPLOT_API ImPlotRect GetPlotLimits(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); + // Returns true if the plot area in the current plot is hovered. IMPLOT_API bool IsPlotHovered(); -// Returns true if the XAxis plot area in the current plot is hovered. -IMPLOT_API bool IsPlotXAxisHovered(); -// Returns true if the YAxis[n] plot area in the current plot is hovered. -IMPLOT_API bool IsPlotYAxisHovered(ImPlotYAxis y_axis = 0); -// Returns the mouse position in x,y coordinates of the current plot. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially). -IMPLOT_API ImPlotPoint GetPlotMousePos(ImPlotYAxis y_axis = IMPLOT_AUTO); -// Returns the current plot axis range. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially). -IMPLOT_API ImPlotLimits GetPlotLimits(ImPlotYAxis y_axis = IMPLOT_AUTO); +// Returns true if the axis label area in the current plot is hovered. +IMPLOT_API bool IsAxisHovered(ImAxis axis); +// Returns true if the bounding frame of a subplot is hovered. +IMPLOT_API bool IsSubplotsHovered(); // Returns true if the current plot is being box selected. IMPLOT_API bool IsPlotSelected(); -// Returns the current plot box selection bounds. -IMPLOT_API ImPlotLimits GetPlotSelection(ImPlotYAxis y_axis = IMPLOT_AUTO); +// Returns the current plot box selection bounds. Passing IMPLOT_AUTO uses the current axes. +IMPLOT_API ImPlotRect GetPlotSelection(ImAxis x_axis = IMPLOT_AUTO, ImAxis y_axis = IMPLOT_AUTO); +// Cancels a the current plot box selection. +IMPLOT_API void CancelPlotSelection(); -// Returns true if the current plot is being queried or has an active query. Query must be enabled with ImPlotFlags_Query. -IMPLOT_API bool IsPlotQueried(); -// Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query. -IMPLOT_API ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis = IMPLOT_AUTO); -// Set the current plot query bounds. Query must be enabled with ImPlotFlags_Query. -IMPLOT_API void SetPlotQuery(const ImPlotLimits& query, ImPlotYAxis y_axis = IMPLOT_AUTO); +// Hides or shows the next plot item (i.e. as if it were toggled from the legend). +// Use ImPlotCond_Always if you need to forcefully set this every frame. +IMPLOT_API void HideNextItem(bool hidden = true, ImPlotCond cond = ImPlotCond_Once); -//----------------------------------------------------------------------------- -// Plot Tools -//----------------------------------------------------------------------------- - -// The following functions MUST be called BETWEEN Begin/EndPlot! - -// Shows an annotation callout at a chosen point. -IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const char* fmt, ...) IM_FMTARGS(4); -IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(5); -IMPLOT_API void AnnotateV(double x, double y, const ImVec2& pix_offset, const char* fmt, va_list args) IM_FMTLIST(4); -IMPLOT_API void AnnotateV(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(5); +// Use the following around calls to Begin/EndPlot to align l/r/t/b padding. +// Consider using Begin/EndSubplots first. They are more feature rich and +// accomplish the same behaviour by default. The functions below offer lower +// level control of plot alignment. -// Same as above, but the annotation will always be clamped to stay inside the plot area. -IMPLOT_API void AnnotateClamped(double x, double y, const ImVec2& pix_offset, const char* fmt, ...) IM_FMTARGS(4); -IMPLOT_API void AnnotateClamped(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(5); -IMPLOT_API void AnnotateClampedV(double x, double y, const ImVec2& pix_offset, const char* fmt, va_list args) IM_FMTLIST(4); -IMPLOT_API void AnnotateClampedV(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(5); - -// Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineX(const char* id, double* x_value, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); -// Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineY(const char* id, double* y_value, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); -// Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 4); +// Align axis padding over multiple plots in a single row or column. #group_id must +// be unique. If this function returns true, EndAlignedPlots() must be called. +IMPLOT_API bool BeginAlignedPlots(const char* group_id, bool vertical = true); +// Only call EndAlignedPlots() if BeginAlignedPlots() returns true! +IMPLOT_API void EndAlignedPlots(); //----------------------------------------------------------------------------- -// Legend Utils and Tools +// [SECTION] Legend Utils //----------------------------------------------------------------------------- -// The following functions MUST be called BETWEEN Begin/EndPlot! - -// Set the location of the current plot's legend (default = North|West). -IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation = ImPlotOrientation_Vertical, bool outside = false); -// Set the location of the current plot's mouse position text (default = South|East). -IMPLOT_API void SetMousePosLocation(ImPlotLocation location); -// Returns true if a plot item legend entry is hovered. -IMPLOT_API bool IsLegendEntryHovered(const char* label_id); - // Begin a popup for a legend entry. IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button = 1); // End a popup for a legend entry. IMPLOT_API void EndLegendPopup(); +// Returns true if a plot item legend entry is hovered. +IMPLOT_API bool IsLegendEntryHovered(const char* label_id); //----------------------------------------------------------------------------- -// Drag and Drop Utils +// [SECTION] Drag and Drop //----------------------------------------------------------------------------- -// The following functions MUST be called BETWEEN Begin/EndPlot! - // Turns the current plot's plotting area into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTarget(); +IMPLOT_API bool BeginDragDropTargetPlot(); // Turns the current plot's X-axis into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTargetX(); -// Turns the current plot's Y-Axis into a drag and drop target. Don't forget to call EndDragDropTarget! -IMPLOT_API bool BeginDragDropTargetY(ImPlotYAxis axis = ImPlotYAxis_1); +IMPLOT_API bool BeginDragDropTargetAxis(ImAxis axis); // Turns the current plot's legend into a drag and drop target. Don't forget to call EndDragDropTarget! IMPLOT_API bool BeginDragDropTargetLegend(); // Ends a drag and drop target (currently just an alias for ImGui::EndDragDropTarget). @@ -636,19 +882,17 @@ IMPLOT_API void EndDragDropTarget(); // NB: By default, plot and axes drag and drop *sources* require holding the Ctrl modifier to initiate the drag. // You can change the modifier if desired. If ImGuiKeyModFlags_None is provided, the axes will be locked from panning. -// Turns the current plot's plotting area into a drag and drop source. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSource(ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); -// Turns the current plot's X-axis into a drag and drop source. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceX(ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); -// Turns the current plot's Y-axis into a drag and drop source. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceY(ImPlotYAxis axis = ImPlotYAxis_1, ImGuiKeyModFlags key_mods = ImGuiKeyModFlags_Ctrl, ImGuiDragDropFlags flags = 0); +// Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags = 0); +// Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! +IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags = 0); // Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags = 0); // Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). IMPLOT_API void EndDragDropSource(); //----------------------------------------------------------------------------- -// Plot and Item Styling +// [SECTION] Styling //----------------------------------------------------------------------------- // Styling colors in ImPlot works similarly to styling colors in ImGui, but @@ -734,7 +978,7 @@ IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); //----------------------------------------------------------------------------- -// Colormaps +// [SECTION] Colormaps //----------------------------------------------------------------------------- // Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to @@ -784,7 +1028,7 @@ IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). -IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO, const char* fmt = "%g"); +IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO, const char* format = "%g"); // Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = NULL, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO); // Shows a button with a colormap gradient brackground. @@ -800,7 +1044,19 @@ IMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0, IMPLOT_API void BustColorCache(const char* plot_title_id = NULL); //----------------------------------------------------------------------------- -// Miscellaneous +// [SECTION] Input Mapping +//----------------------------------------------------------------------------- + +// Provides access to input mapping structure for permanant modifications to controls for pan, select, etc. +IMPLOT_API ImPlotInputMap& GetInputMap(); + +// Default input mapping: pan = LMB drag, box select = RMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. +IMPLOT_API void MapInputDefault(ImPlotInputMap* dst = NULL); +// Reverse input mapping: pan = RMB drag, box select = LMB drag, fit = LMB double click, context menu = RMB click, zoom = scroll. +IMPLOT_API void MapInputReverse(ImPlotInputMap* dst = NULL); + +//----------------------------------------------------------------------------- +// [SECTION] Miscellaneous //----------------------------------------------------------------------------- // Render icons similar to those that appear in legends (nifty for data lists). @@ -819,6 +1075,8 @@ IMPLOT_API void PopPlotClipRect(); IMPLOT_API bool ShowStyleSelector(const char* label); // Shows ImPlot colormap selector dropdown menu. IMPLOT_API bool ShowColormapSelector(const char* label); +// Shows ImPlot input map selector dropdown menu. +IMPLOT_API bool ShowInputMapSelector(const char* label); // Shows ImPlot style editor block (not a window). IMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = NULL); // Add basic help/info block for end users (not a window). @@ -827,10 +1085,59 @@ IMPLOT_API void ShowUserGuide(); IMPLOT_API void ShowMetricsWindow(bool* p_popen = NULL); //----------------------------------------------------------------------------- -// Demo (add implot_demo.cpp to your sources!) +// [SECTION] Demo //----------------------------------------------------------------------------- -// Shows the ImPlot demo window. +// Shows the ImPlot demo window (add implot_demo.cpp to your sources!) IMPLOT_API void ShowDemoWindow(bool* p_open = NULL); } // namespace ImPlot + +//----------------------------------------------------------------------------- +// [SECTION] Obsolete API +//----------------------------------------------------------------------------- + +// The following functions will be removed! Keep your copy of implot up to date! +// Occasionally set '#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS' to stay ahead. +// If you absolutely must use these functions and do not want to receive compiler +// warnings, set '#define IMPLOT_DISABLE_OBSOLETE_WARNINGS'. + +#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS + +#ifndef IMPLOT_DISABLE_DEPRECATED_WARNINGS +#if __cplusplus > 201402L +#define IMPLOT_DEPRECATED(method) [[deprecated]] method +#elif defined( __GNUC__ ) && !defined( __INTEL_COMPILER ) && ( __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) +#define IMPLOT_DEPRECATED(method) method __attribute__( ( deprecated ) ) +#elif defined( _MSC_VER ) +#define IMPLOT_DEPRECATED(method) __declspec(deprecated) method +#else +#define IMPLOT_DEPRECATED(method) method +#endif +#else +#define IMPLOT_DEPRECATED(method) method +#endif + +enum ImPlotFlagsObsolete_ { + ImPlotFlags_YAxis2 = 1 << 20, + ImPlotFlags_YAxis3 = 1 << 21, +}; + +namespace ImPlot { + +// OBSOLETED in v0.13 -> PLANNED REMOVAL in v1.0 +IMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id, + const char* x_label, // = NULL, + const char* y_label, // = NULL, + const ImVec2& size = ImVec2(-1,0), + ImPlotFlags flags = ImPlotFlags_None, + ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, + ImPlotAxisFlags y_flags = ImPlotAxisFlags_None, + ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault, + ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault, + const char* y2_label = NULL, + const char* y3_label = NULL) ); + +} // namespace ImPlot + +#endif diff --git a/3rdparty/implot/implot_demo.cpp b/3rdparty/implot/implot_demo.cpp index 53989df..be1548c 100644 --- a/3rdparty/implot/implot_demo.cpp +++ b/3rdparty/implot/implot_demo.cpp @@ -20,7 +20,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.10 WIP +// ImPlot v0.13 WIP + +// We define this so that the demo does not accidentally use deprecated API +#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS #include "implot.h" #include <math.h> @@ -174,1424 +177,1937 @@ struct HugeTimeData { static const int Size = 60*60*24*366; }; -void ShowDemoWindow(bool* p_open) { - double DEMO_TIME = ImGui::GetTime(); - static bool show_imgui_metrics = false; - static bool show_implot_metrics = false; - static bool show_imgui_style_editor = false; - static bool show_implot_style_editor = false; - static bool show_implot_benchmark = false; - if (show_imgui_metrics) { - ImGui::ShowMetricsWindow(&show_imgui_metrics); +//----------------------------------------------------------------------------- +// [SECTION] Demo Functions +//----------------------------------------------------------------------------- + +void ShowDemo_Help() { + ImGui::Text("ABOUT THIS DEMO:"); + ImGui::BulletText("Sections below are demonstrating many aspects of the library."); + ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" + "and Metrics (general purpose Dear ImGui debugging tool)."); + ImGui::Separator(); + ImGui::Text("PROGRAMMER GUIDE:"); + ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); + ImGui::BulletText("By default, anti-aliased lines are turned OFF."); + ImGui::Indent(); + ImGui::BulletText("Software AA can be enabled globally with ImPlotStyle.AntiAliasedLines."); + ImGui::BulletText("Software AA can be enabled per plot with ImPlotFlags_AntiAliased."); + ImGui::BulletText("AA for plots can be toggled from the plot's context menu."); + ImGui::BulletText("If permitable, you are better off using hardware AA (e.g. MSAA)."); + ImGui::Unindent(); + ImGui::BulletText("If you see visual artifacts, do one of the following:"); + ImGui::Indent(); + ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); + ImGui::BulletText("Or, enable 32-bit indices in imconfig.h."); + ImGui::BulletText("Your current configuration is:"); + ImGui::Indent(); + ImGui::BulletText("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); + ImGui::BulletText("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); + ImGui::Unindent(); + ImGui::Unindent(); + ImGui::Separator(); + ImGui::Text("USER GUIDE:"); + ShowUserGuide(); +} + +//----------------------------------------------------------------------------- + +void ButtonSelector(const char* label, ImGuiMouseButton* b) { + ImGui::PushID(label); + if (ImGui::RadioButton("LMB",*b == ImGuiMouseButton_Left)) + *b = ImGuiMouseButton_Left; + ImGui::SameLine(); + if (ImGui::RadioButton("RMB",*b == ImGuiMouseButton_Right)) + *b = ImGuiMouseButton_Right; + ImGui::SameLine(); + if (ImGui::RadioButton("MMB",*b == ImGuiMouseButton_Middle)) + *b = ImGuiMouseButton_Middle; + ImGui::PopID(); +} + +void ModSelector(const char* label, ImGuiKeyModFlags* k) { + ImGui::PushID(label); + ImGui::CheckboxFlags("Ctrl", (unsigned int*)k, ImGuiKeyModFlags_Ctrl); ImGui::SameLine(); + ImGui::CheckboxFlags("Shift", (unsigned int*)k, ImGuiKeyModFlags_Shift); ImGui::SameLine(); + ImGui::CheckboxFlags("Alt", (unsigned int*)k, ImGuiKeyModFlags_Alt); ImGui::SameLine(); + ImGui::CheckboxFlags("Super", (unsigned int*)k, ImGuiKeyModFlags_Super); + ImGui::PopID(); +} + +void InputMapping(const char* label, ImGuiMouseButton* b, ImGuiKeyModFlags* k) { + ImGui::LabelText("##","%s",label); + if (b != NULL) { + ImGui::SameLine(100); + ButtonSelector(label,b); } - if (show_implot_metrics) { - ImPlot::ShowMetricsWindow(&show_implot_metrics); + if (k != NULL) { + ImGui::SameLine(300); + ModSelector(label,k); } - if (show_imgui_style_editor) { - ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); - ImGui::ShowStyleEditor(); - ImGui::End(); +} + +void ShowInputMapping() { + ImPlotInputMap& map = ImPlot::GetInputMap(); + InputMapping("Pan",&map.Pan,&map.PanMod); + InputMapping("Fit",&map.Fit,NULL); + InputMapping("Select",&map.Select,&map.SelectMod); + InputMapping("SelectHorzMod",NULL,&map.SelectHorzMod); + InputMapping("SelectVertMod",NULL,&map.SelectVertMod); + InputMapping("SelectCancel",&map.SelectCancel,NULL); + InputMapping("Menu",&map.Menu,NULL); + InputMapping("OverrideMod",NULL,&map.OverrideMod); + InputMapping("ZoomMod",NULL,&map.ZoomMod); + ImGui::SliderFloat("ZoomRate",&map.ZoomRate,-1,1); +} + +void ShowDemo_Config() { + ImGui::ShowFontSelector("Font"); + ImGui::ShowStyleSelector("ImGui Style"); + ImPlot::ShowStyleSelector("ImPlot Style"); + ImPlot::ShowColormapSelector("ImPlot Colormap"); + ImPlot::ShowInputMapSelector("Input Map"); + ImGui::Separator(); + ImGui::Checkbox("Anti-Aliased Lines", &ImPlot::GetStyle().AntiAliasedLines); + ImGui::Checkbox("Use Local Time", &ImPlot::GetStyle().UseLocalTime); + ImGui::Checkbox("Use ISO 8601", &ImPlot::GetStyle().UseISO8601); + ImGui::Checkbox("Use 24 Hour Clock", &ImPlot::GetStyle().Use24HourClock); + ImGui::Separator(); + if (ImPlot::BeginPlot("Preview")) { + static double now = (double)time(0); + ImPlot::SetupAxis(ImAxis_X1,NULL,ImPlotAxisFlags_Time); + ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600); + for (int i = 0; i < 10; ++i) { + double x[2] = {now, now + 24*3600}; + double y[2] = {0,i/9.0}; + ImGui::PushID(i); + ImPlot::PlotLine("##Line",x,y,2); + ImGui::PopID(); + } + ImPlot::EndPlot(); } - if (show_implot_style_editor) { - ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); - ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); - ImPlot::ShowStyleEditor(); - ImGui::End(); +} + +//----------------------------------------------------------------------------- + +void ShowDemo_LinePlots() { + static float xs1[1001], ys1[1001]; + for (int i = 0; i < 1001; ++i) { + xs1[i] = i * 0.001f; + ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); } - if (show_implot_benchmark) { - ImGui::SetNextWindowSize(ImVec2(530,740), ImGuiCond_Appearing); - ImGui::Begin("ImPlot Benchmark Tool", &show_implot_benchmark); - ImPlot::ShowBenchmarkTool(); - ImGui::End(); - return; + static double xs2[11], ys2[11]; + for (int i = 0; i < 11; ++i) { + xs2[i] = i * 0.1f; + ys2[i] = xs2[i] * xs2[i]; } - ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); - ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); - if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Tools")) { - ImGui::MenuItem("Metrics (ImGui)", NULL, &show_imgui_metrics); - ImGui::MenuItem("Metrics (ImPlot)", NULL, &show_implot_metrics); - ImGui::MenuItem("Style Editor (ImGui)", NULL, &show_imgui_style_editor); - ImGui::MenuItem("Style Editor (ImPlot)", NULL, &show_implot_style_editor); - ImGui::MenuItem("Benchmark", NULL, &show_implot_benchmark); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); + ImGui::BulletText("Anti-aliasing can be enabled from the plot's context menu (see Help)."); + if (ImPlot::BeginPlot("Line Plot")) { + ImPlot::SetupAxes("x","f(x)"); + ImPlot::PlotLine("sin(x)", xs1, ys1, 1001); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::PlotLine("x^2", xs2, ys2, 11); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - ImGui::Text("ImPlot says hello. (%s)", IMPLOT_VERSION); - ImGui::Spacing(); +} - if (ImGui::CollapsingHeader("Help")) { - ImGui::Text("ABOUT THIS DEMO:"); - ImGui::BulletText("Sections below are demonstrating many aspects of the library."); - ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" - "and Metrics (general purpose Dear ImGui debugging tool)."); - ImGui::Separator(); - ImGui::Text("PROGRAMMER GUIDE:"); - ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); - ImGui::BulletText("By default, anti-aliased lines are turned OFF."); - ImGui::Indent(); - ImGui::BulletText("Software AA can be enabled globally with ImPlotStyle.AntiAliasedLines."); - ImGui::BulletText("Software AA can be enabled per plot with ImPlotFlags_AntiAliased."); - ImGui::BulletText("AA for plots can be toggled from the plot's context menu."); - ImGui::BulletText("If permitable, you are better off using hardware AA (e.g. MSAA)."); - ImGui::Unindent(); - ImGui::BulletText("If you see visual artifacts, do one of the following:"); - ImGui::Indent(); - ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); - ImGui::BulletText("Or, enable 32-bit indices in imconfig.h."); - ImGui::BulletText("Your current configuration is:"); - ImGui::Indent(); - ImGui::BulletText("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); - ImGui::BulletText("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); - ImGui::Unindent(); - ImGui::Unindent(); - ImGui::Separator(); - ImGui::Text("USER GUIDE:"); - ShowUserGuide(); - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Configuration")) { - ImGui::ShowFontSelector("Font"); - ImGui::ShowStyleSelector("ImGui Style"); - ImPlot::ShowStyleSelector("ImPlot Style"); - ImPlot::ShowColormapSelector("ImPlot Colormap"); - float indent = ImGui::CalcItemWidth() - ImGui::GetFrameHeight(); - ImGui::Indent(ImGui::CalcItemWidth() - ImGui::GetFrameHeight()); - ImGui::Checkbox("Anti-Aliased Lines", &ImPlot::GetStyle().AntiAliasedLines); - ImGui::Unindent(indent); +//----------------------------------------------------------------------------- + +void ShowDemo_FilledLinePlots() { + static double xs1[101], ys1[101], ys2[101], ys3[101]; + srand(0); + for (int i = 0; i < 101; ++i) { + xs1[i] = (float)i; + ys1[i] = RandomRange(400.0,450.0); + ys2[i] = RandomRange(275.0,350.0); + ys3[i] = RandomRange(150.0,225.0); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Line Plots")) { - static float xs1[1001], ys1[1001]; - for (int i = 0; i < 1001; ++i) { - xs1[i] = i * 0.001f; - ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)DEMO_TIME / 10)); - } - static double xs2[11], ys2[11]; - for (int i = 0; i < 11; ++i) { - xs2[i] = i * 0.1f; - ys2[i] = xs2[i] * xs2[i]; - } - ImGui::BulletText("Anti-aliasing can be enabled from the plot's context menu (see Help)."); - if (ImPlot::BeginPlot("Line Plot", "x", "f(x)")) { - ImPlot::PlotLine("sin(x)", xs1, ys1, 1001); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotLine("x^2", xs2, ys2, 11); - ImPlot::EndPlot(); + static bool show_lines = true; + static bool show_fills = true; + static float fill_ref = 0; + static int shade_mode = 0; + ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); + ImGui::Checkbox("Fills",&show_fills); + if (show_fills) { + ImGui::SameLine(); + if (ImGui::RadioButton("To -INF",shade_mode == 0)) + shade_mode = 0; + ImGui::SameLine(); + if (ImGui::RadioButton("To +INF",shade_mode == 1)) + shade_mode = 1; + ImGui::SameLine(); + if (ImGui::RadioButton("To Ref",shade_mode == 2)) + shade_mode = 2; + if (shade_mode == 2) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(100); + ImGui::DragFloat("##Ref",&fill_ref, 1, -100, 500); } } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Filled Line Plots")) { - static double xs1[101], ys1[101], ys2[101], ys3[101]; - srand(0); - for (int i = 0; i < 101; ++i) { - xs1[i] = (float)i; - ys1[i] = RandomRange(400.0,450.0); - ys2[i] = RandomRange(275.0,350.0); - ys3[i] = RandomRange(150.0,225.0); - } - static bool show_lines = true; - static bool show_fills = true; - static float fill_ref = 0; - static int shade_mode = 0; - ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); - ImGui::Checkbox("Fills",&show_fills); + + if (ImPlot::BeginPlot("Stock Prices")) { + ImPlot::SetupAxes("Days","Price"); + ImPlot::SetupAxesLimits(0,100,0,500); if (show_fills) { - ImGui::SameLine(); - if (ImGui::RadioButton("To -INF",shade_mode == 0)) - shade_mode = 0; - ImGui::SameLine(); - if (ImGui::RadioButton("To +INF",shade_mode == 1)) - shade_mode = 1; - ImGui::SameLine(); - if (ImGui::RadioButton("To Ref",shade_mode == 2)) - shade_mode = 2; - if (shade_mode == 2) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(100); - ImGui::DragFloat("##Ref",&fill_ref, 1, -100, 500); - } + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); + ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); + ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); + ImPlot::PopStyleVar(); } + if (show_lines) { + ImPlot::PlotLine("Stock 1", xs1, ys1, 101); + ImPlot::PlotLine("Stock 2", xs1, ys2, 101); + ImPlot::PlotLine("Stock 3", xs1, ys3, 101); + } + ImPlot::EndPlot(); + } +} +//----------------------------------------------------------------------------- - ImPlot::SetNextPlotLimits(0,100,0,500); - if (ImPlot::BeginPlot("Stock Prices", "Days", "Price")) { - if (show_fills) { - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); - ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); - ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); - ImPlot::PopStyleVar(); - } - if (show_lines) { - ImPlot::PlotLine("Stock 1", xs1, ys1, 101); - ImPlot::PlotLine("Stock 2", xs1, ys2, 101); - ImPlot::PlotLine("Stock 3", xs1, ys3, 101); - } - ImPlot::EndPlot(); - } +void ShowDemo_ShadedPlots() { + static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; + srand(0); + for (int i = 0; i < 1001; ++i) { + xs[i] = i * 0.001f; + ys[i] = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f); + ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); + ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); + ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]); + ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Shaded Plots##")) { - static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; - srand(0); - for (int i = 0; i < 1001; ++i) { - xs[i] = i * 0.001f; - ys[i] = 0.25f + 0.25f * sinf(25 * xs[i]) * sinf(5 * xs[i]) + RandomRange(-0.01f, 0.01f); - ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); - ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); - ys3[i] = 0.75f + 0.2f * sinf(25 * xs[i]); - ys4[i] = 0.75f + 0.1f * cosf(25 * xs[i]); - } - static float alpha = 0.25f; - ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); + static float alpha = 0.25f; + ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); - if (ImPlot::BeginPlot("Shaded Plots", "X-Axis", "Y-Axis")) { - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); - ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); - ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); - ImPlot::PlotShaded("Overlapping",xs,ys3,ys4,1001); - ImPlot::PlotLine("Overlapping",xs,ys3,1001); - ImPlot::PlotLine("Overlapping",xs,ys4,1001); - ImPlot::PopStyleVar(); - ImPlot::EndPlot(); - } + if (ImPlot::BeginPlot("Shaded Plots")) { + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); + ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); + ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); + ImPlot::PlotShaded("Overlapping",xs,ys3,ys4,1001); + ImPlot::PlotLine("Overlapping",xs,ys3,1001); + ImPlot::PlotLine("Overlapping",xs,ys4,1001); + ImPlot::PopStyleVar(); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Scatter Plots")) { - srand(0); - static float xs1[100], ys1[100]; - for (int i = 0; i < 100; ++i) { - xs1[i] = i * 0.01f; - ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); - } - static float xs2[50], ys2[50]; - for (int i = 0; i < 50; i++) { - xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); - ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); - } +} - if (ImPlot::BeginPlot("Scatter Plot", NULL, NULL)) { - ImPlot::PlotScatter("Data 1", xs1, ys1, 100); - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImVec4(0,1,0,0.5f), IMPLOT_AUTO, ImVec4(0,1,0,1)); - ImPlot::PlotScatter("Data 2", xs2, ys2, 50); - ImPlot::PopStyleVar(); - ImPlot::EndPlot(); - } +//----------------------------------------------------------------------------- + +void ShowDemo_ScatterPlots() { + srand(0); + static float xs1[100], ys1[100]; + for (int i = 0; i < 100; ++i) { + xs1[i] = i * 0.01f; + ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Stairstep Plots")) { - static float ys1[101], ys2[101]; - for (int i = 0; i < 101; ++i) { - ys1[i] = 0.5f + 0.4f * sinf(50 * i * 0.01f); - ys2[i] = 0.5f + 0.2f * sinf(25 * i * 0.01f); - } - if (ImPlot::BeginPlot("Stairstep Plot", "x", "f(x)")) { - ImPlot::PlotStairs("Signal 1", ys1, 101, 0.01f); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 2.0f); - ImPlot::PlotStairs("Signal 2", ys2, 101, 0.01f); - ImPlot::EndPlot(); - } + static float xs2[50], ys2[50]; + for (int i = 0; i < 50; i++) { + xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); + ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Bar Plots")) { - static bool horz = false; - static ImS8 midtm[10] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90}; - static ImS16 final[10] = {80, 62, 56, 99, 55, 78, 88, 78, 90, 100}; - static ImS32 grade[10] = {80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; + if (ImPlot::BeginPlot("Scatter Plot")) { + ImPlot::PlotScatter("Data 1", xs1, ys1, 100); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); + ImPlot::PlotScatter("Data 2", xs2, ys2, 50); + ImPlot::PopStyleVar(); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void ShowDemo_StairstepPlots() { + static float ys1[101], ys2[101]; + for (int i = 0; i < 101; ++i) { + ys1[i] = 0.5f + 0.4f * sinf(50 * i * 0.01f); + ys2[i] = 0.5f + 0.2f * sinf(25 * i * 0.01f); + } + if (ImPlot::BeginPlot("Stairstep Plot")) { + ImPlot::SetupAxes("x","f(x)"); + ImPlot::PlotStairs("Signal 1", ys1, 101, 0.01f); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 2.0f); + ImPlot::PlotStairs("Signal 2", ys2, 101, 0.01f); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void ShowDemo_BarPlots() { + static ImS8 data[10] = {1,2,3,4,5,6,7,8,9,10}; + if (ImPlot::BeginPlot("Bar Plot")) { + ImPlot::PlotBars("Bars",data,10,0.7,1); + ImPlot::PlotBarsH("BarsH",data,10,0.4,1); + ImPlot::EndPlot(); + } +} + +void ShowDemo_BarGroups() { + static ImS8 data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90, // midterm + 80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final + 80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course + + static const char* ilabels[] = {"Midterm Exam","Final Exam","Course Grade"}; + static const char* glabels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; + static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; - static const char* labels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; - static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; + static int items = 3; + static int groups = 10; + static float size = 0.67f; - ImGui::Checkbox("Horizontal",&horz); + static ImPlotBarGroupsFlags flags = 0; + static bool horz = false; + ImGui::CheckboxFlags("Stacked", (unsigned int*)&flags, ImPlotBarGroupsFlags_Stacked); + ImGui::SameLine(); + ImGui::Checkbox("Horizontal",&horz); + + ImGui::SliderInt("Items",&items,1,3); + ImGui::SliderFloat("Size",&size,0,1); + + if (ImPlot::BeginPlot("Bar Group")) { + ImPlot::SetupLegend(ImPlotLocation_East, ImPlotLegendFlags_Outside); if (horz) { - ImPlot::SetNextPlotLimits(0, 110, -0.5, 9.5, ImGuiCond_Always); - ImPlot::SetNextPlotTicksY(positions, 10, labels); + ImPlot::SetupAxes("Score","Student",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); + ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels); + ImPlot::PlotBarGroupsH(ilabels,data,items,groups,size,0,flags); } else { - ImPlot::SetNextPlotLimits(-0.5, 9.5, 0, 110, ImGuiCond_Always); - ImPlot::SetNextPlotTicksX(positions, 10, labels); - } - if (ImPlot::BeginPlot("Bar Plot", horz ? "Score" : "Student", horz ? "Student" : "Score", - ImVec2(-1,0), 0, 0, horz ? ImPlotAxisFlags_Invert : 0)) - { - if (horz) { - ImPlot::SetLegendLocation(ImPlotLocation_West, ImPlotOrientation_Vertical); - ImPlot::PlotBarsH("Midterm Exam", midtm, 10, 0.2, -0.2); - ImPlot::PlotBarsH("Final Exam", final, 10, 0.2, 0); - ImPlot::PlotBarsH("Course Grade", grade, 10, 0.2, 0.2); - } - else { - ImPlot::SetLegendLocation(ImPlotLocation_South, ImPlotOrientation_Horizontal); - ImPlot::PlotBars("Midterm Exam", midtm, 10, 0.2, -0.2); - ImPlot::PlotBars("Final Exam", final, 10, 0.2, 0); - ImPlot::PlotBars("Course Grade", grade, 10, 0.2, 0.2); - } - ImPlot::EndPlot(); + ImPlot::SetupAxes("Student","Score",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); + ImPlot::SetupAxisTicks(ImAxis_X1,positions, groups, glabels); + ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags); } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Error Bars")) { - static float xs[5] = {1,2,3,4,5}; - static float bar[5] = {1,2,5,3,4}; - static float lin1[5] = {8,8,9,7,8}; - static float lin2[5] = {6,7,6,9,6}; - static float err1[5] = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f}; - static float err2[5] = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f}; - static float err3[5] = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f}; - static float err4[5] = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f}; +} +void ShowDemo_BarStacks() { - ImPlot::SetNextPlotLimits(0, 6, 0, 10); - if (ImPlot::BeginPlot("##ErrorBars",NULL,NULL)) { + static ImPlotColormap Liars = -1; + if (Liars == -1) { + static const ImU32 Liars_Data[6] = { 4282515870, 4282609140, 4287357182, 4294630301, 4294945280, 4294921472 }; + Liars = ImPlot::AddColormap("Liars", Liars_Data, 6); + } - ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); - ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); + static bool diverging = true; + ImGui::Checkbox("Diverging",&diverging); - ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); - ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotLine("Line", xs, lin1, 5); + static const char* politicians[] = {"Trump","Bachman","Cruz","Gingrich","Palin","Santorum","Walker","Perry","Ryan","McCain","Rubio","Romney","Rand Paul","Christie","Biden","Kasich","Sanders","J Bush","H Clinton","Obama"}; + static int data_reg[] = {18,26,7,14,10,8,6,11,4,4,3,8,6,8,6,5,0,3,1,2, // Pants on Fire + 43,36,30,21,30,27,25,17,11,22,15,16,16,17,12,12,14,6,13,12, // False + 16,13,28,22,15,21,15,18,30,17,24,18,13,10,14,15,17,22,14,12, // Mostly False + 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True + 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True + 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True + static const char* labels_reg[] = {"Pants on Fire","False","Mostly False","Half True","Mostly True","True"}; - ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); - ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); - ImPlot::PlotErrorBarsH("Scatter", xs, lin2, err3, err4, 5); - ImPlot::PopStyleColor(); - ImPlot::PlotScatter("Scatter", xs, lin2, 5); - ImPlot::EndPlot(); - } + static int data_div[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Pants on Fire (dummy, to order legend logically) + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // False (dummy, to order legend logically) + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Mostly False (dummy, to order legend logically) + -16,-13,-28,-22,-15,-21,-15,-18,-30,-17,-24,-18,-13,-10,-14,-15,-17,-22,-14,-12, // Mostly False + -43,-36,-30,-21,-30,-27,-25,-17,-11,-22,-15,-16,-16,-17,-12,-12,-14,-6,-13,-12, // False + -18,-26,-7,-14,-10,-8,-6,-11,-4,-4,-3,-8,-6,-8,-6,-5,0,-3,-1,-2, // Pants on Fire + 17,10,13,25,12,22,19,26,23,17,22,27,20,26,29,17,18,22,21,27, // Half True + 5,7,16,10,10,12,23,13,17,20,22,16,23,19,20,26,36,29,27,26, // Mostly True + 1,8,6,8,23,10,12,15,15,20,14,15,22,20,19,25,15,18,24,21}; // True + static const char* labels_div[] = {"Pants on Fire","False","Mostly False","Mostly False","False","Pants on Fire","Half True","Mostly True","True"}; + + ImPlot::PushColormap(Liars); + if (ImPlot::BeginPlot("PolitiFact: Who Lies More?",ImVec2(-1,400),ImPlotFlags_NoMouseText)) { + ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside|ImPlotLegendFlags_Horizontal); + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert); + ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false); + if (diverging) + ImPlot::PlotBarGroupsH(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked); + else + ImPlot::PlotBarGroupsH(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Stem Plots##")) { - static double xs[51], ys1[51], ys2[51]; - for (int i = 0; i < 51; ++i) { - xs[i] = i * 0.02; - ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]); - ys2[i] = 0.5 + 0.25 * sin(10*xs[i]) * sin(xs[i]); - } - ImPlot::SetNextPlotLimits(0,1,0,1.6); - if (ImPlot::BeginPlot("Stem Plots")) { - ImPlot::PlotStems("Stems 1",xs,ys1,51); - ImPlot::SetNextLineStyle(ImVec4(1,0.5f,0,0.75f)); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,5,ImVec4(1,0.5f,0,0.25f)); - ImPlot::PlotStems("Stems 2", xs, ys2,51); - ImPlot::EndPlot(); - } + ImPlot::PopColormap(); +} + +//----------------------------------------------------------------------------- + +void ShowDemo_ErrorBars() { + static float xs[5] = {1,2,3,4,5}; + static float bar[5] = {1,2,5,3,4}; + static float lin1[5] = {8,8,9,7,8}; + static float lin2[5] = {6,7,6,9,6}; + static float err1[5] = {0.2f, 0.4f, 0.2f, 0.6f, 0.4f}; + static float err2[5] = {0.4f, 0.2f, 0.4f, 0.8f, 0.6f}; + static float err3[5] = {0.09f, 0.14f, 0.09f, 0.12f, 0.16f}; + static float err4[5] = {0.02f, 0.08f, 0.15f, 0.05f, 0.2f}; + + + if (ImPlot::BeginPlot("##ErrorBars")) { + ImPlot::SetupAxesLimits(0, 6, 0, 10); + ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); + ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); + ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); + ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::PlotLine("Line", xs, lin1, 5); + ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); + ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); + ImPlot::PlotErrorBarsH("Scatter", xs, lin2, err3, err4, 5); + ImPlot::PopStyleColor(); + ImPlot::PlotScatter("Scatter", xs, lin2, 5); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Infinite Lines")) { - static double vals[] = {0.25, 0.5, 0.75}; - if (ImPlot::BeginPlot("##Infinite",0,0,ImVec2(-1,0),0,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit)) { - ImPlot::PlotVLines("VLines",vals,3); - ImPlot::PlotHLines("HLines",vals,3); - ImPlot::EndPlot(); - } +} + +void ShowDemo_StemPlots() { + static double xs[51], ys1[51], ys2[51]; + for (int i = 0; i < 51; ++i) { + xs[i] = i * 0.02; + ys1[i] = 1.0 + 0.5 * sin(25*xs[i])*cos(2*xs[i]); + ys2[i] = 0.5 + 0.25 * sin(10*xs[i]) * sin(xs[i]); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Pie Charts")) { - static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; - static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; - static bool normalize = false; - ImGui::SetNextItemWidth(250); - ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); - if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { - ImGui::SameLine(); - ImGui::Checkbox("Normalize", &normalize); - } + if (ImPlot::BeginPlot("Stem Plots")) { + ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0); + ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6); + ImPlot::PlotStems("Stems 1",xs,ys1,51); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,5); + ImPlot::PlotStems("Stems 2", xs, ys2,51); + ImPlot::EndPlot(); + } +} - ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Pie1", NULL, NULL, ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { - ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, normalize, "%.2f"); - ImPlot::EndPlot(); - } +void ShowDemo_InfiniteLines() { + static double vals[] = {0.25, 0.5, 0.75}; + if (ImPlot::BeginPlot("##Infinite")) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit); + ImPlot::PlotVLines("VLines",vals,3); + ImPlot::PlotHLines("HLines",vals,3); + ImPlot::EndPlot(); + } +} +void ShowDemo_PieCharts() { + static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; + static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; + static bool normalize = false; + ImGui::SetNextItemWidth(250); + ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); + if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { ImGui::SameLine(); + ImGui::Checkbox("Normalize", &normalize); + } - static const char* labels2[] = {"A","B","C","D","E"}; - static int data2[] = {1,1,2,3,5}; + if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { + ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(0, 1, 0, 1); + ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, normalize, "%.2f"); + ImPlot::EndPlot(); + } - ImPlot::PushColormap(ImPlotColormap_Pastel); - ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Pie2", NULL, NULL, ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { - ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, true, "%.0f", 180); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); + ImGui::SameLine(); + + static const char* labels2[] = {"A","B","C","D","E"}; + static int data2[] = {1,1,2,3,5}; + + ImPlot::PushColormap(ImPlotColormap_Pastel); + if (ImPlot::BeginPlot("##Pie2", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { + ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(0, 1, 0, 1); + ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, true, "%.0f", 180); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Heatmaps")) { - static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, - {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, - {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, - {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, - {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, - {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, - {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; - static float scale_min = 0; - static float scale_max = 6.3f; - static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; - static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; + ImPlot::PopColormap(); +} - static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { - map = (map + 1) % ImPlot::GetColormapCount(); - // We bust the color cache of our plots so that item colors will - // resample the new colormap in the event that they have already - // been created. See documentation in implot.h. - BustColorCache("##Heatmap1"); - BustColorCache("##Heatmap2"); - } +void ShowDemo_Heatmaps() { + static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, + {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, + {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, + {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, + {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, + {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, + {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; + static float scale_min = 0; + static float scale_max = 6.3f; + static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; + static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; - ImGui::SameLine(); - ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); - ImGui::SetNextItemWidth(225); - ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); - static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; + static ImPlotColormap map = ImPlotColormap_Viridis; + if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { + map = (map + 1) % ImPlot::GetColormapCount(); + // We bust the color cache of our plots so that item colors will + // resample the new colormap in the event that they have already + // been created. See documentation in implot.h. + BustColorCache("##Heatmap1"); + BustColorCache("##Heatmap2"); + } - ImPlot::PushColormap(map); - SetNextPlotTicksX(0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); - SetNextPlotTicksY(1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); - if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMousePos,axes_flags,axes_flags)) { - ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); - ImPlot::EndPlot(); - } - ImGui::SameLine(); - ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); + ImGui::SetNextItemWidth(225); + ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; - ImGui::SameLine(); + ImPlot::PushColormap(map); + + if (ImPlot::BeginPlot("##Heatmap1",ImVec2(225,225),ImPlotFlags_NoLegend|ImPlotFlags_NoMouseText)) { + ImPlot::SetupAxes(NULL, NULL, axes_flags, axes_flags); + ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); + ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); - const int size = 200; - static double values2[size*size]; - srand((unsigned int)(DEMO_TIME*1000000)); - for (int i = 0; i < size*size; ++i) - values2[i] = RandomRange(0.0,1.0); + ImGui::SameLine(); - ImPlot::SetNextPlotLimits(-1,1,-1,1); - if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(225,225),0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { - ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,NULL); - ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); + const int size = 200; + static double values2[size*size]; + srand((unsigned int)(ImGui::GetTime()*1000000)); + for (int i = 0; i < size*size; ++i) + values2[i] = RandomRange(0.0,1.0); + if (ImPlot::BeginPlot("##Heatmap2",ImVec2(225,225))) { + ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(-1,1,-1,1); + ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,NULL); + ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Histograms")) { - static int bins = 50; - static bool cumulative = false; - static bool density = true; - static bool outliers = true; - static double mu = 5; - static double sigma = 2; + ImPlot::PopColormap(); + +} + +void ShowDemo_Histogram() { + static int bins = 50; + static bool cumulative = false; + static bool density = true; + static bool outliers = true; + static double mu = 5; + static double sigma = 2; + ImGui::SetNextItemWidth(200); + if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); + if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); + if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); + if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); + if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; + if (bins>=0) { + ImGui::SameLine(); ImGui::SetNextItemWidth(200); - if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); - if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); - if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); - if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); - if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; - if (bins>=0) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(200); - ImGui::SliderInt("##Bins", &bins, 1, 100); - } - if (ImGui::Checkbox("Density", &density)) - ImPlot::FitNextPlotAxes(); + ImGui::SliderInt("##Bins", &bins, 1, 100); + } + if (ImGui::Checkbox("Density", &density)) + { + ImPlot::SetNextAxisToFit(ImAxis_X1); + ImPlot::SetNextAxisToFit(ImAxis_Y1); + } + ImGui::SameLine(); + if (ImGui::Checkbox("Cumulative", &cumulative)) + { + ImPlot::SetNextAxisToFit(ImAxis_X1); + ImPlot::SetNextAxisToFit(ImAxis_Y1); + } + ImGui::SameLine(); + static bool range = false; + ImGui::Checkbox("Range", &range); + static float rmin = -3; + static float rmax = 13; + if (range) { ImGui::SameLine(); - if (ImGui::Checkbox("Cumulative", &cumulative)) - ImPlot::FitNextPlotAxes(); + ImGui::SetNextItemWidth(200); + ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); ImGui::SameLine(); - static bool range = false; - ImGui::Checkbox("Range", &range); - static float rmin = -3; - static float rmax = 13; - if (range) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(200); - ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); - ImGui::SameLine(); - ImGui::Checkbox("Outliers",&outliers); - } + ImGui::Checkbox("Outliers",&outliers); + } - static NormalDistribution<10000> dist(mu, sigma); - static double x[100]; - static double y[100]; - if (density) { - for (int i = 0; i < 100; ++i) { - x[i] = -3 + 16 * (double)i/99.0; - y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); - } - if (cumulative) { - for (int i = 1; i < 100; ++i) - y[i] += y[i-1]; - for (int i = 0; i < 100; ++i) - y[i] /= y[99]; - } + static NormalDistribution<10000> dist(mu, sigma); + static double x[100]; + static double y[100]; + if (density) { + for (int i = 0; i < 100; ++i) { + x[i] = -3 + 16 * (double)i/99.0; + y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); } - - if (ImPlot::BeginPlot("##Histograms")) { - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, cumulative, density, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), outliers); - if (density && outliers) - ImPlot::PlotLine("Theoretical",x,y,100); - ImPlot::EndPlot(); + if (cumulative) { + for (int i = 1; i < 100; ++i) + y[i] += y[i-1]; + for (int i = 0; i < 100; ++i) + y[i] /= y[99]; } + } - static int count = 500000; - static int xybins[2] = {200,200}; - static bool density2 = false; - ImGui::SliderInt("Count",&count,100,500000); - ImGui::SliderInt2("Bins",xybins,1,500); - ImGui::SameLine(); - ImGui::Checkbox("Density##2",&density2); - static NormalDistribution<500000> dist1(1, 2); - static NormalDistribution<500000> dist2(1, 1); - double max_count = 0; - ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground; - ImPlot::PushColormap("Hot"); - if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0),0,flags,flags)) { - max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6)); - ImPlot::EndPlot(); - } - ImGui::SameLine(); - ImPlot::ColormapScale(density2 ? "Density" : "Count",0,max_count,ImVec2(100,0)); - ImPlot::PopColormap(); + if (ImPlot::BeginPlot("##Histograms")) { + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); + ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, cumulative, density, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), outliers); + if (density && outliers) + ImPlot::PlotLine("Theoretical",x,y,100); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Digital Plots")) { - ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); - ImGui::Indent(); - ImGui::Text("you can drag analog plots over the rising/falling digital edge."); - ImGui::Unindent(); +} - static bool paused = false; - static ScrollingBuffer dataDigital[2]; - static ScrollingBuffer dataAnalog[2]; - static bool showDigital[2] = {true, false}; - static bool showAnalog[2] = {true, false}; +void ShowDemo_Histogram2D() { + static int count = 500000; + static int xybins[2] = {200,200}; + static bool density2 = false; + ImGui::SliderInt("Count",&count,100,500000); + ImGui::SliderInt2("Bins",xybins,1,500); + ImGui::SameLine(); + ImGui::Checkbox("Density##2",&density2); + static NormalDistribution<500000> dist1(1, 2); + static NormalDistribution<500000> dist2(1, 1); + double max_count = 0; + ImPlotAxisFlags flags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Foreground; + ImPlot::PushColormap("Hot"); + if (ImPlot::BeginPlot("##Hist2D",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) { + ImPlot::SetupAxes(NULL, NULL, flags, flags); + ImPlot::SetupAxesLimits(-6,6,-6,6); + max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotRect(-6,6,-6,6)); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale(density2 ? "Density" : "Count",0,max_count,ImVec2(100,0)); + ImPlot::PopColormap(); +} - char label[32]; - ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); - ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); - ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); - ImGui::Checkbox("analog_1", &showAnalog[1]); +void ShowDemo_DigitalPlots() { + ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); + ImGui::Indent(); + ImGui::Text("you can drag analog plots over the rising/falling digital edge."); + ImGui::Unindent(); - static float t = 0; - if (!paused) { - t += ImGui::GetIO().DeltaTime; - //digital signal values - if (showDigital[0]) - dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); - if (showDigital[1]) - dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); - //Analog signal values - if (showAnalog[0]) - dataAnalog[0].AddPoint(t, sinf(2*t)); - if (showAnalog[1]) - dataAnalog[1].AddPoint(t, cosf(2*t)); - } - ImPlot::SetNextPlotLimitsY(-1, 1); - ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); - if (ImPlot::BeginPlot("##Digital")) { - for (int i = 0; i < 2; ++i) { - if (showDigital[i] && dataDigital[i].Data.size() > 0) { - sprintf(label, "digital_%d", i); - ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); - } + static bool paused = false; + static ScrollingBuffer dataDigital[2]; + static ScrollingBuffer dataAnalog[2]; + static bool showDigital[2] = {true, false}; + static bool showAnalog[2] = {true, false}; + + char label[32]; + ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); + ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); + ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); + ImGui::Checkbox("analog_1", &showAnalog[1]); + + static float t = 0; + if (!paused) { + t += ImGui::GetIO().DeltaTime; + //digital signal values + if (showDigital[0]) + dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); + if (showDigital[1]) + dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); + //Analog signal values + if (showAnalog[0]) + dataAnalog[0].AddPoint(t, sinf(2*t)); + if (showAnalog[1]) + dataAnalog[1].AddPoint(t, cosf(2*t)); + } + if (ImPlot::BeginPlot("##Digital")) { + ImPlot::SetupAxisLimits(ImAxis_X1, t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); + ImPlot::SetupAxisLimits(ImAxis_Y1, -1, 1); + for (int i = 0; i < 2; ++i) { + if (showDigital[i] && dataDigital[i].Data.size() > 0) { + sprintf(label, "digital_%d", i); + ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); } - for (int i = 0; i < 2; ++i) { - if (showAnalog[i]) { - sprintf(label, "analog_%d", i); - if (dataAnalog[i].Data.size() > 0) - ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); - } + } + for (int i = 0; i < 2; ++i) { + if (showAnalog[i]) { + sprintf(label, "analog_%d", i); + if (dataAnalog[i].Data.size() > 0) + ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); } - ImPlot::EndPlot(); } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Images")) { - ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); - ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); - ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); - static ImVec2 bmin(0,0); - static ImVec2 bmax(1,1); - static ImVec2 uv0(0,0); - static ImVec2 uv1(1,1); - static ImVec4 tint(1,1,1,1); - ImGui::SliderFloat2("Min", &bmin.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("Max", &bmax.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("UV0", &uv0.x, -2, 2, "%.1f"); - ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f"); - ImGui::ColorEdit4("Tint",&tint.x); - if (ImPlot::BeginPlot("##image")) { - ImPlot::PlotImage("my image",ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint); - ImPlot::EndPlot(); - } +} + +void ShowDemo_Images() { + ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); + ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); + ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); + static ImVec2 bmin(0,0); + static ImVec2 bmax(1,1); + static ImVec2 uv0(0,0); + static ImVec2 uv1(1,1); + static ImVec4 tint(1,1,1,1); + ImGui::SliderFloat2("Min", &bmin.x, -2, 2, "%.1f"); + ImGui::SliderFloat2("Max", &bmax.x, -2, 2, "%.1f"); + ImGui::SliderFloat2("UV0", &uv0.x, -2, 2, "%.1f"); + ImGui::SliderFloat2("UV1", &uv1.x, -2, 2, "%.1f"); + ImGui::ColorEdit4("Tint",&tint.x); + if (ImPlot::BeginPlot("##image")) { + ImPlot::PlotImage("my image",ImGui::GetIO().Fonts->TexID, bmin, bmax, uv0, uv1, tint); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Realtime Plots")) { - ImGui::BulletText("Move your mouse to change the data!"); - ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); - static ScrollingBuffer sdata1, sdata2; - static RollingBuffer rdata1, rdata2; - ImVec2 mouse = ImGui::GetMousePos(); - static float t = 0; - t += ImGui::GetIO().DeltaTime; - sdata1.AddPoint(t, mouse.x * 0.0005f); - rdata1.AddPoint(t, mouse.x * 0.0005f); - sdata2.AddPoint(t, mouse.y * 0.0005f); - rdata2.AddPoint(t, mouse.y * 0.0005f); +} - static float history = 10.0f; - ImGui::SliderFloat("History",&history,1,30,"%.1f s"); - rdata1.Span = history; - rdata2.Span = history; +void ShowDemo_RealtimePlots() { + ImGui::BulletText("Move your mouse to change the data!"); + ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); + static ScrollingBuffer sdata1, sdata2; + static RollingBuffer rdata1, rdata2; + ImVec2 mouse = ImGui::GetMousePos(); + static float t = 0; + t += ImGui::GetIO().DeltaTime; + sdata1.AddPoint(t, mouse.x * 0.0005f); + rdata1.AddPoint(t, mouse.x * 0.0005f); + sdata2.AddPoint(t, mouse.y * 0.0005f); + rdata2.AddPoint(t, mouse.y * 0.0005f); - static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; - ImPlot::SetNextPlotLimitsX(t - history, t, ImGuiCond_Always); - ImPlot::SetNextPlotLimitsY(0,1); - if (ImPlot::BeginPlot("##Scrolling", NULL, NULL, ImVec2(-1,150), 0, flags, flags)) { - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, sdata1.Offset, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), sdata2.Offset, 2*sizeof(float)); - ImPlot::EndPlot(); + static float history = 10.0f; + ImGui::SliderFloat("History",&history,1,30,"%.1f s"); + rdata1.Span = history; + rdata2.Span = history; + + static ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; + + if (ImPlot::BeginPlot("##Scrolling", ImVec2(-1,150))) { + ImPlot::SetupAxes(NULL, NULL, flags, flags); + ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always); + ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); + ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, sdata1.Offset, 2 * sizeof(float)); + ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), sdata2.Offset, 2*sizeof(float)); + ImPlot::EndPlot(); + } + if (ImPlot::BeginPlot("##Rolling", ImVec2(-1,150))) { + ImPlot::SetupAxes(NULL, NULL, flags, flags); + ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always); + ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); + ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 2 * sizeof(float)); + ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 2 * sizeof(float)); + ImPlot::EndPlot(); + } +} + +void ShowDemo_MarkersAndText() { + static float mk_size = ImPlot::GetStyle().MarkerSize; + static float mk_weight = ImPlot::GetStyle().MarkerWeight; + ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); + ImGui::DragFloat("Marker Weight", &mk_weight,0.05f,0.5f,3.0f,"%.2f px"); + + if (ImPlot::BeginPlot("##MarkerStyles", ImVec2(-1,0), ImPlotFlags_CanvasOnly)) { + + ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(0, 10, 0, 12); + + ImS8 xs[2] = {1,4}; + ImS8 ys[2] = {10,11}; + + // filled markers + for (int m = 0; m < ImPlotMarker_COUNT; ++m) { + ImGui::PushID(m); + ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight); + ImPlot::PlotLine("##Filled", xs, ys, 2); + ImGui::PopID(); + ys[0]--; ys[1]--; } - ImPlot::SetNextPlotLimitsX(0, history, ImGuiCond_Always); - ImPlot::SetNextPlotLimitsY(0,1); - if (ImPlot::BeginPlot("##Rolling", NULL, NULL, ImVec2(-1,150), 0, flags, flags)) { - ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 2 * sizeof(float)); - ImPlot::EndPlot(); + xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11; + // open markers + for (int m = 0; m < ImPlotMarker_COUNT; ++m) { + ImGui::PushID(m); + ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight); + ImPlot::PlotLine("##Open", xs, ys, 2); + ImGui::PopID(); + ys[0]--; ys[1]--; } + + ImPlot::PlotText("Filled Markers", 2.5f, 6.0f); + ImPlot::PlotText("Open Markers", 7.5f, 6.0f); + + ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); + ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, true); + ImPlot::PopStyleColor(); + + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Markers and Text")) { - static float mk_size = ImPlot::GetStyle().MarkerSize; - static float mk_weight = ImPlot::GetStyle().MarkerWeight; - ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); - ImGui::DragFloat("Marker Weight", &mk_weight,0.05f,0.5f,3.0f,"%.2f px"); +} - ImPlot::SetNextPlotLimits(0, 10, 0, 12); - if (ImPlot::BeginPlot("##MarkerStyles", NULL, NULL, ImVec2(-1,0), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { - ImS8 xs[2] = {1,4}; - ImS8 ys[2] = {10,11}; +void ShowDemo_LogAxes() { + static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; + for (int i = 0; i < 1001; ++i) { + xs[i] = i*0.1f; + ys1[i] = sin(xs[i]) + 1; + ys2[i] = log(xs[i]); + ys3[i] = pow(10.0, xs[i]); + } + ImGui::BulletText("Open the plot context menu (right click) to change scales."); - // filled markers - for (int m = 0; m < ImPlotMarker_COUNT; ++m) { - ImGui::PushID(m); - ImPlot::SetNextMarkerStyle(m, mk_size, IMPLOT_AUTO_COL, mk_weight); - ImPlot::PlotLine("##Filled", xs, ys, 2); - ImGui::PopID(); - ys[0]--; ys[1]--; - } - xs[0] = 6; xs[1] = 9; ys[0] = 10; ys[1] = 11; - // open markers - for (int m = 0; m < ImPlotMarker_COUNT; ++m) { - ImGui::PushID(m); - ImPlot::SetNextMarkerStyle(m, mk_size, ImVec4(0,0,0,0), mk_weight); - ImPlot::PlotLine("##Open", xs, ys, 2); - ImGui::PopID(); - ys[0]--; ys[1]--; - } + if (ImPlot::BeginPlot("Log Plot", ImVec2(-1,0))) { + ImPlot::SetupAxis(ImAxis_X1, NULL, ImPlotAxisFlags_LogScale); + ImPlot::SetupAxesLimits(0.1, 100, 0, 10); + ImPlot::PlotLine("f(x) = x", xs, xs, 1001); + ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); + ImPlot::PlotLine("f(x) = log(x)", xs, ys2, 1001); + ImPlot::PlotLine("f(x) = 10^x", xs, ys3, 21); + ImPlot::EndPlot(); + } +} - ImPlot::PlotText("Filled Markers", 2.5f, 6.0f); - ImPlot::PlotText("Open Markers", 7.5f, 6.0f); +void ShowDemo_TimeAxes() { - ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); - ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, true); - ImPlot::PopStyleColor(); + static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) + static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) - ImPlot::EndPlot(); + ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" + "UNIX timestamps in seconds and axis labels are formated as date/time."); + ImGui::BulletText("By default, labels are in UTC time but can be set to use local time instead."); + + ImGui::Checkbox("Local Time",&ImPlot::GetStyle().UseLocalTime); + ImGui::SameLine(); + ImGui::Checkbox("ISO 8601",&ImPlot::GetStyle().UseISO8601); + ImGui::SameLine(); + ImGui::Checkbox("24 Hour Clock",&ImPlot::GetStyle().Use24HourClock); + + static HugeTimeData* data = NULL; + if (data == NULL) { + ImGui::SameLine(); + if (ImGui::Button("Generate Huge Data (~500MB!)")) { + static HugeTimeData sdata(t_min); + data = &sdata; } } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Log Scale")) { - static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; - for (int i = 0; i < 1001; ++i) { - xs[i] = i*0.1f; - ys1[i] = sin(xs[i]) + 1; - ys2[i] = log(xs[i]); - ys3[i] = pow(10.0, xs[i]); - } - ImGui::BulletText("Open the plot context menu (right click) to change scales."); - ImPlot::SetNextPlotLimits(0.1, 100, 0, 10); - if (ImPlot::BeginPlot("Log Plot", NULL, NULL, ImVec2(-1,0), 0, ImPlotAxisFlags_LogScale )) { - ImPlot::PlotLine("f(x) = x", xs, xs, 1001); - ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); - ImPlot::PlotLine("f(x) = log(x)", xs, ys2, 1001); - ImPlot::PlotLine("f(x) = 10^x", xs, ys3, 21); - ImPlot::EndPlot(); + if (ImPlot::BeginPlot("##Time", ImVec2(-1,0))) { + ImPlot::SetupAxis(ImAxis_X1, NULL, ImPlotAxisFlags_Time); + ImPlot::SetupAxesLimits(t_min,t_max,0,1); + if (data != NULL) { + // downsample our data + int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; + int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min); + start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start; + int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000; + end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; + int size = (end - start)/downsample; + // plot it + ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, sizeof(double)*downsample); } + // plot time now + double t_now = (double)time(0); + double y_now = HugeTimeData::GetY(t_now); + ImPlot::PlotScatter("Now",&t_now,&y_now,1); + ImPlot::Annotation(t_now,y_now,ImPlot::GetLastItemColor(),ImVec2(10,10),false,"Now"); + ImPlot::EndPlot(); } - if (ImGui::CollapsingHeader("Time Formatted Axes")) { +} - static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) - static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) +void ShowDemo_MultipleAxes() { + static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; + for (int i = 0; i < 1001; ++i) { + xs[i] = (i*0.1f); + xs2[i] = xs[i] + 10.0f; + ys1[i] = sinf(xs[i]) * 3 + 1; + ys2[i] = cosf(xs[i]) * 0.2f + 0.5f; + ys3[i] = sinf(xs[i]+0.5f) * 100 + 200; + } - ImGui::BulletText("When ImPlotAxisFlags_Time is enabled on the X-Axis, values are interpreted as\n" - "UNIX timestamps in seconds and axis labels are formated as date/time."); - ImGui::BulletText("By default, labels are in UTC time but can be set to use local time instead."); + static bool x2_axis = true; + static bool y2_axis = true; + static bool y3_axis = true; - ImGui::Checkbox("Local Time",&ImPlot::GetStyle().UseLocalTime); - ImGui::SameLine(); - ImGui::Checkbox("ISO 8601",&ImPlot::GetStyle().UseISO8601); - ImGui::SameLine(); - ImGui::Checkbox("24 Hour Clock",&ImPlot::GetStyle().Use24HourClock); + ImGui::Checkbox("X-Axis 2", &x2_axis); + ImGui::SameLine(); + ImGui::Checkbox("Y-Axis 2", &y2_axis); + ImGui::SameLine(); + ImGui::Checkbox("Y-Axis 3", &y3_axis); - static HugeTimeData* data = NULL; - if (data == NULL) { - ImGui::SameLine(); - if (ImGui::Button("Generate Huge Data (~500MB!)")) { - static HugeTimeData sdata(t_min); - data = &sdata; - } - } + ImGui::BulletText("You can drag axes to the opposite side of the plot."); + ImGui::BulletText("Hover over legend items to see which axis they are plotted on."); - ImPlot::SetNextPlotLimits(t_min,t_max,0,1); - if (ImPlot::BeginPlot("##Time", NULL, NULL, ImVec2(-1,0), 0, ImPlotAxisFlags_Time)) { - if (data != NULL) { - // downsample our data - int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; - int start = (int)(ImPlot::GetPlotLimits().X.Min - t_min); - start = start < 0 ? 0 : start > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : start; - int end = (int)(ImPlot::GetPlotLimits().X.Max - t_min) + 1000; - end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; - int size = (end - start)/downsample; - // plot it - ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, sizeof(double)*downsample); - } - // plot time now - double t_now = (double)time(0); - double y_now = HugeTimeData::GetY(t_now); - ImPlot::PlotScatter("Now",&t_now,&y_now,1); - ImPlot::Annotate(t_now,y_now,ImVec2(10,10),ImPlot::GetLastItemColor(),"Now"); - ImPlot::EndPlot(); + if (ImPlot::BeginPlot("Multi-Axis Plot", ImVec2(-1,0))) { + ImPlot::SetupAxes("X-Axis 1", "Y-Axis 1"); + ImPlot::SetupAxesLimits(0, 100, 0, 10); + if (x2_axis) { + ImPlot::SetupAxis(ImAxis_X2, "X-Axis 2",ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxisLimits(ImAxis_X2, 0, 100); } - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Multiple Y-Axes")) { - static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; - for (int i = 0; i < 1001; ++i) { - xs[i] = (i*0.1f); - ys1[i] = sinf(xs[i]) * 3 + 1; - ys2[i] = cosf(xs[i]) * 0.2f + 0.5f; - ys3[i] = sinf(xs[i]+0.5f) * 100 + 200; - xs2[i] = xs[i] + 10.0f; + if (y2_axis) { + ImPlot::SetupAxis(ImAxis_Y2, "Y-Axis 2",ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxisLimits(ImAxis_Y2, 0, 1); + } + if (y3_axis) { + ImPlot::SetupAxis(ImAxis_Y3, "Y-Axis 3",ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxisLimits(ImAxis_Y3, 0, 300); } - static bool y2_axis = true; - static bool y3_axis = true; - ImGui::Checkbox("Y-Axis 2", &y2_axis); - ImGui::SameLine(); - ImGui::Checkbox("Y-Axis 3", &y3_axis); - ImGui::SameLine(); - - // you can fit axes programatically - ImGui::SameLine(); if (ImGui::Button("Fit X")) ImPlot::FitNextPlotAxes(true, false, false, false); - ImGui::SameLine(); if (ImGui::Button("Fit Y")) ImPlot::FitNextPlotAxes(false, true, false, false); - ImGui::SameLine(); if (ImGui::Button("Fit Y2")) ImPlot::FitNextPlotAxes(false, false, true, false); - ImGui::SameLine(); if (ImGui::Button("Fit Y3")) ImPlot::FitNextPlotAxes(false, false, false, true); - ImPlot::SetNextPlotLimits(0.1, 100, 0, 10); - ImPlot::SetNextPlotLimitsY(0, 1, ImGuiCond_Once, 1); - ImPlot::SetNextPlotLimitsY(0, 300, ImGuiCond_Once, 2); - if (ImPlot::BeginPlot("Multi-Axis Plot", NULL, "Y-Axis 1", ImVec2(-1,0), - (y2_axis ? ImPlotFlags_YAxis2 : 0) | - (y3_axis ? ImPlotFlags_YAxis3 : 0), - ImPlotAxisFlags_None, ImPlotAxisFlags_None, ImPlotAxisFlags_NoGridLines, ImPlotAxisFlags_NoGridLines, - "Y-Axis 2", "Y-Axis 3")) { - ImPlot::PlotLine("f(x) = x", xs, xs, 1001); - ImPlot::PlotLine("f(x) = sin(x)*3+1", xs, ys1, 1001); - if (y2_axis) { - ImPlot::SetPlotYAxis(ImPlotYAxis_2); - ImPlot::PlotLine("f(x) = cos(x)*.2+.5 (Y2)", xs, ys2, 1001); - } - if (y3_axis) { - ImPlot::SetPlotYAxis(ImPlotYAxis_3); - ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 (Y3)", xs2, ys3, 1001); - } - ImPlot::EndPlot(); + ImPlot::PlotLine("f(x) = x", xs, xs, 1001); + if (x2_axis) { + ImPlot::SetAxes(ImAxis_X2, ImAxis_Y1); + ImPlot::PlotLine("f(x) = sin(x)*3+1", xs2, ys1, 1001); + } + if (y2_axis) { + ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); + ImPlot::PlotLine("f(x) = cos(x)*.2+.5", xs, ys2, 1001); } + if (y3_axis) { + ImPlot::SetAxes(ImAxis_X2, ImAxis_Y3); + ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 ", xs2, ys3, 1001); + } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Linked Axes")) { - static double xmin = 0, xmax = 1, ymin = 0, ymax = 1; - static bool linkx = true, linky = true; - int data[2] = {0,1}; - ImGui::Checkbox("Link X", &linkx); - ImGui::SameLine(); - ImGui::Checkbox("Link Y", &linky); - ImPlot::LinkNextPlotLimits(linkx ? &xmin : NULL , linkx ? &xmax : NULL, linky ? &ymin : NULL, linky ? &ymax : NULL); +} + +void ShowDemo_LinkedAxes() { + static ImPlotRect lims(0,1,0,1); + static bool linkx = true, linky = true; + int data[2] = {0,1}; + ImGui::Checkbox("Link X", &linkx); + ImGui::SameLine(); + ImGui::Checkbox("Link Y", &linky); + + ImGui::DragScalarN("Limits",ImGuiDataType_Double,&lims.X.Min,4,0.01f); + + if (BeginAlignedPlots("AlignedGroup")) { if (ImPlot::BeginPlot("Plot A")) { + ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : NULL, linkx ? &lims.X.Max : NULL); + ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : NULL, linky ? &lims.Y.Max : NULL); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } - ImPlot::LinkNextPlotLimits(linkx ? &xmin : NULL , linkx ? &xmax : NULL, linky ? &ymin : NULL, linky ? &ymax : NULL); if (ImPlot::BeginPlot("Plot B")) { + ImPlot::SetupAxisLinks(ImAxis_X1, linkx ? &lims.X.Min : NULL, linkx ? &lims.X.Max : NULL); + ImPlot::SetupAxisLinks(ImAxis_Y1, linky ? &lims.Y.Min : NULL, linky ? &lims.Y.Max : NULL); ImPlot::PlotLine("Line",data,2); ImPlot::EndPlot(); } + ImPlot::EndAlignedPlots(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Equal Axes")) { - static double xs[1000], ys[1000]; - for (int i = 0; i < 1000; ++i) { - double angle = i * 2 * PI / 999.0; - xs[i] = cos(angle); ys[i] = sin(angle); - } - if (ImPlot::BeginPlot("",0,0,ImVec2(-1,0),ImPlotFlags_Equal)) { - ImPlot::PlotLine("Circle",xs,ys,1000); - ImPlot::EndPlot(); - } +} + +void ShowDemo_EqualAxes() { + ImGui::BulletText("Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2"); + static double xs1[360], ys1[360]; + for (int i = 0; i < 360; ++i) { + double angle = i * 2 * PI / 359.0; + xs1[i] = cos(angle); ys1[i] = sin(angle); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Auto-Fitting Data")) { + float xs2[] = {-1,0,1,0,-1}; + float ys2[] = {0,1,0,-1,0}; + if (ImPlot::BeginPlot("##EqualAxes",ImVec2(-1,0),ImPlotFlags_Equal)) { + ImPlot::SetupAxis(ImAxis_X2, NULL, ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxis(ImAxis_Y2, NULL, ImPlotAxisFlags_AuxDefault); + ImPlot::PlotLine("Circle",xs1,ys1,360); + ImPlot::SetAxes(ImAxis_X2, ImAxis_Y2); + ImPlot::PlotLine("Diamond",xs2,ys2,5); + ImPlot::EndPlot(); + } +} - ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); - ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); - ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); +void ShowDemo_AutoFittingData() { + ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); + ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); + ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); - static ImPlotAxisFlags xflags = ImPlotAxisFlags_None; - static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit; + static ImPlotAxisFlags xflags = ImPlotAxisFlags_None; + static ImPlotAxisFlags yflags = ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit; - ImGui::TextUnformatted("X: "); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit); + ImGui::TextUnformatted("X: "); ImGui::SameLine(); + ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); + ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##X", (unsigned int*)&xflags, ImPlotAxisFlags_RangeFit); - ImGui::TextUnformatted("Y: "); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); - ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit); + ImGui::TextUnformatted("Y: "); ImGui::SameLine(); + ImGui::CheckboxFlags("ImPlotAxisFlags_AutoFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_AutoFit); ImGui::SameLine(); + ImGui::CheckboxFlags("ImPlotAxisFlags_RangeFit##Y", (unsigned int*)&yflags, ImPlotAxisFlags_RangeFit); - static double data[101]; - srand(0); - for (int i = 0; i < 101; ++i) - data[i] = 1 + sin(i/10.0f); + static double data[101]; + srand(0); + for (int i = 0; i < 101; ++i) + data[i] = 1 + sin(i/10.0f); - if (ImPlot::BeginPlot("##DataFitting","X","Y",ImVec2(-1,0),0,xflags,yflags)) { - ImPlot::PlotLine("Line",data,101); - ImPlot::PlotStems("Stems",data,101); - ImPlot::EndPlot(); - }; - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Querying")) { - static ImVector<ImPlotPoint> data; - static ImPlotLimits range, query, select; + if (ImPlot::BeginPlot("##DataFitting")) { + ImPlot::SetupAxes("X","Y",xflags,yflags); + ImPlot::PlotLine("Line",data,101); + ImPlot::PlotStems("Stems",data,101); + ImPlot::EndPlot(); + }; +} + +ImPlotPoint SinewaveGetter(void* data, int i) { + float f = *(float*)data; + return ImPlotPoint(i,sinf(f*i)); +} - ImGui::BulletText("Ctrl + click in the plot area to draw points."); - ImGui::BulletText("Middle click (or Ctrl + right click) and drag to create a query rect."); - ImGui::Indent(); - ImGui::BulletText("Hold Alt to expand query horizontally."); - ImGui::BulletText("Hold Shift to expand query vertically."); - ImGui::BulletText("The query rect can be dragged after it's created."); - ImGui::Unindent(); +void ShowDemo_SubplotsSizing() { - if (ImPlot::BeginPlot("##Drawing", NULL, NULL, ImVec2(-1,0), ImPlotFlags_Query, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { - if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { - ImPlotPoint pt = ImPlot::GetPlotMousePos(); - data.push_back(pt); + static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None; + ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); + ImGui::CheckboxFlags("ImPlotSubplotFlags_NoTitle", (unsigned int*)&flags, ImPlotSubplotFlags_NoTitle); + + static int rows = 3; + static int cols = 3; + ImGui::SliderInt("Rows",&rows,1,5); + ImGui::SliderInt("Cols",&cols,1,5); + static float rratios[] = {5,1,1,1,1,1}; + static float cratios[] = {5,1,1,1,1,1}; + ImGui::DragScalarN("Row Ratios",ImGuiDataType_Float,rratios,rows,0.01f,0); + ImGui::DragScalarN("Col Ratios",ImGuiDataType_Float,cratios,cols,0.01f,0); + if (ImPlot::BeginSubplots("My Subplots", rows, cols, ImVec2(-1,400), flags, rratios, cratios)) { + for (int i = 0; i < rows*cols; ++i) { + if (ImPlot::BeginPlot("",ImVec2(),ImPlotFlags_NoLegend)) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); + float fi = 0.01f * (i+1); + ImPlot::SetNextLineStyle(SampleColormap((float)i/(float)(rows*cols-1),ImPlotColormap_Jet)); + ImPlot::PlotLineG("data",SinewaveGetter,&fi,1000); + ImPlot::EndPlot(); } - if (data.size() > 0) - ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 2 * sizeof(double)); - if (ImPlot::IsPlotQueried() && data.size() > 0) { - ImPlotLimits range2 = ImPlot::GetPlotQuery(); - int cnt = 0; - ImPlotPoint avg; - for (int i = 0; i < data.size(); ++i) { - if (range2.Contains(data[i].x, data[i].y)) { - avg.x += data[i].x; - avg.y += data[i].y; - cnt++; + } + ImPlot::EndSubplots(); + } +} + +void ShowDemo_SubplotItemSharing() { + static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems; + ImGui::CheckboxFlags("ImPlotSubplotFlags_ShareItems", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems); + ImGui::CheckboxFlags("ImPlotSubplotFlags_ColMajor", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor); + ImGui::BulletText("Drag and drop items from the legend onto plots (except for 'common')"); + static int rows = 2; + static int cols = 3; + static int id[] = {0,1,2,3,4,5}; + static int curj = -1; + if (ImPlot::BeginSubplots("##ItemSharing", rows, cols, ImVec2(-1,400), flags)) { + for (int i = 0; i < rows*cols; ++i) { + if (ImPlot::BeginPlot("")) { + float fc = 0.01f; + ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); + for (int j = 0; j < 6; ++j) { + if (id[j] == i) { + char label[8]; + float fj = 0.01f * (j+2); + sprintf(label, "data%d", j); + ImPlot::PlotLineG(label,SinewaveGetter,&fj,1000); + if (ImPlot::BeginDragDropSourceItem(label)) { + curj = j; + ImGui::SetDragDropPayload("MY_DND",NULL,0); + ImPlot::ItemIcon(GetLastItemColor()); ImGui::SameLine(); + ImGui::TextUnformatted(label); + ImPlot::EndDragDropSource(); + } } } - if (cnt > 0) { - avg.x = avg.x / cnt; - avg.y = avg.y / cnt; - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); - ImPlot::PlotScatter("Average", &avg.x, &avg.y, 1); + if (ImPlot::BeginDragDropTargetPlot()) { + if (ImGui::AcceptDragDropPayload("MY_DND")) + id[curj] = i; + ImPlot::EndDragDropTarget(); } + ImPlot::EndPlot(); } - range = ImPlot::GetPlotLimits(); - query = ImPlot::GetPlotQuery(); - select = ImPlot::GetPlotSelection(); - ImPlot::EndPlot(); } - ImGui::Text("Limits: [%g,%g,%g,%g]", range.X.Min, range.X.Max, range.Y.Min, range.Y.Max); - ImGui::Text("Query: [%g,%g,%g,%g]", query.X.Min, query.X.Max, query.Y.Min, query.Y.Max); - ImGui::Text("Selection: [%g,%g,%g,%g]", select.X.Min, select.X.Max, select.Y.Min, select.Y.Max); + ImPlot::EndSubplots(); + } +} - ImGui::Separator(); +void ShowDemo_SubplotAxisLinking() { + static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols; + ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkRows", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows); + ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkCols", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols); + ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllX", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllX); + ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkAllY", (unsigned int*)&flags, ImPlotSubplotFlags_LinkAllY); - // mimic's soulthread's imgui_plot demo - static float x_data[512]; - static float y_data1[512]; - static float y_data2[512]; - static float y_data3[512]; - static float sampling_freq = 44100; - static float freq = 500; - for (size_t i = 0; i < 512; ++i) { - const float t = i / sampling_freq; - x_data[i] = t; - const float arg = 2 * 3.14f * freq * t; - y_data1[i] = sinf(arg); - y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f; - y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; + static int rows = 2; + static int cols = 2; + if (ImPlot::BeginSubplots("##AxisLinking", rows, cols, ImVec2(-1,400), flags)) { + for (int i = 0; i < rows*cols; ++i) { + if (ImPlot::BeginPlot("")) { + ImPlot::SetupAxesLimits(0,1000,-1,1); + float fc = 0.01f; + ImPlot::PlotLineG("common",SinewaveGetter,&fc,1000); + ImPlot::EndPlot(); + } } - ImGui::BulletText("Query the first plot to render a subview in the second plot (see above for controls)."); - ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; - static bool use_selection = false; - ImGui::Checkbox("Use Box Selection",&use_selection); - bool is_viewed = false; - ImPlotLimits view; - ImPlot::SetNextPlotLimits(0,0.01,-1,1); - if (ImPlot::BeginPlot("##View1",NULL,NULL,ImVec2(-1,150), ImPlotFlags_Query, flags, flags)) { - ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); - ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); - ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); - is_viewed = use_selection ? ImPlot::IsPlotSelected() : ImPlot::IsPlotQueried(); - view = use_selection ? ImPlot::GetPlotSelection() : ImPlot::GetPlotQuery(); - ImPlot::EndPlot(); + ImPlot::EndSubplots(); + } +} + + +void ShowDemo_LegendOptions() { + static ImPlotLocation loc = ImPlotLocation_East; + static bool h = false; static bool o = true; + ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); + ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); + ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); + ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); ImGui::SameLine(); + ImGui::Checkbox("Horizontal##2", &h); ImGui::SameLine(); + ImGui::Checkbox("Outside", &o); + + ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); + ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); + + if (ImPlot::BeginPlot("##Legend",ImVec2(-1,0))) { + ImPlotLegendFlags flags = ImPlotLegendFlags_None; + if (h) flags |= ImPlotLegendFlags_Horizontal; + if (o) flags |= ImPlotLegendFlags_Outside; + ImPlot::SetupLegend(loc, flags); + static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); + static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); + static MyImPlot::WaveData data3(0.001, 0.2, 6, 0.5); + ImPlot::PlotLineG("Item 1", MyImPlot::SineWave, &data1, 1000); // "Item 1" added to legend + ImPlot::PlotLineG("Item 2##IDText", MyImPlot::SawWave, &data2, 1000); // "Item 2" added to legend, text after ## used for ID only + ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend + ImPlot::PlotLineG("Item 3", MyImPlot::SineWave, &data1, 1000); // "Item 3" added to legend + ImPlot::PlotLineG("Item 3", MyImPlot::SawWave, &data2, 1000); // combined with previous "Item 3" + ImPlot::EndPlot(); + } +} + +void ShowDemo_DragPoints() { + ImGui::BulletText("Click and drag each point."); + static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; + ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); + ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); + ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); + ImPlotAxisFlags ax_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; + if (ImPlot::BeginPlot("##Bezier",ImVec2(-1,0),ImPlotFlags_CanvasOnly)) { + ImPlot::SetupAxes(0,0,ax_flags,ax_flags); + ImPlot::SetupAxesLimits(0,1,0,1); + static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; + + ImPlot::DragPoint(0,&P[0].x,&P[0].y, ImVec4(0,0.9f,0,1),4,flags); + ImPlot::DragPoint(1,&P[1].x,&P[1].y, ImVec4(1,0.5f,1,1),4,flags); + ImPlot::DragPoint(2,&P[2].x,&P[2].y, ImVec4(0,0.5f,1,1),4,flags); + ImPlot::DragPoint(3,&P[3].x,&P[3].y, ImVec4(0,0.9f,0,1),4,flags); + + static ImPlotPoint B[100]; + for (int i = 0; i < 100; ++i) { + double t = i / 99.0; + double u = 1 - t; + double w1 = u*u*u; + double w2 = 3*u*u*t; + double w3 = 3*u*t*t; + double w4 = t*t*t; + B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); } - ImPlot::SetNextPlotLimits(view.X.Min, view.X.Max, view.Y.Min, view.Y.Max, ImGuiCond_Always); - if (ImPlot::BeginPlot("##View2",NULL,NULL,ImVec2(-1,150), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { - if (is_viewed) { - ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); - ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); - ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); - } - ImPlot::EndPlot(); + + + ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); + ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); + ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); + ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, sizeof(ImPlotPoint)); + + ImPlot::EndPlot(); + } +} + +void ShowDemo_DragLines() { + ImGui::BulletText("Click and drag the horizontal and vertical lines."); + static double x1 = 0.2; + static double x2 = 0.8; + static double y1 = 0.25; + static double y2 = 0.75; + static double f = 0.1; + static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; + ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); + ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); + ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); + if (ImPlot::BeginPlot("##lines",ImVec2(-1,0))) { + ImPlot::SetupAxesLimits(0,1,0,1); + ImPlot::DragLineX(0,&x1,ImVec4(1,1,1,1),1,flags); + ImPlot::DragLineX(1,&x2,ImVec4(1,1,1,1),1,flags); + ImPlot::DragLineY(2,&y1,ImVec4(1,1,1,1),1,flags); + ImPlot::DragLineY(3,&y2,ImVec4(1,1,1,1),1,flags); + double xs[1000], ys[1000]; + for (int i = 0; i < 1000; ++i) { + xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); + ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); } + ImPlot::PlotLine("Interactive Data", xs, ys, 1000); + ImPlot::DragLineY(120482,&f,ImVec4(1,0.5f,1,1),1,flags); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Legend")) { - static ImPlotLocation loc = ImPlotLocation_East; - static bool h = false; static bool o = true; - ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); - ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); - ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); - ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); ImGui::SameLine(); - ImGui::Checkbox("Horizontal##2", &h); ImGui::SameLine(); - ImGui::Checkbox("Outside", &o); +} - ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f"); +void ShowDemo_DragRects() { - if (ImPlot::BeginPlot("##Legend","x","y",ImVec2(-1,0))) { - ImPlot::SetLegendLocation(loc, h ? ImPlotOrientation_Horizontal : ImPlotOrientation_Vertical, o); - static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); - static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); - static MyImPlot::WaveData data3(0.001, 0.2, 6, 0.5); - ImPlot::PlotLineG("Item 1", MyImPlot::SineWave, &data1, 1000); // "Item 1" added to legend - ImPlot::PlotLineG("Item 2##IDText", MyImPlot::SawWave, &data2, 1000); // "Item 2" added to legend, text after ## used for ID only - ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend - ImPlot::PlotLineG("Item 3", MyImPlot::SineWave, &data1, 1000); // "Item 3" added to legend - ImPlot::PlotLineG("Item 3", MyImPlot::SawWave, &data2, 1000); // combined with previous "Item 3" - ImPlot::EndPlot(); + static float x_data[512]; + static float y_data1[512]; + static float y_data2[512]; + static float y_data3[512]; + static float sampling_freq = 44100; + static float freq = 500; + for (size_t i = 0; i < 512; ++i) { + const float t = i / sampling_freq; + x_data[i] = t; + const float arg = 2 * 3.14f * freq * t; + y_data1[i] = sinf(arg); + y_data2[i] = y_data1[i] * -0.6f + sinf(2 * arg) * 0.4f; + y_data3[i] = y_data2[i] * -0.6f + sinf(3 * arg) * 0.4f; + } + ImGui::BulletText("Click and drag the edges, corners, and center of the rect."); + static ImPlotRect rect(0.0025,0.0045,0,0.5); + static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; + ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); + ImGui::CheckboxFlags("NoFit", (unsigned int*)&flags, ImPlotDragToolFlags_NoFit); ImGui::SameLine(); + ImGui::CheckboxFlags("NoInput", (unsigned int*)&flags, ImPlotDragToolFlags_NoInputs); + + if (ImPlot::BeginPlot("##Main",ImVec2(-1,150))) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoTickLabels,ImPlotAxisFlags_NoTickLabels); + ImPlot::SetupAxesLimits(0,0.01,-1,1); + ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); + ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); + ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); + ImPlot::DragRect(0,&rect.X.Min,&rect.Y.Min,&rect.X.Max,&rect.Y.Max,ImVec4(1,0,1,1),flags); + ImPlot::EndPlot(); + } + if (ImPlot::BeginPlot("##rect",ImVec2(-1,150), ImPlotFlags_CanvasOnly)) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(rect.X.Min, rect.X.Max, rect.Y.Min, rect.Y.Max, ImGuiCond_Always); + ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); + ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); + ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); + ImPlot::EndPlot(); + } +} + +ImPlotPoint FindCentroid(const ImVector<ImPlotPoint>& data, ImPlotRect& bounds, int& cnt) { + cnt = 0; + ImPlotPoint avg; + for (int i = 0; i < data.size(); ++i) { + if (bounds.Contains(data[i].x, data[i].y)) { + avg.x += data[i].x; + avg.y += data[i].y; + cnt++; } } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Drag Lines and Points")) { - ImGui::BulletText("Click and drag the horizontal and vertical lines."); - static double x1 = 0.2; - static double x2 = 0.8; - static double y1 = 0.25; - static double y2 = 0.75; - static double f = 0.1; - static bool show_labels = true; - ImGui::Checkbox("Show Labels##1",&show_labels); - ImPlot::SetNextPlotLimits(0,1,0,1); - if (ImPlot::BeginPlot("##guides",0,0,ImVec2(-1,0),ImPlotFlags_YAxis2)) { - ImPlot::DragLineX("x1",&x1,show_labels); - ImPlot::DragLineX("x2",&x2,show_labels); - ImPlot::DragLineY("y1",&y1,show_labels); - ImPlot::DragLineY("y2",&y2,show_labels); - double xs[1000], ys[1000]; - for (int i = 0; i < 1000; ++i) { - xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); - ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); + if (cnt > 0) { + avg.x = avg.x / cnt; + avg.y = avg.y / cnt; + } + return avg; +} + +void ShowDemo_Querying() { + static ImVector<ImPlotPoint> data; + static ImVector<ImPlotRect> rects; + static ImPlotRect limits, select; + static bool init = true; + if (init) { + for (int i = 0; i < 50; ++i) + { + double x = RandomRange(0.1, 0.9); + double y = RandomRange(0.1, 0.9); + data.push_back(ImPlotPoint(x,y)); + } + init = false; + } + + ImGui::BulletText("Box select and left click mouse to create a new query rect."); + ImGui::BulletText("Ctrl + click in the plot area to draw points."); + + if (ImGui::Button("Clear Queries")) + rects.shrink(0); + + if (ImPlot::BeginPlot("##Centroid")) { + ImPlot::SetupAxesLimits(0,1,0,1); + if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { + ImPlotPoint pt = ImPlot::GetPlotMousePos(); + data.push_back(pt); + } + ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 2 * sizeof(double)); + if (ImPlot::IsPlotSelected()) { + select = ImPlot::GetPlotSelection(); + int cnt; + ImPlotPoint centroid = FindCentroid(data,select,cnt); + if (cnt > 0) { + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); + ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); + } + if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) { + CancelPlotSelection(); + rects.push_back(select); } - ImPlot::PlotLine("Interactive Data", xs, ys, 1000); - ImPlot::SetPlotYAxis(ImPlotYAxis_2); - ImPlot::DragLineY("f",&f,show_labels,ImVec4(1,0.5f,1,1)); - ImPlot::EndPlot(); } - ImGui::BulletText("Click and drag any point."); - ImGui::Checkbox("Show Labels##2",&show_labels); - ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks; - ImPlot::SetNextPlotLimits(0,1,0,1); - if (ImPlot::BeginPlot("##Bezier",0,0,ImVec2(-1,0),ImPlotFlags_CanvasOnly,flags,flags)) { - static ImPlotPoint P[] = {ImPlotPoint(.05f,.05f), ImPlotPoint(0.2,0.4), ImPlotPoint(0.8,0.6), ImPlotPoint(.95f,.95f)}; - static ImPlotPoint B[100]; - for (int i = 0; i < 100; ++i) { - double t = i / 99.0; - double u = 1 - t; - double w1 = u*u*u; - double w2 = 3*u*u*t; - double w3 = 3*u*t*t; - double w4 = t*t*t; - B[i] = ImPlotPoint(w1*P[0].x + w2*P[1].x + w3*P[2].x + w4*P[3].x, w1*P[0].y + w2*P[1].y + w3*P[2].y + w4*P[3].y); + for (int i = 0; i < rects.size(); ++i) { + int cnt; + ImPlotPoint centroid = FindCentroid(data,rects[i],cnt); + if (cnt > 0) { + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,6); + ImPlot::PlotScatter("Centroid", ¢roid.x, ¢roid.y, 1); } - ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); - ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); - ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, sizeof(ImPlotPoint)); - ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); - ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, sizeof(ImPlotPoint)); - ImPlot::DragPoint("P0",&P[0].x,&P[0].y, show_labels, ImVec4(0,0.9f,0,1)); - ImPlot::DragPoint("P1",&P[1].x,&P[1].y, show_labels, ImVec4(1,0.5f,1,1)); - ImPlot::DragPoint("P2",&P[2].x,&P[2].y, show_labels, ImVec4(0,0.5f,1,1)); - ImPlot::DragPoint("P3",&P[3].x,&P[3].y, show_labels, ImVec4(0,0.9f,0,1)); - ImPlot::EndPlot(); + ImPlot::DragRect(i,&rects[i].X.Min,&rects[i].Y.Min,&rects[i].X.Max,&rects[i].Y.Max,ImVec4(1,0,1,1)); } + limits = ImPlot::GetPlotLimits(); + ImPlot::EndPlot(); } - if (ImGui::CollapsingHeader("Annotations")) { - static bool clamp = false; - ImGui::Checkbox("Clamp",&clamp); - ImPlot::SetNextPlotLimits(0,2,0,1); - if (ImPlot::BeginPlot("##Annotations")) { +} - static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; - ImPlot::PlotScatter("##Points",&p[0],&p[1],4); - ImVec4 col = GetLastItemColor(); - clamp ? ImPlot::AnnotateClamped(0.25,0.25,ImVec2(-15,15),col,"BL") : ImPlot::Annotate(0.25,0.25,ImVec2(-15,15),col,"BL"); - clamp ? ImPlot::AnnotateClamped(0.75,0.25,ImVec2(15,15),col,"BR") : ImPlot::Annotate(0.75,0.25,ImVec2(15,15),col,"BR"); - clamp ? ImPlot::AnnotateClamped(0.75,0.75,ImVec2(15,-15),col,"TR") : ImPlot::Annotate(0.75,0.75,ImVec2(15,-15),col,"TR"); - clamp ? ImPlot::AnnotateClamped(0.25,0.75,ImVec2(-15,-15),col,"TL") : ImPlot::Annotate(0.25,0.75,ImVec2(-15,-15),col,"TL"); - clamp ? ImPlot::AnnotateClamped(0.5,0.5,ImVec2(0,0),col,"Center") : ImPlot::Annotate(0.5,0.5,ImVec2(0,0),col,"Center"); +void ShowDemo_Annotations() { + static bool clamp = false; + ImGui::Checkbox("Clamp",&clamp); + if (ImPlot::BeginPlot("##Annotations")) { + ImPlot::SetupAxesLimits(0,2,0,1); + static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; + ImPlot::PlotScatter("##Points",&p[0],&p[1],4); + ImVec4 col = GetLastItemColor(); + ImPlot::Annotation(0.25,0.25,col,ImVec2(-15,15),clamp,"BL"); + ImPlot::Annotation(0.75,0.25,col,ImVec2(15,15),clamp,"BR"); + ImPlot::Annotation(0.75,0.75,col,ImVec2(15,-15),clamp,"TR"); + ImPlot::Annotation(0.25,0.75,col,ImVec2(-15,-15),clamp,"TL"); + ImPlot::Annotation(0.5,0.5,col,ImVec2(0,0),clamp,"Center"); - float bx[] = {1.2f,1.5f,1.8f}; - float by[] = {0.25f, 0.5f, 0.75f}; - ImPlot::PlotBars("##Bars",bx,by,3,0.2); - for (int i = 0; i < 3; ++i) - ImPlot::Annotate(bx[i],by[i],ImVec2(0,-5),"B[%d]=%.2f",i,by[i]); - ImPlot::EndPlot(); + ImPlot::Annotation(1.25,0.75,ImVec4(0,1,0,1),ImVec2(0,0),clamp); + + float bx[] = {1.2f,1.5f,1.8f}; + float by[] = {0.25f, 0.5f, 0.75f}; + ImPlot::PlotBars("##Bars",bx,by,3,0.2); + for (int i = 0; i < 3; ++i) + ImPlot::Annotation(bx[i],by[i],ImVec4(0,0,0,0),ImVec2(0,-5),clamp,"B[%d]=%.2f",i,by[i]); + ImPlot::EndPlot(); + } +} + +void ShowDemo_Tags() { + static bool show = true; + ImGui::Checkbox("Show Tags",&show); + if (ImPlot::BeginPlot("##Tags")) { + ImPlot::SetupAxis(ImAxis_X2); + ImPlot::SetupAxis(ImAxis_Y2); + if (show) { + ImPlot::TagX(0.25, ImVec4(1,1,0,1)); + ImPlot::TagY(0.75, ImVec4(1,1,0,1)); + static double drag_tag = 0.25; + ImPlot::DragLineY(0,&drag_tag,ImVec4(1,0,0,1),1,ImPlotDragToolFlags_NoFit); + ImPlot::TagY(drag_tag, ImVec4(1,0,0,1), "Drag"); + SetAxes(ImAxis_X2, ImAxis_Y2); + ImPlot::TagX(0.5, ImVec4(0,1,1,1), "%s", "MyTag"); + ImPlot::TagY(0.5, ImVec4(0,1,1,1), "Tag: %d", 42); } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Drag and Drop")) { - ImGui::BulletText("Drag/drop items from the left column."); - ImGui::BulletText("Drag/drop items between plots."); - ImGui::Indent(); - ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); - ImGui::BulletText("Plot 1 Sources: Legend Items"); - ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); - ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); - ImGui::Unindent(); +} - // convenience struct to manage DND items; do this however you like - struct MyDndItem { - int Idx; - int Plt; - int Yax; - char Label[16]; - ImVector<ImVec2> Data; - ImVec4 Color; - MyDndItem() { - static int i = 0; - Idx = i++; - Plt = 0; - Yax = ImPlotYAxis_1; - sprintf(Label, "%02d Hz", Idx+1); - Color = RandomColor(); - Data.reserve(1001); - for (int k = 0; k < 1001; ++k) { - float t = k * 1.0f / 999; - Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); - } +void ShowDemo_DragAndDrop() { + ImGui::BulletText("Drag/drop items from the left column."); + ImGui::BulletText("Drag/drop items between plots."); + ImGui::Indent(); + ImGui::BulletText("Plot 1 Targets: Plot, Y-Axes, Legend"); + ImGui::BulletText("Plot 1 Sources: Legend Item Labels"); + ImGui::BulletText("Plot 2 Targets: Plot, X-Axis, Y-Axis"); + ImGui::BulletText("Plot 2 Sources: Plot, X-Axis, Y-Axis (hold Ctrl)"); + ImGui::Unindent(); + + // convenience struct to manage DND items; do this however you like + struct MyDndItem { + int Idx; + int Plt; + ImAxis Yax; + char Label[16]; + ImVector<ImVec2> Data; + ImVec4 Color; + MyDndItem() { + static int i = 0; + Idx = i++; + Plt = 0; + Yax = ImAxis_Y1; + sprintf(Label, "%02d Hz", Idx+1); + Color = RandomColor(); + Data.reserve(1001); + for (int k = 0; k < 1001; ++k) { + float t = k * 1.0f / 999; + Data.push_back(ImVec2(t, 0.5f + 0.5f * sinf(2*3.14f*t*(Idx+1)))); } - void Reset() { Plt = 0; Yax = ImPlotYAxis_1; } - }; + } + void Reset() { Plt = 0; Yax = ImAxis_Y1; } + }; - const int k_dnd = 20; - static MyDndItem dnd[k_dnd]; - static MyDndItem* dndx = NULL; // for plot 2 - static MyDndItem* dndy = NULL; // for plot 2 + const int k_dnd = 20; + static MyDndItem dnd[k_dnd]; + static MyDndItem* dndx = NULL; // for plot 2 + static MyDndItem* dndy = NULL; // for plot 2 - // child window to serve as initial source for our DND items - ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); - if (ImGui::Button("Reset Data", ImVec2(100, 0))) { - for (int k = 0; k < k_dnd; ++k) - dnd[k].Reset(); - dndx = dndy = NULL; - } - for (int k = 0; k < k_dnd; ++k) { - if (dnd[k].Plt > 0) - continue; + // child window to serve as initial source for our DND items + ImGui::BeginChild("DND_LEFT",ImVec2(100,400)); + if (ImGui::Button("Reset Data")) { + for (int k = 0; k < k_dnd; ++k) + dnd[k].Reset(); + dndx = dndy = NULL; + } + for (int k = 0; k < k_dnd; ++k) { + if (dnd[k].Plt > 0) + continue; + ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); + ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::Selectable(dnd[k].Label, false, 0, ImVec2(100, 0)); - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); - ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::TextUnformatted(dnd[k].Label); - ImGui::EndDragDropSource(); - } + ImGui::TextUnformatted(dnd[k].Label); + ImGui::EndDragDropSource(); } - ImGui::EndChild(); - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Reset(); - } - ImGui::EndDragDropTarget(); + } + ImGui::EndChild(); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Reset(); } + ImGui::EndDragDropTarget(); + } - ImGui::SameLine(); - ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); - // plot 1 (time series) - ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines; - if (ImPlot::BeginPlot("##DND1", NULL, "[drop here]", ImVec2(-1,195), ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3, flags | ImPlotAxisFlags_Lock, flags, flags, flags, "[drop here]", "[drop here]")) { - for (int k = 0; k < k_dnd; ++k) { - if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { - ImPlot::SetPlotYAxis(dnd[k].Yax); - ImPlot::SetNextLineStyle(dnd[k].Color); - static char label[32]; - sprintf(label,"%s (Y%d)", dnd[k].Label, dnd[k].Yax+1); - ImPlot::PlotLine(label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 2 * sizeof(float)); - // allow legend item labels to be DND sources - if (ImPlot::BeginDragDropSourceItem(label)) { - ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); - ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); - ImGui::TextUnformatted(dnd[k].Label); - ImPlot::EndDragDropSource(); - } - } - } - // allow the main plot area to be a DND target - if (ImPlot::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = 0; - } - ImPlot::EndDragDropTarget(); - } - // allow each y-axis to be a DND target - for (int y = 0; y < 3; ++y) { - if (ImPlot::BeginDragDropTargetY(y)) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; - } - ImPlot::EndDragDropTarget(); + ImGui::SameLine(); + ImGui::BeginChild("DND_RIGHT",ImVec2(-1,400)); + // plot 1 (time series) + ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines; + if (ImPlot::BeginPlot("##DND1", ImVec2(-1,195))) { + ImPlot::SetupAxis(ImAxis_X1, NULL, flags|ImPlotAxisFlags_Lock); + ImPlot::SetupAxis(ImAxis_Y1, "[drop here]", flags); + ImPlot::SetupAxis(ImAxis_Y2, "[drop here]", flags|ImPlotAxisFlags_Opposite); + ImPlot::SetupAxis(ImAxis_Y3, "[drop here]", flags|ImPlotAxisFlags_Opposite); + + for (int k = 0; k < k_dnd; ++k) { + if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { + ImPlot::SetAxis(dnd[k].Yax); + ImPlot::SetNextLineStyle(dnd[k].Color); + ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 2 * sizeof(float)); + // allow legend item labels to be DND sources + if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) { + ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); + ImPlot::ItemIcon(dnd[k].Color); ImGui::SameLine(); + ImGui::TextUnformatted(dnd[k].Label); + ImPlot::EndDragDropSource(); } } - // allow the legend to be a DND target - if (ImPlot::BeginDragDropTargetLegend()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = 0; - } - ImPlot::EndDragDropTarget(); - } - ImPlot::EndPlot(); } - // plot 2 (Lissajous) - ImPlot::PushStyleColor(ImPlotCol_XAxis, dndx == NULL ? ImPlot::GetStyle().Colors[ImPlotCol_XAxis] : dndx->Color); - ImPlot::PushStyleColor(ImPlotCol_YAxis, dndy == NULL ? ImPlot::GetStyle().Colors[ImPlotCol_YAxis] : dndy->Color); - if (ImPlot::BeginPlot("##DND2", dndx == NULL ? "[drop here]" : dndx->Label, dndy == NULL ? "[drop here]" : dndy->Label, ImVec2(-1,195), 0, flags, flags )) { - if (dndx != NULL && dndy != NULL) { - ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); - ImPlot::SetNextLineStyle(mixed); - ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 2 * sizeof(float)); + // allow the main plot area to be a DND target + if (ImPlot::BeginDragDropTargetPlot()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } - // allow the x-axis to be a DND target - if (ImPlot::BeginDragDropTargetX()) { + ImPlot::EndDragDropTarget(); + } + // allow each y-axis to be a DND target + for (int y = ImAxis_Y1; y <= ImAxis_Y3; ++y) { + if (ImPlot::BeginDragDropTargetAxis(y)) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndx = &dnd[i]; + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = y; } ImPlot::EndDragDropTarget(); } - // allow the x-axis to be a DND source - if (dndx != NULL && ImPlot::BeginDragDropSourceX()) { - ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); - ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); - ImGui::TextUnformatted(dndx->Label); - ImPlot::EndDragDropSource(); - } - // allow the y-axis to be a DND target - if (ImPlot::BeginDragDropTargetY()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndy = &dnd[i]; - } - ImPlot::EndDragDropTarget(); + } + // allow the legend to be a DND target + if (ImPlot::BeginDragDropTargetLegend()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dnd[i].Plt = 1; dnd[i].Yax = ImAxis_Y1; } - // allow the y-axis to be a DND source - if (dndy != NULL && ImPlot::BeginDragDropSourceY()) { - ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); - ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); - ImGui::TextUnformatted(dndy->Label); - ImPlot::EndDragDropSource(); + ImPlot::EndDragDropTarget(); + } + ImPlot::EndPlot(); + } + // plot 2 (Lissajous) + if (ImPlot::BeginPlot("##DND2", ImVec2(-1,195))) { + ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndx != NULL ? dndx->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); + ImPlot::SetupAxis(ImAxis_X1, dndx == NULL ? "[drop here]" : dndx->Label, flags); + ImPlot::PushStyleColor(ImPlotCol_AxisBg, dndy != NULL ? dndy->Color : ImPlot::GetStyle().Colors[ImPlotCol_AxisBg]); + ImPlot::SetupAxis(ImAxis_Y1, dndy == NULL ? "[drop here]" : dndy->Label, flags); + ImPlot::PopStyleColor(2); + if (dndx != NULL && dndy != NULL) { + ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); + ImPlot::SetNextLineStyle(mixed); + ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 2 * sizeof(float)); + } + // allow the x-axis to be a DND target + if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndx = &dnd[i]; } - // allow the plot area to be a DND target - if (ImPlot::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { - int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; - } + ImPlot::EndDragDropTarget(); + } + // allow the x-axis to be a DND source + if (dndx != NULL && ImPlot::BeginDragDropSourceAxis(ImAxis_X1)) { + ImGui::SetDragDropPayload("MY_DND", &dndx->Idx, sizeof(int)); + ImPlot::ItemIcon(dndx->Color); ImGui::SameLine(); + ImGui::TextUnformatted(dndx->Label); + ImPlot::EndDragDropSource(); + } + // allow the y-axis to be a DND target + if (ImPlot::BeginDragDropTargetAxis(ImAxis_Y1)) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndy = &dnd[i]; } - // allow the plot area to be a DND source - if (ImPlot::BeginDragDropSource()) { - ImGui::TextUnformatted("Yes, you can\ndrag this!"); - ImPlot::EndDragDropSource(); + ImPlot::EndDragDropTarget(); + } + // allow the y-axis to be a DND source + if (dndy != NULL && ImPlot::BeginDragDropSourceAxis(ImAxis_Y1)) { + ImGui::SetDragDropPayload("MY_DND", &dndy->Idx, sizeof(int)); + ImPlot::ItemIcon(dndy->Color); ImGui::SameLine(); + ImGui::TextUnformatted(dndy->Label); + ImPlot::EndDragDropSource(); + } + // allow the plot area to be a DND target + if (ImPlot::BeginDragDropTargetPlot()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_DND")) { + int i = *(int*)payload->Data; dndx = dndy = &dnd[i]; } - ImPlot::EndPlot(); } - ImPlot::PopStyleColor(2); - ImGui::EndChild(); + // allow the plot area to be a DND source + if (ImPlot::BeginDragDropSourcePlot()) { + ImGui::TextUnformatted("Yes, you can\ndrag this!"); + ImPlot::EndDragDropSource(); + } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Tables")) { + ImGui::EndChild(); +} + +void ShowDemo_Tables() { #ifdef IMGUI_HAS_TABLE - static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_RowBg; - static bool anim = true; - static int offset = 0; - ImGui::BulletText("Plots can be used inside of ImGui tables."); - ImGui::Checkbox("Animate",&anim); - if (anim) - offset = (offset + 1) % 100; - if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) { - ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f); - ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f); - ImGui::TableSetupColumn("EMG Signal"); - ImGui::TableHeadersRow(); - ImPlot::PushColormap(ImPlotColormap_Cool); - for (int row = 0; row < 10; row++) { - ImGui::TableNextRow(); - static float data[100]; - srand(row); - for (int i = 0; i < 100; ++i) - data[i] = RandomRange(0.0f,10.0f); - ImGui::TableSetColumnIndex(0); - ImGui::Text("EMG %d", row); - ImGui::TableSetColumnIndex(1); - ImGui::Text("%.3f V", data[offset]); - ImGui::TableSetColumnIndex(2); - ImGui::PushID(row); - MyImPlot::Sparkline("##spark",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35)); - ImGui::PopID(); - } - ImPlot::PopColormap(); - ImGui::EndTable(); + static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | + ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable; + static bool anim = true; + static int offset = 0; + ImGui::BulletText("Plots can be used inside of ImGui tables as another means of creating subplots."); + ImGui::Checkbox("Animate",&anim); + if (anim) + offset = (offset + 1) % 100; + if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) { + ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f); + ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f); + ImGui::TableSetupColumn("EMG Signal"); + ImGui::TableHeadersRow(); + ImPlot::PushColormap(ImPlotColormap_Cool); + for (int row = 0; row < 10; row++) { + ImGui::TableNextRow(); + static float data[100]; + srand(row); + for (int i = 0; i < 100; ++i) + data[i] = RandomRange(0.0f,10.0f); + ImGui::TableSetColumnIndex(0); + ImGui::Text("EMG %d", row); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%.3f V", data[offset]); + ImGui::TableSetColumnIndex(2); + ImGui::PushID(row); + MyImPlot::Sparkline("##spark",data,100,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35)); + ImGui::PopID(); } + ImPlot::PopColormap(); + ImGui::EndTable(); + } #else ImGui::BulletText("You need to merge the ImGui 'tables' branch for this section."); #endif - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Offset and Stride")) { - static const int k_circles = 11; - static const int k_points_per = 50; - static const int k_size = 2 * k_points_per * k_circles; - static double interleaved_data[k_size]; - for (int p = 0; p < k_points_per; ++p) { - for (int c = 0; c < k_circles; ++c) { - double r = (double)c / (k_circles - 1) * 0.2 + 0.2; - interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28); - interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28); - } +} + +void ShowDemo_OffsetAndStride() { + static const int k_circles = 11; + static const int k_points_per = 50; + static const int k_size = 2 * k_points_per * k_circles; + static double interleaved_data[k_size]; + for (int p = 0; p < k_points_per; ++p) { + for (int c = 0; c < k_circles; ++c) { + double r = (double)c / (k_circles - 1) * 0.2 + 0.2; + interleaved_data[p*2*k_circles + 2*c + 0] = 0.5 + r * cos((double)p/k_points_per * 6.28); + interleaved_data[p*2*k_circles + 2*c + 1] = 0.5 + r * sin((double)p/k_points_per * 6.28); } - static int offset = 0; - ImGui::BulletText("Offsetting is useful for realtime plots (see above) and circular buffers."); - ImGui::BulletText("Striding is useful for interleaved data (e.g. audio) or plotting structs."); - ImGui::BulletText("Here, all circle data is stored in a single interleaved buffer:"); - ImGui::BulletText("[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]"); - ImGui::BulletText("The offset value indicates which circle point index is considered the first."); - ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); - ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); - if (ImPlot::BeginPlot("##strideoffset",0,0,ImVec2(-1,0), ImPlotFlags_Equal)) { - ImPlot::PushColormap(ImPlotColormap_Jet); - char buff[16]; - for (int c = 0; c < k_circles; ++c) { - sprintf(buff, "Circle %d", c); - ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, offset, 2*k_circles*sizeof(double)); - } - ImPlot::EndPlot(); - ImPlot::PopColormap(); + } + static int offset = 0; + ImGui::BulletText("Offsetting is useful for realtime plots (see above) and circular buffers."); + ImGui::BulletText("Striding is useful for interleaved data (e.g. audio) or plotting structs."); + ImGui::BulletText("Here, all circle data is stored in a single interleaved buffer:"); + ImGui::BulletText("[c0.x0 c0.y0 ... cn.x0 cn.y0 c0.x1 c0.y1 ... cn.x1 cn.y1 ... cn.xm cn.ym]"); + ImGui::BulletText("The offset value indicates which circle point index is considered the first."); + ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); + ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); + if (ImPlot::BeginPlot("##strideoffset",ImVec2(-1,0),ImPlotFlags_Equal)) { + ImPlot::PushColormap(ImPlotColormap_Jet); + char buff[16]; + for (int c = 0; c < k_circles; ++c) { + sprintf(buff, "Circle %d", c); + ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, offset, 2*k_circles*sizeof(double)); } - // offset++; uncomment for animation! + ImPlot::EndPlot(); + ImPlot::PopColormap(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Data and Getters")) { - ImGui::BulletText("You can plot custom structs using the stride feature."); - ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); - ImGui::Indent(); - ImGui::BulletText("You can optionally pass user data to be given to your getter function."); - ImGui::BulletText("C++ lambdas can be passed as function pointers as well!"); - ImGui::Unindent(); + // offset++; uncomment for animation! +} - MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) }; +void ShowDemo_CustomDataAndGetters() { + ImGui::BulletText("You can plot custom structs using the stride feature."); + ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); + ImGui::Indent(); + ImGui::BulletText("You can optionally pass user data to be given to your getter function."); + ImGui::BulletText("C++ lambdas can be passed as function pointers as well!"); + ImGui::Unindent(); - if (ImPlot::BeginPlot("##Custom Data")) { + MyImPlot::Vector2f vec2_data[2] = { MyImPlot::Vector2f(0,0), MyImPlot::Vector2f(1,1) }; - // custom structs using stride example: - ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); + if (ImPlot::BeginPlot("##Custom Data")) { - // custom getter example 1: - ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, NULL, 1000); + // custom structs using stride example: + ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); - // custom getter example 2: - static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); - static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); - ImPlot::PlotLineG("Waves", MyImPlot::SineWave, &data1, 1000); - ImPlot::PlotLineG("Waves", MyImPlot::SawWave, &data2, 1000); - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShadedG("Waves", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000); - ImPlot::PopStyleVar(); + // custom getter example 1: + ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, NULL, 1000); + + // custom getter example 2: + static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75); + static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25); + ImPlot::PlotLineG("Waves", MyImPlot::SineWave, &data1, 1000); + ImPlot::PlotLineG("Waves", MyImPlot::SawWave, &data2, 1000); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::PlotShadedG("Waves", MyImPlot::SineWave, &data1, MyImPlot::SawWave, &data2, 1000); + ImPlot::PopStyleVar(); - // you can also pass C++ lambdas: - // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); }; - // ImPlot::PlotLine("My Lambda", lambda, data, 1000); + // you can also pass C++ lambdas: + // auto lamda = [](void* data, int idx) { ... return ImPlotPoint(x,y); }; + // ImPlot::PlotLine("My Lambda", lambda, data, 1000); - ImPlot::EndPlot(); + ImPlot::EndPlot(); + } +} + +void MetricFormatter(double value, char* buff, int size, void* data) { + const char* unit = (const char*)data; + static double v[] = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001}; + static const char* p[] = {"G","M","k","","m","u","n"}; + if (value == 0) { + snprintf(buff,size,"0 %s", unit); + return; + } + for (int i = 0; i < 7; ++i) { + if (fabs(value) >= v[i]) { + snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); + return; } } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Ticks##")) { - static bool custom_fmt = true; - static bool custom_ticks = false; - static bool custom_labels = true; - ImGui::Checkbox("Show Custom Format", &custom_fmt); + snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); +} + +void ShowDemo_TickLabels() { + static bool custom_fmt = true; + static bool custom_ticks = false; + static bool custom_labels = true; + ImGui::Checkbox("Show Custom Format", &custom_fmt); + ImGui::SameLine(); + ImGui::Checkbox("Show Custom Ticks", &custom_ticks); + if (custom_ticks) { ImGui::SameLine(); - ImGui::Checkbox("Show Custom Ticks", &custom_ticks); - if (custom_ticks) { - ImGui::SameLine(); - ImGui::Checkbox("Show Custom Labels", &custom_labels); - } - double pi = 3.14; - const char* pi_str[] = {"PI"}; - static double yticks[] = {1,3,7,9}; - static const char* ylabels[] = {"One","Three","Seven","Nine"}; - static double yticks_aux[] = {0.2,0.4,0.6}; - static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; + ImGui::Checkbox("Show Custom Labels", &custom_labels); + } + const double pi = 3.14; + const char* pi_str[] = {"PI"}; + static double yticks[] = {100,300,700,900}; + static const char* ylabels[] = {"One","Three","Seven","Nine"}; + static double yticks_aux[] = {0.2,0.4,0.6}; + static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; + + if (ImPlot::BeginPlot("##Ticks")) { + ImPlot::SetupAxesLimits(2.5,5,0,1000); + ImPlot::SetupAxis(ImAxis_Y2, NULL, ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxis(ImAxis_Y3, NULL, ImPlotAxisFlags_AuxDefault); if (custom_fmt) { - ImPlot::SetNextPlotFormatX("%g ms"); - ImPlot::SetNextPlotFormatY("%g Hz", ImPlotYAxis_1); - ImPlot::SetNextPlotFormatY("%g dB", ImPlotYAxis_2); - ImPlot::SetNextPlotFormatY("%g km", ImPlotYAxis_3); + ImPlot::SetupAxisFormat(ImAxis_X1, "%g ms"); + ImPlot::SetupAxisFormat(ImAxis_Y1, MetricFormatter, (void*)"Hz"); + ImPlot::SetupAxisFormat(ImAxis_Y2, "%g dB"); + ImPlot::SetupAxisFormat(ImAxis_Y3, MetricFormatter, (void*)"m"); } if (custom_ticks) { - ImPlot::SetNextPlotTicksX(&pi,1,custom_labels ? pi_str : NULL, true); - ImPlot::SetNextPlotTicksY(yticks, 4, custom_labels ? ylabels : NULL, ImPlotYAxis_1); - ImPlot::SetNextPlotTicksY(yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false, ImPlotYAxis_2); - ImPlot::SetNextPlotTicksY(0, 1, 6, custom_labels ? ylabels_aux : NULL, false, ImPlotYAxis_3); - } - ImPlot::SetNextPlotLimits(2.5,5,0,10); - if (ImPlot::BeginPlot("Custom Ticks", NULL, NULL, ImVec2(-1,0), ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3)) { - // nothing to see here, just the ticks - ImPlot::EndPlot(); + ImPlot::SetupAxisTicks(ImAxis_X1, &pi,1,custom_labels ? pi_str : NULL, true); + ImPlot::SetupAxisTicks(ImAxis_Y1, yticks, 4, custom_labels ? ylabels : NULL, false); + ImPlot::SetupAxisTicks(ImAxis_Y2, yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false); + ImPlot::SetupAxisTicks(ImAxis_Y3, 0, 1, 6, custom_labels ? ylabels_aux : NULL, false); } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Styles")) { - ImPlot::PushColormap(ImPlotColormap_Deep); - // normally you wouldn't change the entire style each frame - ImPlotStyle backup = ImPlot::GetStyle(); - MyImPlot::StyleSeaborn(); - ImPlot::SetNextPlotLimits(-0.5f, 9.5f, 0, 10); - if (ImPlot::BeginPlot("seaborn style", "x-axis", "y-axis")) { - unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8}; - unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4}; - unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7}; - ImPlot::PlotBars("Bars", bar, 10, 0.5f); - ImPlot::PlotLine("Line", lin, 10); - ImPlot::NextColormapColor(); // skip green - ImPlot::PlotScatter("Scatter", dot, 10); - ImPlot::EndPlot(); - } - ImPlot::GetStyle() = backup; - ImPlot::PopColormap(); +} + +void ShowDemo_CustomStyles() { + ImPlot::PushColormap(ImPlotColormap_Deep); + // normally you wouldn't change the entire style each frame + ImPlotStyle backup = ImPlot::GetStyle(); + MyImPlot::StyleSeaborn(); + if (ImPlot::BeginPlot("seaborn style")) { + ImPlot::SetupAxes( "x-axis", "y-axis"); + ImPlot::SetupAxesLimits(-0.5f, 9.5f, 0, 10); + unsigned int lin[10] = {8,8,9,7,8,8,8,9,7,8}; + unsigned int bar[10] = {1,2,5,3,4,1,2,5,3,4}; + unsigned int dot[10] = {7,6,6,7,8,5,6,5,8,7}; + ImPlot::PlotBars("Bars", bar, 10, 0.5f); + ImPlot::PlotLine("Line", lin, 10); + ImPlot::NextColormapColor(); // skip green + ImPlot::PlotScatter("Scatter", dot, 10); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Rendering")) { - if (ImPlot::BeginPlot("##CustomRend")) { - ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); - ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); - ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f)); - ImPlot::PushPlotClipRect(); - ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20); - ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255)); - ImPlot::PopPlotClipRect(); - ImPlot::EndPlot(); - } + ImPlot::GetStyle() = backup; + ImPlot::PopColormap(); +} + +void ShowDemo_CustomRendering() { + if (ImPlot::BeginPlot("##CustomRend")) { + ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); + ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); + ImVec2 rmax = ImPlot::PlotToPixels(ImPlotPoint(0.75f, 0.25f)); + ImPlot::PushPlotClipRect(); + ImPlot::GetPlotDrawList()->AddCircleFilled(cntr,20,IM_COL32(255,255,0,255),20); + ImPlot::GetPlotDrawList()->AddRect(rmin, rmax, IM_COL32(128,0,255,255)); + ImPlot::PopPlotClipRect(); + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Context Menus")) { - ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); - ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); +} - static float frequency = 0.1f; - static float amplitude = 0.5f; - static ImVec4 color = ImVec4(1,1,0,1); - static float alpha = 1.0f; - static bool line = false; - static float thickness = 1; - static bool markers = false; - static bool shaded = false; +void ShowDemo_LegendPopups() { + ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); + ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); - static float vals[101]; - for (int i = 0; i < 101; ++i) - vals[i] = amplitude * sinf(frequency * i); + static float frequency = 0.1f; + static float amplitude = 0.5f; + static ImVec4 color = ImVec4(1,1,0,1); + static float alpha = 1.0f; + static bool line = false; + static float thickness = 1; + static bool markers = false; + static bool shaded = false; - ImPlot::SetNextPlotLimits(0,100,-1,1); - if (ImPlot::BeginPlot("Right Click the Legend")) { - // rendering logic - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); - if (!line) { - ImPlot::SetNextFillStyle(color); - ImPlot::PlotBars("Right Click Me", vals, 101); - } - else { - if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::SetNextLineStyle(color, thickness); - ImPlot::PlotLine("Right Click Me", vals, 101); - if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); - } - ImPlot::PopStyleVar(); - // custom legend context menu - if (ImPlot::BeginLegendPopup("Right Click Me")) { - ImGui::SliderFloat("Frequency",&frequency,0,1,"%0.2f"); - ImGui::SliderFloat("Amplitude",&litude,0,1,"%0.2f"); - ImGui::Separator(); - ImGui::ColorEdit3("Color",&color.x); - ImGui::SliderFloat("Transparency",&alpha,0,1,"%.2f"); - ImGui::Checkbox("Line Plot", &line); - if (line) { - ImGui::SliderFloat("Thickness", &thickness, 0, 5); - ImGui::Checkbox("Markers", &markers); - ImGui::Checkbox("Shaded",&shaded); - } - ImPlot::EndLegendPopup(); + static float vals[101]; + for (int i = 0; i < 101; ++i) + vals[i] = amplitude * sinf(frequency * i); + + if (ImPlot::BeginPlot("Right Click the Legend")) { + ImPlot::SetupAxesLimits(0,100,-1,1); + // rendering logic + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); + if (!line) { + ImPlot::SetNextFillStyle(color); + ImPlot::PlotBars("Right Click Me", vals, 101); + } + else { + if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::SetNextLineStyle(color, thickness); + ImPlot::PlotLine("Right Click Me", vals, 101); + if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); + } + ImPlot::PopStyleVar(); + // custom legend context menu + if (ImPlot::BeginLegendPopup("Right Click Me")) { + ImGui::SliderFloat("Frequency",&frequency,0,1,"%0.2f"); + ImGui::SliderFloat("Amplitude",&litude,0,1,"%0.2f"); + ImGui::Separator(); + ImGui::ColorEdit3("Color",&color.x); + ImGui::SliderFloat("Transparency",&alpha,0,1,"%.2f"); + ImGui::Checkbox("Line Plot", &line); + if (line) { + ImGui::SliderFloat("Thickness", &thickness, 0, 5); + ImGui::Checkbox("Markers", &markers); + ImGui::Checkbox("Shaded",&shaded); } - ImPlot::EndPlot(); + ImPlot::EndLegendPopup(); } + ImPlot::EndPlot(); } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Custom Plotters and Tooltips")) { - ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); - double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; - double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; - double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9}; - double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1}; - double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3}; - static bool tooltip = true; - ImGui::Checkbox("Show Tooltip", &tooltip); - ImGui::SameLine(); - static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f); - static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f); - ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs); - ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); - ImPlot::GetStyle().UseLocalTime = false; - ImPlot::SetNextPlotFormatY("$%.0f"); - ImPlot::SetNextPlotLimits(1546300800, 1571961600, 1250, 1600); - if (ImPlot::BeginPlot("Candlestick Chart",NULL,NULL,ImVec2(-1,0),0,ImPlotAxisFlags_Time,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit)) { - MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); - ImPlot::EndPlot(); +} + +void ShowDemo_ColormapTools() { + static int cmap = 0; + if (ImPlot::ColormapButton("Colormap Button",ImVec2(0,0),cmap)) { + cmap = (cmap + 1) % ImPlot::GetColormapCount(); + } + ImPlot::ColormapIcon(cmap); ImGui::SameLine(); ImGui::Text("Colormap Icon"); + ImPlot::ColormapScale("Colormap Scale",0,1,ImVec2(0,0),cmap); +} + +void ShowDemo_CustomPlottersAndTooltips() { + ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); + double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; + double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; + double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9}; + double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1}; + double closes[] = {1283.35,1315.3,1326.1,1317.4,1321.5,1317.4,1323.5,1319.2,1321.3,1323.3,1319.7,1325.1,1323.6,1313.8,1282.05,1279.05,1314.2,1315.2,1310.8,1329.1,1334.5,1340.2,1340.5,1350,1347.1,1344.3,1344.6,1339.7,1339.4,1343.7,1337,1338.9,1340.1,1338.7,1346.8,1324.25,1329.55,1369.6,1372.5,1352.4,1357.6,1354.2,1353.4,1346,1341,1323.8,1311.9,1309.1,1312.2,1310.7,1324.3,1315.7,1322.4,1333.8,1319.4,1327.1,1325.8,1330.9,1325.8,1331.6,1336.5,1346.7,1339.2,1334.7,1313.3,1316.5,1312.4,1313.4,1313.3,1312.2,1313.7,1319.9,1326.3,1331.9,1311.3,1313.4,1309.4,1295.2,1294.7,1294.1,1277.9,1295.8,1291.2,1297.4,1297.7,1306.8,1299.4,1303.6,1302.2,1289.9,1299.2,1301.8,1303.6,1299.5,1303.2,1305.3,1319.5,1313.6,1315.1,1303.5,1293,1294.6,1290.4,1291.4,1302.7,1301,1284.15,1284.95,1294.3,1297.9,1304.1,1322.6,1339.3,1340.1,1344.9,1354,1357.4,1340.7,1342.7,1348.2,1355.1,1355.9,1354.2,1362.1,1360.1,1408.3,1411.2,1429.5,1430.1,1426.8,1423.4,1425.1,1400.8,1419.8,1432.9,1423.55,1412.1,1412.2,1412.8,1424.9,1419.3,1424.8,1426.1,1423.6,1435.9,1440.8,1439.4,1439.7,1434.5,1436.5,1427.5,1432.2,1433.3,1441.8,1437.8,1432.4,1457.5,1476.5,1484.2,1519.6,1509.5,1508.5,1517.2,1514.1,1527.8,1531.2,1523.6,1511.6,1515.7,1515.7,1508.5,1537.6,1537.2,1551.8,1549.1,1536.9,1529.4,1538.05,1535.15,1555.9,1560.4,1525.5,1515.5,1511.1,1499.2,1503.2,1507.4,1499.5,1511.5,1513.4,1515.8,1506.2,1515.1,1531.5,1540.2,1512.3,1515.2,1506.4,1472.9,1489,1507.9,1513.8,1512.9,1504.4,1503.9,1512.8,1500.9,1488.7,1497.6,1483.5,1494,1498.3,1494.1,1488.1,1487.5,1495.7,1504.7,1505.3}; + static bool tooltip = true; + ImGui::Checkbox("Show Tooltip", &tooltip); + ImGui::SameLine(); + static ImVec4 bullCol = ImVec4(0.000f, 1.000f, 0.441f, 1.000f); + static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f); + ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs); + ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); + ImPlot::GetStyle().UseLocalTime = false; + + if (ImPlot::BeginPlot("Candlestick Chart",ImVec2(-1,0))) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_Time,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); + ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600); + ImPlot::SetupAxisFormat(ImAxis_Y1, "$%.0f"); + MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); + ImPlot::EndPlot(); + } + } + +//----------------------------------------------------------------------------- +// DEMO WINDOW +//----------------------------------------------------------------------------- + +void ShowDemoWindow(bool* p_open) { + static bool show_imgui_metrics = false; + static bool show_implot_metrics = false; + static bool show_imgui_style_editor = false; + static bool show_implot_style_editor = false; + static bool show_implot_benchmark = false; + if (show_imgui_metrics) { + ImGui::ShowMetricsWindow(&show_imgui_metrics); + } + if (show_implot_metrics) { + ImPlot::ShowMetricsWindow(&show_implot_metrics); + } + if (show_imgui_style_editor) { + ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); + ImGui::ShowStyleEditor(); + ImGui::End(); + } + if (show_implot_style_editor) { + ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); + ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); + ImPlot::ShowStyleEditor(); + ImGui::End(); + } + if (show_implot_benchmark) { + ImGui::SetNextWindowSize(ImVec2(530,740), ImGuiCond_Appearing); + ImGui::Begin("ImPlot Benchmark Tool", &show_implot_benchmark); + ImPlot::ShowBenchmarkTool(); + ImGui::End(); + return; + } + ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); + ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Tools")) { + ImGui::MenuItem("Metrics (ImGui)", NULL, &show_imgui_metrics); + ImGui::MenuItem("Metrics (ImPlot)", NULL, &show_implot_metrics); + ImGui::MenuItem("Style Editor (ImGui)", NULL, &show_imgui_style_editor); + ImGui::MenuItem("Style Editor (ImPlot)", NULL, &show_implot_style_editor); + ImGui::MenuItem("Benchmark", NULL, &show_implot_benchmark); + ImGui::EndMenu(); } + ImGui::EndMenuBar(); } //------------------------------------------------------------------------- + ImGui::Text("ImPlot says hello. (%s)", IMPLOT_VERSION); + // display warning about 16-bit indices + static bool showWarning = sizeof(ImDrawIdx)*8 == 16 && (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) == false; + if (showWarning) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,1,0,1)); + ImGui::TextWrapped("WARNING: ImDrawIdx is 16-bit and ImGuiBackendFlags_RendererHasVtxOffset is false. Expect visual glitches and artifacts! See README for more information."); + ImGui::PopStyleColor(); + } + + ImGui::Spacing(); + + if (ImGui::BeginTabBar("ImPlotDemoTabs")) { + if (ImGui::BeginTabItem("Plots")) { + if (ImGui::CollapsingHeader("Line Plots")) + ShowDemo_LinePlots(); + if (ImGui::CollapsingHeader("Filled Line Plots")) + ShowDemo_FilledLinePlots(); + if (ImGui::CollapsingHeader("Shaded Plots##")) + ShowDemo_ShadedPlots(); + if (ImGui::CollapsingHeader("Scatter Plots")) + ShowDemo_ScatterPlots(); + if (ImGui::CollapsingHeader("Realtime Plots")) + ShowDemo_RealtimePlots(); + if (ImGui::CollapsingHeader("Stairstep Plots")) + ShowDemo_StairstepPlots(); + if (ImGui::CollapsingHeader("Bar Plots")) + ShowDemo_BarPlots(); + if (ImGui::CollapsingHeader("Bar Groups")) + ShowDemo_BarGroups(); + if (ImGui::CollapsingHeader("Bar Stacks")) + ShowDemo_BarStacks(); + if (ImGui::CollapsingHeader("Error Bars")) + ShowDemo_ErrorBars(); + if (ImGui::CollapsingHeader("Stem Plots##")) + ShowDemo_StemPlots(); + if (ImGui::CollapsingHeader("Infinite Lines")) + ShowDemo_InfiniteLines(); + if (ImGui::CollapsingHeader("Pie Charts")) + ShowDemo_PieCharts(); + if (ImGui::CollapsingHeader("Heatmaps")) + ShowDemo_Heatmaps(); + if (ImGui::CollapsingHeader("Histogram")) + ShowDemo_Histogram(); + if (ImGui::CollapsingHeader("Histogram 2D")) + ShowDemo_Histogram2D(); + if (ImGui::CollapsingHeader("Digital Plots")) + ShowDemo_DigitalPlots(); + if (ImGui::CollapsingHeader("Images")) + ShowDemo_Images(); + if (ImGui::CollapsingHeader("Markers and Text")) + ShowDemo_MarkersAndText(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Subplots")) { + if (ImGui::CollapsingHeader("Sizing")) + ShowDemo_SubplotsSizing(); + if (ImGui::CollapsingHeader("Item Sharing")) + ShowDemo_SubplotItemSharing(); + if (ImGui::CollapsingHeader("Axis Linking")) + ShowDemo_SubplotAxisLinking(); + if (ImGui::CollapsingHeader("Tables")) + ShowDemo_Tables(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Axes")) { + if (ImGui::CollapsingHeader("Log Axes")) + ShowDemo_LogAxes(); + if (ImGui::CollapsingHeader("Time Axes")) + ShowDemo_TimeAxes(); + if (ImGui::CollapsingHeader("Multiple Axes")) + ShowDemo_MultipleAxes(); + if (ImGui::CollapsingHeader("Tick Labels")) + ShowDemo_TickLabels(); + if (ImGui::CollapsingHeader("Linked Axes")) + ShowDemo_LinkedAxes(); + if (ImGui::CollapsingHeader("Equal Axes")) + ShowDemo_EqualAxes(); + if (ImGui::CollapsingHeader("Auto-Fitting Data")) + ShowDemo_AutoFittingData(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Tools")) { + if (ImGui::CollapsingHeader("Offset and Stride")) + ShowDemo_OffsetAndStride(); + if (ImGui::CollapsingHeader("Drag Points")) + ShowDemo_DragPoints(); + if (ImGui::CollapsingHeader("Drag Lines")) + ShowDemo_DragLines(); + if (ImGui::CollapsingHeader("Drag Rects")) + ShowDemo_DragRects(); + if (ImGui::CollapsingHeader("Querying")) + ShowDemo_Querying(); + if (ImGui::CollapsingHeader("Annotations")) + ShowDemo_Annotations(); + if (ImGui::CollapsingHeader("Tags")) + ShowDemo_Tags(); + if (ImGui::CollapsingHeader("Drag and Drop")) + ShowDemo_DragAndDrop(); + if (ImGui::CollapsingHeader("Legend Options")) + ShowDemo_LegendOptions(); + if (ImGui::CollapsingHeader("Legend Popups")) + ShowDemo_LegendPopups(); + if (ImGui::CollapsingHeader("Colormap Tools")) + ShowDemo_ColormapTools(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Custom")) { + if (ImGui::CollapsingHeader("Custom Styles")) + ShowDemo_CustomStyles(); + if (ImGui::CollapsingHeader("Custom Data and Getters")) + ShowDemo_CustomDataAndGetters(); + if (ImGui::CollapsingHeader("Custom Rendering")) + ShowDemo_CustomRendering(); + if (ImGui::CollapsingHeader("Custom Plotters and Tooltips")) + ShowDemo_CustomPlottersAndTooltips(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Config")) { + ShowDemo_Config(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Help")) { + ShowDemo_Help(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } ImGui::End(); } @@ -1622,11 +2138,11 @@ ImPlotPoint Spiral(void*, int idx) { 0.5f + (a + b*Th / (2.0f * (float)3.14))*sin(Th)); } -// Example for Tables section. Generates a quick and simple shaded line plot. See implementation at bottom. void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) { ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0)); - ImPlot::SetNextPlotLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); - if (ImPlot::BeginPlot(id,0,0,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { + if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly|ImPlotFlags_NoChild)) { + ImPlot::SetupAxes(0,0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); ImPlot::PushStyleColor(ImPlotCol_Line, col); ImPlot::PlotLine(id, values, count, 1, 0, offset); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); @@ -1656,16 +2172,11 @@ void StyleSeaborn() { colors[ImPlotCol_LegendText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_TitleText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImPlotCol_InlayText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_XAxis] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_XAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxis] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxis2] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid2] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImPlotCol_YAxis3] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImPlotCol_YAxisGrid3] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImPlotCol_AxisText] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImPlotCol_AxisGrid] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImPlotCol_AxisBgHovered] = ImVec4(0.92f, 0.92f, 0.95f, 1.00f); + colors[ImPlotCol_AxisBgActive] = ImVec4(0.92f, 0.92f, 0.95f, 0.75f); colors[ImPlotCol_Selection] = ImVec4(1.00f, 0.65f, 0.00f, 1.00f); - colors[ImPlotCol_Query] = ImVec4(0.23f, 0.10f, 0.64f, 1.00f); colors[ImPlotCol_Crosshairs] = ImVec4(0.23f, 0.10f, 0.64f, 0.50f); style.LineWeight = 1.5; @@ -1688,7 +2199,7 @@ void StyleSeaborn() { style.PlotPadding = ImVec2(12,12); style.LabelPadding = ImVec2(5,5); style.LegendPadding = ImVec2(5,5); - style.MousePosPadding = ImVec2(5,5); + style.MousePosPadding = ImVec2(5,5); style.PlotMinSize = ImVec2(300,225); } @@ -1804,9 +2315,10 @@ struct BenchData { enum BenchMode { Line = 0, - Shaded = 1, - Scatter = 2, - Bars = 3 + LineG = 1, + Shaded = 2, + Scatter = 3, + Bars = 4 }; struct BenchRecord { @@ -1815,6 +2327,11 @@ struct BenchRecord { ImVector<ImPlotPoint> Data; }; +ImPlotPoint BenchmarkGetter(void* data, int idx) { + float* values = (float*)data; + return ImPlotPoint(idx, values[idx]); +} + void ShowBenchmarkTool() { static const int max_items = 500; static BenchData items[max_items]; @@ -1824,7 +2341,7 @@ void ShowBenchmarkTool() { static int F = 0; static double t1, t2; static int mode = BenchMode::Line; - const char* names[] = {"Line","Shaded","Scatter","Bars"}; + const char* names[] = {"Line","LineG","Shaded","Scatter","Bars"}; static ImVector<BenchRecord> records; @@ -1873,8 +2390,9 @@ void ShowBenchmarkTool() { ImGui::ProgressBar((float)L / (float)(max_items - 1)); - ImPlot::SetNextPlotLimits(0,1000,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Bench",NULL,NULL,ImVec2(-1,0),ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { + if (ImPlot::BeginPlot("##Bench",ImVec2(-1,0),ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) { + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); + ImPlot::SetupAxesLimits(0,1000,0,1,ImGuiCond_Always); if (running) { if (mode == BenchMode::Line) { for (int i = 0; i < L; ++i) { @@ -1884,6 +2402,14 @@ void ShowBenchmarkTool() { ImGui::PopID(); } } + else if (mode == BenchMode::LineG) { + for (int i = 0; i < L; ++i) { + ImGui::PushID(i); + ImPlot::SetNextLineStyle(items[i].Col); + ImPlot::PlotLineG("##item",BenchmarkGetter,items[i].Data,1000); + ImGui::PopID(); + } + } else if (mode == BenchMode::Shaded) { for (int i = 0; i < L; ++i) { ImGui::PushID(i); @@ -1911,9 +2437,10 @@ void ShowBenchmarkTool() { } ImPlot::EndPlot(); } - ImPlot::SetNextPlotLimits(0,500,0,500,ImGuiCond_Always); static char buffer[64]; - if (ImPlot::BeginPlot("##Stats", "Items (1,000 pts each)", "Framerate (Hz)", ImVec2(-1,0), ImPlotFlags_NoChild)) { + if (ImPlot::BeginPlot("##Stats", ImVec2(-1,0), ImPlotFlags_NoChild)) { + ImPlot::SetupAxes("Items (1,000 pts each)", "Framerate (Hz)"); + ImPlot::SetupAxesLimits(0,500,0,500,ImGuiCond_Always); for (int run = 0; run < records.size(); ++run) { if (records[run].Data.Size > 1) { sprintf(buffer, "B%d-%s%s", run + 1, names[records[run].Mode], records[run].AA ? "-AA" : ""); diff --git a/3rdparty/implot/implot_internal.h b/3rdparty/implot/implot_internal.h index 3c0d1d5..7895afe 100644 --- a/3rdparty/implot/implot_internal.h +++ b/3rdparty/implot/implot_internal.h @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.10 WIP +// ImPlot v0.13 WIP // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! @@ -42,6 +42,12 @@ #error Must include implot.h before implot_internal.h #endif + +// Support for pre-1.84 versions. ImPool's GetSize() -> GetBufSize() +#if (IMGUI_VERSION_NUM < 18303) +#define GetBufSize GetSize +#endif + //----------------------------------------------------------------------------- // [SECTION] Constants //----------------------------------------------------------------------------- @@ -49,21 +55,24 @@ // Constants can be changed unless stated otherwise. We may move some of these // to ImPlotStyleVar_ over time. -// The maximum number of supported y-axes (DO NOT CHANGE THIS) -#define IMPLOT_Y_AXES 3 -// Zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click) -#define IMPLOT_ZOOM_RATE 0.1f // Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS) #define IMPLOT_MIN_TIME 0 // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 // Default label format for axis labels -#define IMPLOT_LABEL_FMT "%g" +#define IMPLOT_LABEL_FORMAT "%g" +// Max character size for tick labels +#define IMPLOT_LABEL_MAX_SIZE 32 +// Plot values less than or equal to 0 will be replaced with this on log scale axes +#define IMPLOT_LOG_ZERO DBL_MIN //----------------------------------------------------------------------------- // [SECTION] Macros //----------------------------------------------------------------------------- +#define IMPLOT_NUM_X_AXES ImAxis_Y1 +#define IMPLOT_NUM_Y_AXES (ImAxis_COUNT - IMPLOT_NUM_X_AXES) + // Split ImU32 color into RGB components [0 255] #define IM_COL32_SPLIT_RGB(col,r,g,b) \ ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \ @@ -78,7 +87,7 @@ struct ImPlotTick; struct ImPlotAxis; struct ImPlotAxisColor; struct ImPlotItem; -struct ImPlotLegendData; +struct ImPlotLegend; struct ImPlotPlot; struct ImPlotNextPlotData; @@ -86,7 +95,9 @@ struct ImPlotNextPlotData; // [SECTION] Context Pointer //----------------------------------------------------------------------------- +#ifndef GImPlot extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer +#endif //----------------------------------------------------------------------------- // [SECTION] Generic Helpers @@ -110,11 +121,11 @@ static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } // Returns always positive modulo (assumes r != 0) static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } // Returns true if val is NAN or INFINITY -static inline bool ImNanOrInf(double val) { return val == HUGE_VAL || val == -HUGE_VAL || isnan(val); } +static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || isnan(val); } // Turns NANs to 0s static inline double ImConstrainNan(double val) { return isnan(val) ? 0 : val; } // Turns infinity to floating point maximums -static inline double ImConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : val == -HUGE_VAL ? - DBL_MAX : val; } +static inline double ImConstrainInf(double val) { return val >= DBL_MAX ? DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; } // Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) static inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero @@ -137,6 +148,14 @@ static inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_ } *min_out = Min; *max_out = Max; } +// Finds the sim of an array +template <typename T> +static inline T ImSum(const T* values, int count) { + T sum = 0; + for (int i = 0; i < count; ++i) + sum += values[i]; + return sum; +} // Finds the mean of an array template <typename T> static inline double ImMean(const T* values, int count) { @@ -196,42 +215,6 @@ static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)<<IM_COL32_A_SHIFT); } -// Character buffer writer helper (FIXME: Can't we replace this with ImGuiTextBuffer?) -struct ImBufferWriter -{ - char* Buffer; - int Size; - int Pos; - - ImBufferWriter(char* buffer, int size) { - Buffer = buffer; - Size = size; - Pos = 0; - } - - void Write(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - WriteV(fmt, args); - va_end(args); - } - - void WriteV(const char* fmt, va_list args) { - const int written = ::vsnprintf(&Buffer[Pos], Size - Pos - 1, fmt, args); - if (written > 0) - Pos += ImMin(written, Size-Pos-1); - } -}; - -// Fixed size point array -template <int N> -struct ImPlotPointArray { - inline ImPlotPoint& operator[](int i) { return Data[i]; } - inline const ImPlotPoint& operator[](int i) const { return Data[i]; } - inline int Size() { return N; } - ImPlotPoint Data[N]; -}; - //----------------------------------------------------------------------------- // [SECTION] ImPlot Enums //----------------------------------------------------------------------------- @@ -282,23 +265,6 @@ enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] ImPlotTimeFmt_Hr // 7pm [ 19:00 ] }; -// Input mapping structure, default values listed in the comments. -struct ImPlotInputMap { - ImGuiMouseButton PanButton; // LMB enables panning when held - ImGuiKeyModFlags PanMod; // none optional modifier that must be held for panning - ImGuiMouseButton FitButton; // LMB fits visible data when double clicked - ImGuiMouseButton ContextMenuButton; // RMB opens plot context menu (if enabled) when clicked - ImGuiMouseButton BoxSelectButton; // RMB begins box selection when pressed and confirms selection when released - ImGuiKeyModFlags BoxSelectMod; // none optional modifier that must be held for box selection - ImGuiMouseButton BoxSelectCancelButton; // LMB cancels active box selection when pressed - ImGuiMouseButton QueryButton; // MMB begins query selection when pressed and end query selection when released - ImGuiKeyModFlags QueryMod; // none optional modifier that must be held for query selection - ImGuiKeyModFlags QueryToggleMod; // Ctrl when held, active box selections turn into queries - ImGuiKeyModFlags HorizontalMod; // Alt expands active box selection/query horizontally to plot edge when held - ImGuiKeyModFlags VerticalMod; // Shift expands active box selection/query vertically to plot edge when held - IMPLOT_API ImPlotInputMap(); -}; - //----------------------------------------------------------------------------- // [SECTION] ImPlot Structs //----------------------------------------------------------------------------- @@ -504,6 +470,54 @@ struct ImPlotAnnotationCollection { } }; +struct ImPlotTag { + ImAxis Axis; + double Value; + ImU32 ColorBg; + ImU32 ColorFg; + int TextOffset; +}; + +struct ImPlotTagCollection { + + ImVector<ImPlotTag> Tags; + ImGuiTextBuffer TextBuffer; + int Size; + + ImPlotTagCollection() { Reset(); } + + void AppendV(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, va_list args) IM_FMTLIST(6) { + ImPlotTag tag; + tag.Axis = axis; + tag.Value = value; + tag.ColorBg = bg; + tag.ColorFg = fg; + tag.TextOffset = TextBuffer.size(); + Tags.push_back(tag); + TextBuffer.appendfv(fmt, args); + const char nul[] = ""; + TextBuffer.append(nul,nul+1); + Size++; + } + + void Append(ImAxis axis, double value, ImU32 bg, ImU32 fg, const char* fmt, ...) IM_FMTARGS(6) { + va_list args; + va_start(args, fmt); + AppendV(axis, value, bg, fg, fmt, args); + va_end(args); + } + + const char* GetText(int idx) { + return TextBuffer.Buf.Data + Tags[idx].TextOffset; + } + + void Reset() { + Tags.shrink(0); + TextBuffer.Buf.shrink(0); + Size = 0; + } +}; + // Tick mark info struct ImPlotTick { @@ -528,34 +542,29 @@ struct ImPlotTick struct ImPlotTickCollection { ImVector<ImPlotTick> Ticks; ImGuiTextBuffer TextBuffer; - float TotalWidthMax; - float TotalWidth; - float TotalHeight; - float MaxWidth; - float MaxHeight; + ImVec2 MaxSize; + ImVec2 LateSize; int Size; ImPlotTickCollection() { Reset(); } const ImPlotTick& Append(const ImPlotTick& tick) { if (tick.ShowLabel) { - TotalWidth += tick.ShowLabel ? tick.LabelSize.x : 0; - TotalHeight += tick.ShowLabel ? tick.LabelSize.y : 0; - MaxWidth = tick.LabelSize.x > MaxWidth ? tick.LabelSize.x : MaxWidth; - MaxHeight = tick.LabelSize.y > MaxHeight ? tick.LabelSize.y : MaxHeight; + MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; + MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; } Ticks.push_back(tick); Size++; return Ticks.back(); } - const ImPlotTick& Append(double value, bool major, bool show_label, const char* fmt) { + const ImPlotTick& Append(double value, bool major, bool show_label, ImPlotFormatter formatter, void* data) { ImPlotTick tick(value, major, show_label); - if (show_label && fmt != NULL) { - char temp[32]; + if (show_label && formatter != NULL) { + char buff[IMPLOT_LABEL_MAX_SIZE]; tick.TextOffset = TextBuffer.size(); - snprintf(temp, 32, fmt, tick.PlotPos); - TextBuffer.append(temp, temp + strlen(temp) + 1); + formatter(tick.PlotPos, buff, sizeof(buff), data); + TextBuffer.append(buff, buff + strlen(buff) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } return Append(tick); @@ -565,10 +574,21 @@ struct ImPlotTickCollection { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } + void OverrideSize(const ImVec2& size) { + MaxSize.x = size.x > MaxSize.x ? size.x : MaxSize.x; + MaxSize.y = size.y > MaxSize.y ? size.y : MaxSize.y; + } + + void OverrideSizeLate(const ImVec2& size) { + LateSize.x = size.x > LateSize.x ? size.x : LateSize.x; + LateSize.y = size.y > LateSize.y ? size.y : LateSize.y; + } + void Reset() { Ticks.shrink(0); TextBuffer.Buf.shrink(0); - TotalWidth = TotalHeight = MaxWidth = MaxHeight = 0; + MaxSize = LateSize; + LateSize = ImVec2(0,0); Size = 0; } }; @@ -576,37 +596,71 @@ struct ImPlotTickCollection { // Axis state information that must persist after EndPlot struct ImPlotAxis { - ImPlotAxisFlags Flags; - ImPlotAxisFlags PreviousFlags; - ImPlotRange Range; - float Pixels; - ImPlotOrientation Orientation; - bool Dragging; - bool ExtHovered; - bool AllHovered; - bool Present; - bool HasRange; - double* LinkedMin; - double* LinkedMax; - ImPlotTime PickerTimeMin, PickerTimeMax; - int PickerLevel; - ImU32 ColorMaj, ColorMin, ColorTxt; - ImGuiCond RangeCond; - ImRect HoverRect; + ImGuiID ID; + ImPlotAxisFlags Flags; + ImPlotAxisFlags PreviousFlags; + ImPlotCond RangeCond; + ImPlotTickCollection Ticks; + ImPlotRange Range; + ImPlotRange FitExtents; + ImPlotAxis* OrthoAxis; + double* LinkedMin; + double* LinkedMax; + int PickerLevel; + ImPlotTime PickerTimeMin, PickerTimeMax; + float Datum1, Datum2; + float PixelMin, PixelMax; + double LinM, LogD; + ImRect HoverRect; + int LabelOffset; + ImU32 ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi; + char FormatSpec[16]; + ImPlotFormatter Formatter; + void* FormatterData; + bool Enabled; + bool Vertical; + bool FitThisFrame; + bool HasRange; + bool HasFormatSpec; + bool ShowDefaultTicks; + bool Hovered; + bool Held; ImPlotAxis() { - Flags = PreviousFlags = ImPlotAxisFlags_None; - Range.Min = 0; - Range.Max = 1; - Dragging = false; - ExtHovered = false; - AllHovered = false; - LinkedMin = LinkedMax = NULL; - PickerLevel = 0; - ColorMaj = ColorMin = ColorTxt = 0; + Flags = PreviousFlags = ImPlotAxisFlags_None; + Range.Min = 0; + Range.Max = 1; + FitExtents.Min = HUGE_VAL; + FitExtents.Max = -HUGE_VAL; + OrthoAxis = NULL; + LinkedMin = LinkedMax = NULL; + PickerLevel = 0; + Datum1 = Datum2 = 0; + PixelMin = PixelMax = 0; + LabelOffset = -1; + ColorMaj = ColorMin = ColorTick = ColorTxt = ColorBg = ColorHov = ColorAct = 0; + ColorHiLi = IM_COL32_BLACK_TRANS; + Formatter = NULL; + FormatterData = NULL; + Enabled = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false; + ShowDefaultTicks = true; } - bool SetMin(double _min, bool force=false) { + inline void Reset() { + Enabled = false; + LabelOffset = -1; + HasFormatSpec = false; + Formatter = NULL; + FormatterData = NULL; + ShowDefaultTicks = true; + FitThisFrame = false; + FitExtents.Min = HUGE_VAL; + FitExtents.Max = -HUGE_VAL; + OrthoAxis = NULL; + Ticks.Reset(); + } + + inline bool SetMin(double _min, bool force=false) { if (!force && IsLockedMin()) return false; _min = ImConstrainNan(ImConstrainInf(_min)); @@ -618,10 +672,11 @@ struct ImPlotAxis return false; Range.Min = _min; PickerTimeMin = ImPlotTime::FromDouble(Range.Min); + UpdateTransformCache(); return true; }; - bool SetMax(double _max, bool force=false) { + inline bool SetMax(double _max, bool force=false) { if (!force && IsLockedMax()) return false; _max = ImConstrainNan(ImConstrainInf(_max)); @@ -633,23 +688,25 @@ struct ImPlotAxis return false; Range.Max = _max; PickerTimeMax = ImPlotTime::FromDouble(Range.Max); + UpdateTransformCache(); return true; }; - void SetRange(double _min, double _max) { - Range.Min = _min; - Range.Max = _max; + inline void SetRange(double v1, double v2) { + Range.Min = ImMin(v1,v2); + Range.Max = ImMax(v1,v2); Constrain(); PickerTimeMin = ImPlotTime::FromDouble(Range.Min); PickerTimeMax = ImPlotTime::FromDouble(Range.Max); + UpdateTransformCache(); } - void SetRange(const ImPlotRange& range) { + inline void SetRange(const ImPlotRange& range) { SetRange(range.Min, range.Max); } - void SetAspect(double unit_per_pix) { - double new_size = unit_per_pix * Pixels; + inline void SetAspect(double unit_per_pix) { + double new_size = unit_per_pix * PixelSize(); double delta = (new_size - Range.Size()) * 0.5f; if (IsLocked()) return; @@ -661,16 +718,18 @@ struct ImPlotAxis SetRange(Range.Min - delta, Range.Max + delta); } - double GetAspect() const { return Range.Size() / Pixels; } + inline float PixelSize() const { return ImAbs(PixelMax - PixelMin); } + + inline double GetAspect() const { return Range.Size() / PixelSize(); } - void Constrain() { + inline void Constrain() { Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); - if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale)) { + if (IsLog()) { Range.Min = ImConstrainLog(Range.Min); Range.Max = ImConstrainLog(Range.Max); } - if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) { + if (IsTime()) { Range.Min = ImConstrainTime(Range.Min); Range.Max = ImConstrainTime(Range.Max); } @@ -678,22 +737,114 @@ struct ImPlotAxis Range.Max = Range.Min + DBL_EPSILON; } - inline bool IsLabeled() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } - inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } + inline void UpdateTransformCache() { + LinM = (PixelMax - PixelMin) / Range.Size(); + LogD = IsLog() ? ImLog10(Range.Max / Range.Min) : 0; + } + + inline double PixelsToPlot(float pix) const { + double plt = (pix - PixelMin) / LinM + Range.Min; + if (IsLog()) { + double t = (plt - Range.Min) / Range.Size(); + plt = ImPow(10, t * LogD) * Range.Min; + } + return plt; + } + + inline float PlotToPixels(double plt) const { + if (IsLog()) { + plt = plt <= 0.0 ? IMPLOT_LOG_ZERO : plt; + double t = ImLog10(plt / Range.Min) / LogD; + plt = ImLerp(Range.Min, Range.Max, (float)t); + } + return (float)(PixelMin + LinM * (plt - Range.Min)); + } + + inline void ExtendFit(double v) { + if (!ImNanOrInf(v) && !(IsLog() && v <= 0)) { + FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; + FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; + } + } - inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } - inline bool IsRangeLocked() const { return HasRange && RangeCond == ImGuiCond_Always; } + inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) { + if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) + return; + if (!ImNanOrInf(v) && !(IsLog() && v <= 0)) { + FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; + FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; + } + } + + inline void ApplyFit(float padding) { + const double ext_size = FitExtents.Size() * 0.5; + FitExtents.Min -= ext_size * padding; + FitExtents.Max += ext_size * padding; + if (!IsLockedMin() && !ImNanOrInf(FitExtents.Min)) + Range.Min = FitExtents.Min; + if (!IsLockedMax() && !ImNanOrInf(FitExtents.Max)) + Range.Max = FitExtents.Max; + if (ImAlmostEqual(Range.Min, Range.Max)) { + Range.Max += 0.5; + Range.Min -= 0.5; + } + Constrain(); + UpdateTransformCache(); + } - inline bool IsLockedMin() const { return !Present || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } - inline bool IsLockedMax() const { return !Present || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } - inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } + inline bool HasLabel() const { return LabelOffset != -1 && !ImHasFlag(Flags, ImPlotAxisFlags_NoLabel); } + inline bool HasGridLines() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoGridLines); } + inline bool HasTickLabels() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } + inline bool HasTickMarks() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickMarks); } + inline bool WillRender() const { return HasGridLines() || HasTickLabels() || HasTickMarks(); } + inline bool IsOpposite() const { return ImHasFlag(Flags, ImPlotAxisFlags_Opposite); } + inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } + inline bool IsForeground() const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground); } + inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } + inline bool CanInitFit() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; } + inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } + inline bool IsLockedMin() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } + inline bool IsLockedMax() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } + inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } + inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } + inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } + inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } + inline bool IsTime() const { return ImHasFlag(Flags, ImPlotAxisFlags_Time); } + inline bool IsLog() const { return ImHasFlag(Flags, ImPlotAxisFlags_LogScale); } + inline bool HasMenus() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus); } - inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } - inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } - inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } + void PushLinks() { + if (LinkedMin) { *LinkedMin = Range.Min; } + if (LinkedMax) { *LinkedMax = Range.Max; } + } - inline bool IsTime() const { return ImHasFlag(Flags, ImPlotAxisFlags_Time); } - inline bool IsLog() const { return ImHasFlag(Flags, ImPlotAxisFlags_LogScale); } + void PullLinks() { + if (LinkedMin) { SetMin(*LinkedMin,true); } + if (LinkedMax) { SetMax(*LinkedMax,true); } + } +}; + +// Align plots group data +struct ImPlotAlignmentData { + bool Vertical; + float PadA; + float PadB; + float PadAMax; + float PadBMax; + ImPlotAlignmentData() { + Vertical = true; + PadA = PadB = PadAMax = PadBMax = 0; + } + void Begin() { PadAMax = PadBMax = 0; } + void Update(float& pad_a, float& pad_b, float& delta_a, float& delta_b) { + float bak_a = pad_a; float bak_b = pad_b; + if (PadAMax < pad_a) { PadAMax = pad_a; } + if (PadBMax < pad_b) { PadBMax = pad_b; } + if (pad_a < PadA) { pad_a = PadA; delta_a = pad_a - bak_a; } else { delta_a = 0; } + if (pad_b < PadB) { pad_b = PadB; delta_b = pad_b - bak_b; } else { delta_b = 0; } + } + void End() { PadA = PadAMax; PadB = PadBMax; } + void Reset() { PadA = PadB = PadAMax = PadBMax = 0; } }; // State information for Plot items @@ -701,6 +852,7 @@ struct ImPlotItem { ImGuiID ID; ImU32 Color; + ImRect LegendHoverRect; int NameOffset; bool Show; bool LegendHovered; @@ -717,109 +869,207 @@ struct ImPlotItem ~ImPlotItem() { ID = 0; } }; -// Holds Legend state labels and item references -struct ImPlotLegendData +// Holds Legend state +struct ImPlotLegend { - ImVector<int> Indices; - ImGuiTextBuffer Labels; + ImPlotLegendFlags Flags; + ImPlotLegendFlags PreviousFlags; + ImPlotLocation Location; + ImPlotLocation PreviousLocation; + ImVector<int> Indices; + ImGuiTextBuffer Labels; + ImRect Rect; + bool Hovered; + bool Held; + bool CanGoInside; + + ImPlotLegend() { + Flags = PreviousFlags = ImPlotLegendFlags_None; + CanGoInside = true; + Hovered = Held = false; + Location = ImPlotLocation_NorthWest; + } + void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); } }; -// Holds Plot state information that must persist after EndPlot -struct ImPlotPlot +// Holds Items and Legend data +struct ImPlotItemGroup { ImGuiID ID; - ImPlotFlags Flags; - ImPlotFlags PreviousFlags; - ImPlotAxis XAxis; - ImPlotAxis YAxis[IMPLOT_Y_AXES]; - ImPlotLegendData LegendData; - ImPool<ImPlotItem> Items; - ImVec2 SelectStart; - ImRect SelectRect; - ImVec2 QueryStart; - ImRect QueryRect; - bool Initialized; - bool Selecting; - bool Selected; - bool ContextLocked; - bool Querying; - bool Queried; - bool DraggingQuery; - bool LegendHovered; - bool LegendOutside; - bool LegendFlipSideNextFrame; - bool FrameHovered; - bool PlotHovered; + ImPlotLegend Legend; + ImPool<ImPlotItem> ItemPool; int ColormapIdx; - int CurrentYAxis; - ImPlotLocation MousePosLocation; - ImPlotLocation LegendLocation; - ImPlotOrientation LegendOrientation; - ImRect FrameRect; - ImRect CanvasRect; - ImRect PlotRect; - ImRect AxesRect; - ImRect LegendRect; + + ImPlotItemGroup() { ColormapIdx = 0; } + + int GetItemCount() const { return ItemPool.GetBufSize(); } + ImGuiID GetItemID(const char* label_id) { return ImGui::GetID(label_id); /* GetIDWithSeed */ } + ImPlotItem* GetItem(ImGuiID id) { return ItemPool.GetByKey(id); } + ImPlotItem* GetItem(const char* label_id) { return GetItem(GetItemID(label_id)); } + ImPlotItem* GetOrAddItem(ImGuiID id) { return ItemPool.GetOrAddByKey(id); } + ImPlotItem* GetItemByIndex(int i) { return ItemPool.GetByIndex(i); } + int GetItemIndex(ImPlotItem* item) { return ItemPool.GetIndex(item); } + int GetLegendCount() const { return Legend.Indices.size(); } + ImPlotItem* GetLegendItem(int i) { return ItemPool.GetByIndex(Legend.Indices[i]); } + const char* GetLegendLabel(int i) { return Legend.Labels.Buf.Data + GetLegendItem(i)->NameOffset; } + void Reset() { ItemPool.Clear(); Legend.Reset(); ColormapIdx = 0; } +}; + +// Holds Plot state information that must persist after EndPlot +struct ImPlotPlot +{ + ImGuiID ID; + ImPlotFlags Flags; + ImPlotFlags PreviousFlags; + ImPlotLocation MouseTextLocation; + ImPlotMouseTextFlags MouseTextFlags; + ImPlotAxis Axes[ImAxis_COUNT]; + ImGuiTextBuffer TextBuffer; + ImPlotItemGroup Items; + ImAxis CurrentX; + ImAxis CurrentY; + ImRect FrameRect; + ImRect CanvasRect; + ImRect PlotRect; + ImRect AxesRect; + ImRect SelectRect; + ImVec2 SelectStart; + int TitleOffset; + bool JustCreated; + bool Initialized; + bool SetupLocked; + bool FitThisFrame; + bool Hovered; + bool Held; + bool Selecting; + bool Selected; + bool ContextLocked; ImPlotPlot() { Flags = PreviousFlags = ImPlotFlags_None; - XAxis.Orientation = ImPlotOrientation_Horizontal; - for (int i = 0; i < IMPLOT_Y_AXES; ++i) - YAxis[i].Orientation = ImPlotOrientation_Vertical; - SelectStart = QueryStart = ImVec2(0,0); - Initialized = Selecting = Selected = ContextLocked = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSideNextFrame = false; - ColormapIdx = CurrentYAxis = 0; - LegendLocation = ImPlotLocation_North | ImPlotLocation_West; - LegendOrientation = ImPlotOrientation_Vertical; - MousePosLocation = ImPlotLocation_South | ImPlotLocation_East; + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) + XAxis(i).Vertical = false; + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) + YAxis(i).Vertical = true; + SelectStart = ImVec2(0,0); + CurrentX = ImAxis_X1; + CurrentY = ImAxis_Y1; + MouseTextLocation = ImPlotLocation_South | ImPlotLocation_East; + MouseTextFlags = ImPlotMouseTextFlags_None; + TitleOffset = -1; + JustCreated = true; + Initialized = SetupLocked = FitThisFrame = false; + Hovered = Held = Selected = Selecting = ContextLocked = false; + } + + inline bool IsInputLocked() const { + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) { + if (!XAxis(i).IsInputLocked()) + return false; + } + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) { + if (!YAxis(i).IsInputLocked()) + return false; + } + return true; + } + + inline void ClearTextBuffer() { TextBuffer.Buf.shrink(0); } + + inline void SetTitle(const char* title) { + if (title && ImGui::FindRenderedTextEnd(title, NULL) != title) { + TitleOffset = TextBuffer.size(); + TextBuffer.append(title, title + strlen(title) + 1); + } + else { + TitleOffset = -1; + } + } + inline bool HasTitle() const { return TitleOffset != -1 && !ImHasFlag(Flags, ImPlotFlags_NoTitle); } + inline const char* GetTitle() const { return TextBuffer.Buf.Data + TitleOffset; } + + inline ImPlotAxis& XAxis(int i) { return Axes[ImAxis_X1 + i]; } + inline const ImPlotAxis& XAxis(int i) const { return Axes[ImAxis_X1 + i]; } + inline ImPlotAxis& YAxis(int i) { return Axes[ImAxis_Y1 + i]; } + inline const ImPlotAxis& YAxis(int i) const { return Axes[ImAxis_Y1 + i]; } + + inline int EnabledAxesX() { + int cnt = 0; + for (int i = 0; i < IMPLOT_NUM_X_AXES; ++i) + cnt += XAxis(i).Enabled; + return cnt; } - int GetLegendCount() const { return LegendData.Indices.size(); } - ImPlotItem* GetLegendItem(int i); - const char* GetLegendLabel(int i); + inline int EnabledAxesY() { + int cnt = 0; + for (int i = 0; i < IMPLOT_NUM_Y_AXES; ++i) + cnt += YAxis(i).Enabled; + return cnt; + } + + inline void SetAxisLabel(ImPlotAxis& axis, const char* label) { + if (label && ImGui::FindRenderedTextEnd(label, NULL) != label) { + axis.LabelOffset = TextBuffer.size(); + TextBuffer.append(label, label + strlen(label) + 1); + } + else { + axis.LabelOffset = -1; + } + } + + inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; } +}; - inline bool AnyYInputLocked() const { return YAxis[0].IsInputLocked() || (YAxis[1].Present ? YAxis[1].IsInputLocked() : false) || (YAxis[2].Present ? YAxis[2].IsInputLocked() : false); } - inline bool AllYInputLocked() const { return YAxis[0].IsInputLocked() && (YAxis[1].Present ? YAxis[1].IsInputLocked() : true ) && (YAxis[2].Present ? YAxis[2].IsInputLocked() : true ); } - inline bool IsInputLocked() const { return XAxis.IsInputLocked() && YAxis[0].IsInputLocked() && YAxis[1].IsInputLocked() && YAxis[2].IsInputLocked(); } +// Holds subplot data that must persist afer EndSubplot +struct ImPlotSubplot { + ImGuiID ID; + ImPlotSubplotFlags Flags; + ImPlotSubplotFlags PreviousFlags; + ImPlotItemGroup Items; + int Rows; + int Cols; + int CurrentIdx; + ImRect FrameRect; + ImRect GridRect; + ImVec2 CellSize; + ImVector<ImPlotAlignmentData> RowAlignmentData; + ImVector<ImPlotAlignmentData> ColAlignmentData; + ImVector<float> RowRatios; + ImVector<float> ColRatios; + ImVector<ImPlotRange> RowLinkData; + ImVector<ImPlotRange> ColLinkData; + float TempSizes[2]; + bool FrameHovered; + bool HasTitle; + + ImPlotSubplot() { + Rows = Cols = CurrentIdx = 0; + FrameHovered = false; + Items.Legend.Location = ImPlotLocation_North; + Items.Legend.Flags = ImPlotLegendFlags_Horizontal|ImPlotLegendFlags_Outside; + Items.Legend.CanGoInside = false; + HasTitle = false; + } }; // Temporary data storage for upcoming plot struct ImPlotNextPlotData { - ImGuiCond XRangeCond; - ImGuiCond YRangeCond[IMPLOT_Y_AXES]; - ImPlotRange XRange; - ImPlotRange YRange[IMPLOT_Y_AXES]; - bool HasXRange; - bool HasYRange[IMPLOT_Y_AXES]; - bool ShowDefaultTicksX; - bool ShowDefaultTicksY[IMPLOT_Y_AXES]; - char FmtX[16]; - char FmtY[IMPLOT_Y_AXES][16]; - bool HasFmtX; - bool HasFmtY[IMPLOT_Y_AXES]; - bool FitX; - bool FitY[IMPLOT_Y_AXES]; - double* LinkedXmin; - double* LinkedXmax; - double* LinkedYmin[IMPLOT_Y_AXES]; - double* LinkedYmax[IMPLOT_Y_AXES]; + ImPlotCond RangeCond[ImAxis_COUNT]; + ImPlotRange Range[ImAxis_COUNT]; + bool HasRange[ImAxis_COUNT]; + bool Fit[ImAxis_COUNT]; + double* LinkedMin[ImAxis_COUNT]; + double* LinkedMax[ImAxis_COUNT]; ImPlotNextPlotData() { Reset(); } void Reset() { - HasXRange = false; - ShowDefaultTicksX = true; - HasFmtX = false; - FitX = false; - LinkedXmin = LinkedXmax = NULL; - for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - HasYRange[i] = false; - ShowDefaultTicksY[i] = true; - HasFmtY[i] = false; - FitY[i] = false; - LinkedYmin[i] = LinkedYmax[i] = NULL; + for (int i = 0; i < ImAxis_COUNT; ++i) { + HasRange[i] = false; + Fit[i] = false; + LinkedMin[i] = LinkedMax[i] = NULL; } } @@ -843,7 +1093,7 @@ struct ImPlotNextItemData { bool RenderMarkerFill; bool HasHidden; bool Hidden; - ImGuiCond HiddenCond; + ImPlotCond HiddenCond; ImPlotNextItemData() { Reset(); } void Reset() { for (int i = 0; i < 5; ++i) @@ -857,40 +1107,22 @@ struct ImPlotNextItemData { // Holds state information that must persist between calls to BeginPlot()/EndPlot() struct ImPlotContext { // Plot States - ImPool<ImPlotPlot> Plots; - ImPlotPlot* CurrentPlot; - ImPlotItem* CurrentItem; - ImPlotItem* PreviousItem; + ImPool<ImPlotPlot> Plots; + ImPool<ImPlotSubplot> Subplots; + ImPlotPlot* CurrentPlot; + ImPlotSubplot* CurrentSubplot; + ImPlotItemGroup* CurrentItems; + ImPlotItem* CurrentItem; + ImPlotItem* PreviousItem; // Tick Marks and Labels ImPlotTickCollection CTicks; - ImPlotTickCollection XTicks; - ImPlotTickCollection YTicks[IMPLOT_Y_AXES]; - float YAxisReference[IMPLOT_Y_AXES]; - // Annotation and User Labels + // Annotation and Tabs ImPlotAnnotationCollection Annotations; + ImPlotTagCollection Tags; - // Transformations and Data Extents - ImPlotScale Scales[IMPLOT_Y_AXES]; - ImRect PixelRange[IMPLOT_Y_AXES]; - double Mx; - double My[IMPLOT_Y_AXES]; - double LogDenX; - double LogDenY[IMPLOT_Y_AXES]; - ImPlotRange ExtentsX; - ImPlotRange ExtentsY[IMPLOT_Y_AXES]; - - // Data Fitting Flags - bool FitThisFrame; - bool FitX; - bool FitY[IMPLOT_Y_AXES]; - - // Axis Rendering Flags - bool RenderX; - bool RenderY[IMPLOT_Y_AXES]; - - // Axis Locking Flags + // Flags bool ChildWindowMade; // Style and Colormaps @@ -904,16 +1136,22 @@ struct ImPlotContext { tm Tm; // Temp data for general use - ImVector<double> Temp1, Temp2; + ImVector<double> TempDouble1, TempDouble2; + ImVector<int> TempInt1; // Misc - int VisibleItemCount; int DigitalPlotItemCnt; int DigitalPlotOffset; ImPlotNextPlotData NextPlotData; ImPlotNextItemData NextItemData; ImPlotInputMap InputMap; - ImPlotPoint MousePos[IMPLOT_Y_AXES]; + bool OpenContextThisFrame; + ImGuiTextBuffer MousePosStringBuilder; + + // Align plots + ImPool<ImPlotAlignmentData> AlignmentData; + ImPlotAlignmentData* CurrentAlignmentH; + ImPlotAlignmentData* CurrentAlignmentV; }; //----------------------------------------------------------------------------- @@ -930,14 +1168,11 @@ namespace ImPlot { // Initializes an ImPlotContext IMPLOT_API void Initialize(ImPlotContext* ctx); // Resets an ImPlot context for the next call to BeginPlot -IMPLOT_API void Reset(ImPlotContext* ctx); - -//----------------------------------------------------------------------------- -// [SECTION] Input Utils -//----------------------------------------------------------------------------- - -// Allows changing how keyboard/mouse interaction works. -IMPLOT_API ImPlotInputMap& GetInputMap(); +IMPLOT_API void ResetCtxForNextPlot(ImPlotContext* ctx); +// Resets an ImPlot context for the next call to BeginAlignedPlots +IMPLOT_API void ResetCtxForNextAlignedPlots(ImPlotContext* ctx); +// Resets an ImPlot context for the next call to BeginSubplot +IMPLOT_API void ResetCtxForNextSubplot(ImPlotContext* ctx); //----------------------------------------------------------------------------- // [SECTION] Plot Utils @@ -954,6 +1189,27 @@ IMPLOT_API void BustPlotCache(); IMPLOT_API void ShowPlotContextMenu(ImPlotPlot& plot); //----------------------------------------------------------------------------- +// [SECTION] Setup Utils +//----------------------------------------------------------------------------- + +// Lock Setup and call SetupFinish if necessary. +static inline void SetupLock() { + if (!GImPlot->CurrentPlot->SetupLocked) + SetupFinish(); + GImPlot->CurrentPlot->SetupLocked = true; +} + +//----------------------------------------------------------------------------- +// [SECTION] Subplot Utils +//----------------------------------------------------------------------------- + +// Advances to next subplot +IMPLOT_API void SubplotNextCell(); + +// Shows a subplot's context menu. +IMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot); + +//----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- @@ -975,66 +1231,90 @@ IMPLOT_API void BustItemCache(); // [SECTION] Axis Utils //----------------------------------------------------------------------------- -// Gets the current y-axis for the current plot -static inline int GetCurrentYAxis() { return GImPlot->CurrentPlot->CurrentYAxis; } -// Updates axis ticks, lins, and label colors -IMPLOT_API void UpdateAxisColors(int axis_flag, ImPlotAxis* axis); +// Returns true if any enabled axis is locked from user input. +static inline bool AnyAxesInputLocked(ImPlotAxis* axes, int count) { + for (int i = 0; i < count; ++i) { + if (axes[i].Enabled && axes[i].IsInputLocked()) + return true; + } + return false; +} -// Updates plot-to-pixel space transformation variables for the current plot. -IMPLOT_API void UpdateTransformCache(); -// Gets the XY scale for the current plot and y-axis -static inline ImPlotScale GetCurrentScale() { return GImPlot->Scales[GetCurrentYAxis()]; } +// Returns true if all enabled axes are locked from user input. +static inline bool AllAxesInputLocked(ImPlotAxis* axes, int count) { + for (int i = 0; i < count; ++i) { + if (axes[i].Enabled && !axes[i].IsInputLocked()) + return false; + } + return true; +} -// Returns true if the user has requested data to be fit. -static inline bool FitThisFrame() { return GImPlot->FitThisFrame; } -// Extend the the extents of an axis on current plot so that it encompes v -static inline void FitPointAxis(ImPlotAxis& axis, ImPlotRange& ext, double v) { - if (!ImNanOrInf(v) && !(ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale) && v <= 0)) { - ext.Min = v < ext.Min ? v : ext.Min; - ext.Max = v > ext.Max ? v : ext.Max; +static inline bool AnyAxesHeld(ImPlotAxis* axes, int count) { + for (int i = 0; i < count; ++i) { + if (axes[i].Enabled && axes[i].Held) + return true; } + return false; } -// Extend the the extents of an axis on current plot so that it encompes v -static inline void FitPointMultiAxis(ImPlotAxis& axis, ImPlotAxis& alt, ImPlotRange& ext, double v, double v_alt) { - if (ImHasFlag(axis.Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) - return; - if (!ImNanOrInf(v) && !(ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale) && v <= 0)) { - ext.Min = v < ext.Min ? v : ext.Min; - ext.Max = v > ext.Max ? v : ext.Max; + +static inline bool AnyAxesHovered(ImPlotAxis* axes, int count) { + for (int i = 0; i < count; ++i) { + if (axes[i].Enabled && axes[i].Hovered) + return true; } + return false; +} + +// Gets the XY scale for the current plot and y-axis (TODO) +static inline ImPlotScale GetCurrentScale() { + ImPlotPlot& plot = *GetCurrentPlot(); + ImPlotAxis& x = plot.Axes[plot.CurrentX]; + ImPlotAxis& y = plot.Axes[plot.CurrentY]; + if (!x.IsLog() && !y.IsLog()) + return ImPlotScale_LinLin; + else if (x.IsLog() && !y.IsLog()) + return ImPlotScale_LogLin; + else if (!x.IsLog() && y.IsLog()) + return ImPlotScale_LinLog; + else + return ImPlotScale_LogLog; +} + +// Returns true if the user has requested data to be fit. +static inline bool FitThisFrame() { + return GImPlot->CurrentPlot->FitThisFrame; } + // Extends the current plot's axes so that it encompasses a vertical line at x static inline void FitPointX(double x) { - FitPointAxis(GImPlot->CurrentPlot->XAxis, GImPlot->ExtentsX, x); + ImPlotPlot& plot = *GetCurrentPlot(); + ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; + x_axis.ExtendFit(x); } + // Extends the current plot's axes so that it encompasses a horizontal line at y static inline void FitPointY(double y) { - const ImPlotYAxis y_axis = GImPlot->CurrentPlot->CurrentYAxis; - FitPointAxis(GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->ExtentsY[y_axis], y); + ImPlotPlot& plot = *GetCurrentPlot(); + ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; + y_axis.ExtendFit(y); } + // Extends the current plot's axes so that it encompasses point p static inline void FitPoint(const ImPlotPoint& p) { - const ImPlotYAxis y_axis = GImPlot->CurrentPlot->CurrentYAxis; - FitPointMultiAxis(GImPlot->CurrentPlot->XAxis, GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->ExtentsX, p.x, p.y); - FitPointMultiAxis(GImPlot->CurrentPlot->YAxis[y_axis], GImPlot->CurrentPlot->XAxis, GImPlot->ExtentsY[y_axis], p.y, p.x); + ImPlotPlot& plot = *GetCurrentPlot(); + ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; + ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; + x_axis.ExtendFitWith(y_axis, p.x, p.y); + y_axis.ExtendFitWith(x_axis, p.y, p.x); } // Returns true if two ranges overlap static inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) { return r1.Min <= r2.Max && r2.Min <= r1.Max; } -// Updates pointers for linked axes from axis internal range. -IMPLOT_API void PushLinkedAxis(ImPlotAxis& axis); -// Updates axis internal range from points for linked axes. -IMPLOT_API void PullLinkedAxis(ImPlotAxis& axis); - // Shows an axis's context menu. IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false); -// Get format spec for axis -static inline const char* GetFormatX() { return GImPlot->NextPlotData.HasFmtX ? GImPlot->NextPlotData.FmtX : IMPLOT_LABEL_FMT; } -static inline const char* GetFormatY(ImPlotYAxis y) { return GImPlot->NextPlotData.HasFmtY[y] ? GImPlot->NextPlotData.FmtY[y] : IMPLOT_LABEL_FMT; } - //----------------------------------------------------------------------------- // [SECTION] Legend Utils //----------------------------------------------------------------------------- @@ -1042,11 +1322,13 @@ static inline const char* GetFormatY(ImPlotYAxis y) { return GImPlot->NextPlotDa // Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount. IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0)); // Calculates the bounding box size of a legend -IMPLOT_API ImVec2 CalcLegendSize(ImPlotPlot& plot, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orientation); +IMPLOT_API ImVec2 CalcLegendSize(ImPlotItemGroup& items, const ImVec2& pad, const ImVec2& spacing, bool vertical); // Renders legend entries into a bounding box -IMPLOT_API void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orientation, ImDrawList& DrawList); +IMPLOT_API bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, bool vertical, ImDrawList& DrawList); // Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!). -IMPLOT_API void ShowAltLegend(const char* title_id, ImPlotOrientation orientation = ImPlotOrientation_Vertical, const ImVec2 size = ImVec2(0,0), bool interactable = true); +IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const ImVec2 size = ImVec2(0,0), bool interactable = true); +// Shows an legends's context menu. +IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); //----------------------------------------------------------------------------- // [SECTION] Tick Utils @@ -1056,16 +1338,16 @@ IMPLOT_API void ShowAltLegend(const char* title_id, ImPlotOrientation orientatio IMPLOT_API void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotDateTimeFmt fmt); // Populates a list of ImPlotTicks with normal spaced and formatted ticks -IMPLOT_API void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt); +IMPLOT_API void AddTicksDefault(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); // Populates a list of ImPlotTicks with logarithmic space and formatted ticks -IMPLOT_API void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt); +IMPLOT_API void AddTicksLogarithmic(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); +// Populates a list of ImPlotTicks with custom spaced and labeled ticks +IMPLOT_API void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); // Populates a list of ImPlotTicks with time formatted ticks. IMPLOT_API void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks); -// Populates a list of ImPlotTicks with custom spaced and labeled ticks -IMPLOT_API void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, const char* fmt); // Create a a string label for a an axis value -IMPLOT_API int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value, char* buff, int size); +IMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false); //----------------------------------------------------------------------------- // [SECTION] Styling Utils @@ -1087,13 +1369,15 @@ static inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConve // Draws vertical text. The position is the bottom left of the text rect. IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = NULL); +// Draws multiline horizontal text centered. +IMPLOT_API void AddTextCentered(ImDrawList* DrawList, ImVec2 top_center, ImU32 col, const char* text_begin, const char* text_end = NULL); // Calculates the size of vertical text static inline ImVec2 CalcTextSizeVertical(const char *text) { ImVec2 sz = ImGui::CalcTextSize(text); return ImVec2(sz.y, sz.x); } // Returns white or black text given background color -static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.5 ? IM_COL32_BLACK : IM_COL32_WHITE; } +static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299f + bg.y * 0.587f + bg.z * 0.114f) > 0.5f ? IM_COL32_BLACK : IM_COL32_WHITE; } static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); } // Lightens or darkens a color for hover static inline ImU32 CalcHoverColor(ImU32 col) { return ImMixU32(col, CalcTextColor(col), 32); } @@ -1149,13 +1433,6 @@ void FillRange(ImVector<T>& buffer, int n, T vmin, T vmax) { } } -// Offsets and strides a data buffer -template <typename T> -static 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); -} - // Calculate histogram bin counts and widths template <typename T> static inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { diff --git a/3rdparty/implot/implot_items.cpp b/3rdparty/implot/implot_items.cpp index 9f94492..ae750c9 100644 --- a/3rdparty/implot/implot_items.cpp +++ b/3rdparty/implot/implot_items.cpp @@ -20,29 +20,44 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.10 WIP +// ImPlot v0.13 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 IMPLOT_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; \ - } \ - } +#ifndef IMPLOT_NO_FORCE_INLINE + #ifdef _MSC_VER + #define IMPLOT_INLINE __forceinline + #elif defined(__GNUC__) + #define IMPLOT_INLINE inline __attribute__((__always_inline__)) + #elif defined(__CLANG__) + #if __has_attribute(__always_inline__) + #define IMPLOT_INLINE inline __attribute__((__always_inline__)) + #else + #define IMPLOT_INLINE inline + #endif + #else + #define IMPLOT_INLINE inline + #endif +#else + #define IMPLOT_INLINE inline +#endif + +#if defined __SSE__ || defined __x86_64__ || defined _M_X64 +#ifndef IMGUI_ENABLE_SSE +#include <immintrin.h> +#endif +static IMPLOT_INLINE float ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } +#endif -// Support for pre-1.82 version. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. +#define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) + +// Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif @@ -50,37 +65,50 @@ namespace ImPlot { //----------------------------------------------------------------------------- +// Utils +//----------------------------------------------------------------------------- + +// Calc maximum index size of ImDrawIdx +template <typename T> +struct MaxIdx { static const unsigned int Value; }; +template <> const unsigned int MaxIdx<unsigned short>::Value = 65535; +template <> const unsigned int MaxIdx<unsigned int>::Value = 4294967295; + +//----------------------------------------------------------------------------- // Item Utils //----------------------------------------------------------------------------- ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created) { ImPlotContext& gp = *GImPlot; - ImGuiID id = ImGui::GetID(label_id); + ImPlotItemGroup& Items = *gp.CurrentItems; + ImGuiID id = Items.GetItemID(label_id); if (just_created != NULL) - *just_created = gp.CurrentPlot->Items.GetByKey(id) == NULL; - ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id); + *just_created = Items.GetItem(id) == NULL; + ImPlotItem* item = Items.GetOrAddItem(id); if (item->SeenThisFrame) return item; item->SeenThisFrame = true; - int idx = gp.CurrentPlot->Items.GetIndex(item); + int idx = Items.GetItemIndex(item); item->ID = id; if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) { - gp.CurrentPlot->LegendData.Indices.push_back(idx); - item->NameOffset = gp.CurrentPlot->LegendData.Labels.size(); - gp.CurrentPlot->LegendData.Labels.append(label_id, label_id + strlen(label_id) + 1); + Items.Legend.Indices.push_back(idx); + item->NameOffset = Items.Legend.Labels.size(); + Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); } else { item->Show = true; } - if (item->Show) - gp.VisibleItemCount++; return item; } ImPlotItem* GetItem(const char* label_id) { ImPlotContext& gp = *GImPlot; - ImGuiID id = ImGui::GetID(label_id); - return gp.CurrentPlot->Items.GetByKey(id); + return gp.CurrentItems->GetItem(label_id); +} + +bool IsItemHidden(const char* label_id) { + ImPlotItem* item = GetItem(label_id); + return item != NULL && !item->Show; } ImPlotItem* GetCurrentItem() { @@ -123,20 +151,15 @@ ImVec4 GetLastItemColor() { return ImVec4(); } -void HideNextItem(bool hidden, ImGuiCond cond) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.HasHidden = true; - gp.NextItemData.Hidden = hidden; - gp.NextItemData.HiddenCond = cond; -} - void BustItemCache() { ImPlotContext& gp = *GImPlot; - for (int p = 0; p < gp.Plots.GetSize(); ++p) { + for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlotPlot& plot = *gp.Plots.GetByIndex(p); - plot.ColormapIdx = 0; - plot.Items.Clear(); - plot.LegendData.Reset(); + plot.Items.Reset(); + } + for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) { + ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p); + subplot.Items.Reset(); } } @@ -146,12 +169,15 @@ void BustColorCache(const char* plot_title_id) { BustItemCache(); } else { - ImPlotPlot* plot = gp.Plots.GetByKey(ImGui::GetCurrentWindow()->GetID(plot_title_id)); - if (plot == NULL) - return; - plot->ColormapIdx = 0; - plot->Items.Clear(); - plot->LegendData.Reset(); + ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id); + ImPlotPlot* plot = gp.Plots.GetByKey(id); + if (plot != NULL) + plot->Items.Reset(); + else { + ImPlotSubplot* subplot = gp.Subplots.GetByKey(id); + if (subplot != NULL) + subplot->Items.Reset(); + } } } @@ -159,10 +185,15 @@ void BustColorCache(const char* plot_title_id) { // Begin/EndItem //----------------------------------------------------------------------------- +static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; +static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; + + // Begins a new item. Returns false if the item should not be plotted. bool BeginItem(const char* label_id, ImPlotCol recolor_from) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); bool just_created; ImPlotItem* item = RegisterOrGetItem(label_id, &just_created); // set current item @@ -212,12 +243,21 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { s.DigitalBitGap = s.DigitalBitGap < 0 ? gp.Style.DigitalBitGap : s.DigitalBitGap; // apply alpha modifier(s) s.Colors[ImPlotCol_Fill].w *= s.FillAlpha; - // s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all + s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all // apply highlight mods - if (item->LegendHovered && !ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_NoHighlight)) { - s.LineWeight *= 2; - s.MarkerWeight *= 2; - // TODO: highlight fills? + if (item->LegendHovered) { + if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) { + s.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; + s.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; + s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; + // TODO: how to highlight fills? + } + if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) { + if (gp.CurrentPlot->EnabledAxesX() > 1) + gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color; + if (gp.CurrentPlot->EnabledAxesY() > 1) + gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color; + } } // set render flags s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; @@ -243,141 +283,133 @@ void EndItem() { } //----------------------------------------------------------------------------- +// INDEXERS +//----------------------------------------------------------------------------- + +// Offsets and strides a data buffer +template <typename T> +IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { + const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); + switch (s) { + case 3 : return data[idx]; + case 2 : return data[(offset + idx) % count]; + case 1 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx) ) * stride); + case 0 : return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride); + default: return T(0); + } +} + +//----------------------------------------------------------------------------- // GETTERS //----------------------------------------------------------------------------- // Getters can be thought of as iterators that convert user data (e.g. raw arrays) // to ImPlotPoints -// Interprets an array of Y points as ImPlotPoints where the X value is the index template <typename T> -struct GetterYs { - GetterYs(const T* ys, int count, double xscale, double x0, int offset, int stride) : - Ys(ys), +struct GetterIdx { + GetterIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : + Data(data), Count(count), - XScale(xscale), - X0(x0), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } - inline ImPlotPoint operator()(int idx) const { - return ImPlotPoint(X0 + XScale * idx, (double)OffsetAndStride(Ys, idx, Count, Offset, Stride)); + template <typename I> IMPLOT_INLINE double operator()(I idx) const { + return (double)IndexData(Data, idx, Count, Offset, Stride); } - const T* const Ys; - const int Count; - const double XScale; - const double X0; - const int Offset; - const int Stride; + const T* Data; + int Count; + int Offset; + int Stride; }; -// Interprets separate arrays for X and Y points as ImPlotPoints -template <typename T> -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) - { } - inline ImPlotPoint operator()(int idx) const { - return ImPlotPoint((double)OffsetAndStride(Xs, idx, Count, Offset, Stride), (double)OffsetAndStride(Ys, idx, Count, Offset, Stride)); +struct GetterLin { + GetterLin(double m, double b) : M(m), B(b) { } + template <typename I> IMPLOT_INLINE double operator()(I idx) const { + return M * idx + B; } - const T* const Xs; - const T* const Ys; - const int Count; - const int Offset; - const int Stride; + const double M; + const double B; }; -// Always returns a constant Y reference value where the X value is the index -struct GetterYRef { - GetterYRef(double y_ref, int count, double xscale, double x0) : - YRef(y_ref), - Count(count), - XScale(xscale), - X0(x0) - { } - inline ImPlotPoint operator()(int idx) const { - return ImPlotPoint(X0 + XScale*idx, YRef); - } - const double YRef; - const int Count; - const double XScale; - const double X0; +struct GetterRef { + GetterRef(double ref) : Ref(ref) { } + template <typename I> IMPLOT_INLINE double operator()(I) const { return Ref; } + const double Ref; }; -// Interprets an array of X points as ImPlotPoints where the Y value is a constant reference value -template <typename T> -struct GetterXsYRef { - GetterXsYRef(const T* xs, double y_ref, int count, int offset, int stride) : - Xs(xs), - YRef(y_ref), - Count(count), - Offset(count ? ImPosMod(offset, count) : 0), - Stride(stride) - { } - inline ImPlotPoint operator()(int idx) const { - return ImPlotPoint((double)OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef); +template <typename TGetterX, typename TGetterY> +struct GetterXY { + GetterXY(TGetterX x, TGetterY y, int count) : GetterX(x), GetterY(y), Count(count) { } + template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + return ImPlotPoint(GetterX(idx),GetterY(idx)); } - const T* const Xs; - const double YRef; + const TGetterX GetterX; + const TGetterY GetterY; const int Count; - const int Offset; - const int Stride; }; -// Interprets an array of Y points as ImPlotPoints where the X value is a constant reference value +// Interprets an array of Y points as ImPlotPoints where the X value is the index template <typename T> -struct GetterXRefYs { - GetterXRefYs(double x_ref, const T* ys, int count, int offset, int stride) : - XRef(x_ref), - Ys(ys), +struct GetterXs { + GetterXs(const T* xs, int count, double yscale, double y0, int offset, int stride) : + Xs(xs), Count(count), + YScale(yscale), + Y0(y0), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } - inline ImPlotPoint operator()(int idx) const { - return ImPlotPoint(XRef, (double)OffsetAndStride(Ys, idx, Count, Offset, Stride)); + template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + return ImPlotPoint((double)IndexData(Xs, idx, Count, Offset, Stride), Y0 + YScale * idx); } - const double XRef; - const T* const Ys; + const T* const Xs; const int Count; + const double YScale; + const double Y0; const int Offset; const int Stride; }; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { - GetterFuncPtr(ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset) : + GetterFuncPtr(ImPlotPoint (*getter)(void* data, int idx), void* data, int count) : Getter(getter), Data(data), - Count(count), - Offset(count ? ImPosMod(offset, count) : 0) + Count(count) { } - inline ImPlotPoint operator()(int idx) const { - idx = ImPosMod(Offset + idx, Count); + template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return Getter(Data, idx); } ImPlotPoint (* const Getter)(void* data, int idx); void* const Data; const int Count; - const int Offset; }; -template <typename T> -struct GetterBarV { - const T* Ys; double XShift; int Count; int Offset; int Stride; - GetterBarV(const T* ys, double xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) const { return ImPlotPoint((double)idx + (double)XShift, (double)OffsetAndStride(Ys, idx, Count, Offset, Stride)); } +template <typename TGetter> +struct GetterOverrideX { + GetterOverrideX(const TGetter& getter, double x) : Getter(getter), X(x), Count(getter.Count) { } + template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + ImPlotPoint p = Getter(idx); + p.x = X; + return p; + } + const TGetter& Getter; + const double X; + const int Count; }; -template <typename T> -struct GetterBarH { - const T* Xs; double YShift; int Count; int Offset; int Stride; - GetterBarH(const T* xs, double yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) const { return ImPlotPoint((double)OffsetAndStride(Xs, idx, Count, Offset, Stride), (double)idx + (double)YShift); } +template <typename TGetter> +struct GetterOverrideY { + GetterOverrideY(const TGetter& getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } + template <typename I> IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + ImPlotPoint p = Getter(idx); + p.y = Y; + return p; + } + const TGetter& Getter; + const double Y; + const int Count; }; template <typename T> @@ -391,11 +423,11 @@ struct GetterError { Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } - inline ImPlotPointError operator()(int idx) const { - return ImPlotPointError((double)OffsetAndStride(Xs, idx, Count, Offset, Stride), - (double)OffsetAndStride(Ys, idx, Count, Offset, Stride), - (double)OffsetAndStride(Neg, idx, Count, Offset, Stride), - (double)OffsetAndStride(Pos, idx, Count, Offset, Stride)); + template <typename I> IMPLOT_INLINE ImPlotPointError operator()(I idx) const { + return ImPlotPointError((double)IndexData(Xs, idx, Count, Offset, Stride), + (double)IndexData(Ys, idx, Count, Offset, Stride), + (double)IndexData(Neg, idx, Count, Offset, Stride), + (double)IndexData(Pos, idx, Count, Offset, Stride)); } const T* const Xs; const T* const Ys; @@ -412,69 +444,71 @@ struct GetterError { // Transforms convert points in plot space (i.e. ImPlotPoint) to pixel space (i.e. ImVec2) -// Transforms points for linear x and linear y space -struct TransformerLinLin { - TransformerLinLin() : YAxis(GetCurrentYAxis()) {} - // inline ImVec2 operator()(const ImPlotPoint& plt) const { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(const ImPlotPoint& plt) const { - ImPlotContext& gp = *GImPlot; - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (plt.x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (plt.y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - const int YAxis; +struct TransformerLin { + TransformerLin(double pixMin, double pltMin, double, double m, double ) : PixMin(pixMin), PltMin(pltMin), M(m) { } + template <typename T> IMPLOT_INLINE float operator()(T p) const { return (float)(PixMin + M * (p - PltMin)); } + double PixMin, PltMin, M; }; -// Transforms points for log x and linear y space -struct TransformerLogLin { - TransformerLogLin() : YAxis(GetCurrentYAxis()) {} - inline ImVec2 operator()(const ImPlotPoint& plt) const { - ImPlotContext& gp = *GImPlot; - double t = ImLog10(plt.x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - double 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] * (plt.y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); +struct TransformerLog { + TransformerLog(double pixMin, double pltMin, double pltMax, double m, double den) : Den(den), PltMin(pltMin), PltMax(pltMax), PixMin(pixMin), M(m) { } + template <typename T> IMPLOT_INLINE float operator()(T p) const { + p = p <= 0.0 ? IMPLOT_LOG_ZERO : p; + double t = ImLog10(p / PltMin) / Den; + p = ImLerp(PltMin, PltMax, (float)t); + return (float)(PixMin + M * (p - PltMin)); } - const int YAxis; + double Den, PltMin, PltMax, PixMin, M; }; -// Transforms points for linear x and log y space -struct TransformerLinLog { - TransformerLinLog() : YAxis(GetCurrentYAxis()) {} - inline ImVec2 operator()(const ImPlotPoint& plt) const { - ImPlotContext& gp = *GImPlot; - double t = ImLog10(plt.y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - double 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 * (plt.x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - const int YAxis; -}; +template <typename TransformerX, typename TransformerY> +struct TransformerXY { + TransformerXY(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : + Tx(x_axis.PixelMin, + x_axis.Range.Min, + x_axis.Range.Max, + x_axis.LinM, + x_axis.LogD), + Ty(y_axis.PixelMin, + y_axis.Range.Min, + y_axis.Range.Max, + y_axis.LinM, + y_axis.LogD) + { } -// Transforms points for log x and log y space -struct TransformerLogLog { - TransformerLogLog() : YAxis(GetCurrentYAxis()) {} - inline ImVec2 operator()(const ImPlotPoint& plt) const { - ImPlotContext& gp = *GImPlot; - double t = ImLog10(plt.x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - double x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); - t = ImLog10(plt.y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - double 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)) ); + TransformerXY(const ImPlotPlot& plot) : + TransformerXY(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) + { } + + TransformerXY() : + TransformerXY(*GImPlot->CurrentPlot) + { } + + template <typename P> IMPLOT_INLINE ImVec2 operator()(const P& plt) const { + ImVec2 out; + out.x = Tx(plt.x); + out.y = Ty(plt.y); + return out; } - const int YAxis; + TransformerX Tx; + TransformerY Ty; }; +typedef TransformerXY<TransformerLin,TransformerLin> TransformerLinLin; +typedef TransformerXY<TransformerLin,TransformerLog> TransformerLinLog; +typedef TransformerXY<TransformerLog,TransformerLin> TransformerLogLin; +typedef TransformerXY<TransformerLog,TransformerLog> TransformerLogLog; + //----------------------------------------------------------------------------- // PRIMITIVE RENDERERS //----------------------------------------------------------------------------- -inline void AddLine(const ImVec2& P1, const ImVec2& P2, float weight, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { +IMPLOT_INLINE void PrimLine(const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { float dx = P2.x - P1.x; float dy = P2.y - P1.y; IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); - dx *= (weight * 0.5f); - dy *= (weight * 0.5f); + dx *= half_weight; + dy *= half_weight; DrawList._VtxWritePtr[0].pos.x = P1.x + dy; DrawList._VtxWritePtr[0].pos.y = P1.y - dx; DrawList._VtxWritePtr[0].uv = uv; @@ -502,7 +536,7 @@ inline void AddLine(const ImVec2& P1, const ImVec2& P2, float weight, ImU32 col, DrawList._VtxCurrentIdx += 4; } -inline void AddRectFilled(const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { +IMPLOT_INLINE void PrimRectFilled(const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { DrawList._VtxWritePtr[0].pos = Pmin; DrawList._VtxWritePtr[0].uv = uv; DrawList._VtxWritePtr[0].col = col; @@ -530,22 +564,22 @@ inline void AddRectFilled(const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, ImD template <typename TGetter, typename TTransformer> struct LineStripRenderer { - inline LineStripRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : + IMPLOT_INLINE LineStripRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : Getter(getter), Transformer(transformer), Prims(Getter.Count - 1), Col(col), - Weight(weight) + HalfWeight(weight/2) { P1 = Transformer(Getter(0)); } - inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { + IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { ImVec2 P2 = Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } - AddLine(P1,P2,Weight,Col,DrawList,uv); + PrimLine(P1,P2,HalfWeight,Col,DrawList,uv); P1 = P2; return true; } @@ -553,15 +587,43 @@ struct LineStripRenderer { const TTransformer& Transformer; const int Prims; const ImU32 Col; - const float Weight; + const float HalfWeight; mutable ImVec2 P1; static const int IdxConsumed = 6; static const int VtxConsumed = 4; }; +template <typename TGetter1, typename TGetter2, typename TTransformer> +struct LineSegmentsRenderer { + IMPLOT_INLINE LineSegmentsRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col, float weight) : + Getter1(getter1), + Getter2(getter2), + Transformer(transformer), + Prims(ImMin(Getter1.Count, Getter2.Count)), + Col(col), + HalfWeight(weight/2) + {} + IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { + ImVec2 P1 = Transformer(Getter1(prim)); + ImVec2 P2 = Transformer(Getter2(prim)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + PrimLine(P1,P2,HalfWeight,Col,DrawList,uv); + return true; + } + const TGetter1& Getter1; + const TGetter2& Getter2; + const TTransformer& Transformer; + const int Prims; + const ImU32 Col; + const float HalfWeight; + static const int IdxConsumed = 6; + static const int VtxConsumed = 4; +}; + template <typename TGetter, typename TTransformer> struct StairsRenderer { - inline StairsRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : + IMPLOT_INLINE StairsRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : Getter(getter), Transformer(transformer), Prims(Getter.Count - 1), @@ -570,17 +632,14 @@ struct StairsRenderer { { P1 = Transformer(Getter(0)); } - inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { + IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { ImVec2 P2 = Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } - AddRectFilled(ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, DrawList, uv); - AddRectFilled(ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, DrawList, uv); - - // AddLine(P1, P12, Weight, Col, DrawList, uv); - // AddLine(P12, P2, Weight, Col, DrawList, uv); + PrimRectFilled(ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, DrawList, uv); + PrimRectFilled(ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, DrawList, uv); P1 = P2; return true; } @@ -594,37 +653,11 @@ struct StairsRenderer { static const int VtxConsumed = 8; }; -template <typename TGetter1, typename TGetter2, typename TTransformer> -struct LineSegmentsRenderer { - inline LineSegmentsRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col, float weight) : - Getter1(getter1), - Getter2(getter2), - Transformer(transformer), - Prims(ImMin(Getter1.Count, Getter2.Count)), - Col(col), - Weight(weight) - {} - inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { - ImVec2 P1 = Transformer(Getter1(prim)); - ImVec2 P2 = Transformer(Getter2(prim)); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) - return false; - AddLine(P1,P2,Weight,Col,DrawList,uv); - return true; - } - const TGetter1& Getter1; - const TGetter2& Getter2; - const TTransformer& Transformer; - const int Prims; - const ImU32 Col; - const float Weight; - static const int IdxConsumed = 6; - static const int VtxConsumed = 4; -}; + template <typename TGetter1, typename TGetter2, typename TTransformer> struct ShadedRenderer { - ShadedRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col) : + IMPLOT_INLINE ShadedRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col) : Getter1(getter1), Getter2(getter2), Transformer(transformer), @@ -635,7 +668,7 @@ struct ShadedRenderer { P12 = Transformer(Getter2(0)); } - inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { + IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { ImVec2 P21 = Transformer(Getter1(prim+1)); ImVec2 P22 = Transformer(Getter2(prim+1)); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); @@ -666,8 +699,8 @@ struct ShadedRenderer { 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[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4); + DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - intersect); DrawList._IdxWritePtr += 6; DrawList._VtxCurrentIdx += 5; P11 = P21; @@ -685,15 +718,9 @@ struct ShadedRenderer { static const int VtxConsumed = 5; }; -// Stupid way of calculating maximum index size of ImDrawIdx without integer overflow issues -template <typename T> -struct MaxIdx { static const unsigned int Value; }; -template <> const unsigned int MaxIdx<unsigned short>::Value = 65535; -template <> const unsigned int MaxIdx<unsigned int>::Value = 4294967295; - /// Renders primitive shapes in bulk as efficiently as possible. template <typename Renderer> -inline void RenderPrimitives(const Renderer& renderer, ImDrawList& DrawList, const ImRect& cull_rect) { +IMPLOT_INLINE void RenderPrimitives(const Renderer& renderer, ImDrawList& DrawList, const ImRect& cull_rect) { unsigned int prims = renderer.Prims; unsigned int prims_culled = 0; unsigned int idx = 0; @@ -730,7 +757,7 @@ inline void RenderPrimitives(const Renderer& renderer, ImDrawList& DrawList, con } template <typename Getter, typename Transformer> -inline void RenderLineStrip(const Getter& getter, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { +IMPLOT_INLINE void RenderLineStrip(const Getter& getter, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { ImPlotContext& gp = *GImPlot; if (ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased) || gp.Style.AntiAliasedLines) { ImVec2 p1 = transformer(getter(0)); @@ -747,7 +774,7 @@ inline void RenderLineStrip(const Getter& getter, const Transformer& transformer } template <typename Getter1, typename Getter2, typename Transformer> -inline void RenderLineSegments(const Getter1& getter1, const Getter2& getter2, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { +IMPLOT_INLINE void RenderLineSegments(const Getter1& getter1, const Getter2& getter2, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { ImPlotContext& gp = *GImPlot; if (ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased) || gp.Style.AntiAliasedLines) { int I = ImMin(getter1.Count, getter2.Count); @@ -764,7 +791,7 @@ inline void RenderLineSegments(const Getter1& getter1, const Getter2& getter2, c } template <typename Getter, typename Transformer> -inline void RenderStairs(const Getter& getter, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { +IMPLOT_INLINE void RenderStairs(const Getter& getter, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { ImPlotContext& gp = *GImPlot; if (ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased) || gp.Style.AntiAliasedLines) { ImVec2 p1 = transformer(getter(0)); @@ -787,14 +814,14 @@ inline void RenderStairs(const Getter& getter, const Transformer& transformer, I // MARKER RENDERERS //----------------------------------------------------------------------------- -inline void TransformMarker(ImVec2* points, int n, const ImVec2& c, float s) { +IMPLOT_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 RenderMarkerGeneral(ImDrawList& DrawList, ImVec2* points, int n, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerGeneral(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); @@ -804,7 +831,7 @@ inline void RenderMarkerGeneral(ImDrawList& DrawList, ImVec2* points, int n, con } } -inline void RenderMarkerCircle(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerCircle(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), @@ -818,37 +845,37 @@ inline void RenderMarkerCircle(ImDrawList& DrawList, const ImVec2& c, float s, b RenderMarkerGeneral(DrawList, marker, 10, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerDiamond(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerDiamond(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)}; RenderMarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerSquare(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerSquare(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)}; RenderMarkerGeneral(DrawList, marker, 4, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerUp(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerUp(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)}; RenderMarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerDown(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerDown(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)}; RenderMarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerLeft(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerLeft(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)}; RenderMarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerRight(ImDrawList& DrawList, const ImVec2& c, float s, bool outline, ImU32 col_outline, bool fill, ImU32 col_fill, float weight) { +IMPLOT_INLINE void RenderMarkerRight(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)}; RenderMarkerGeneral(DrawList, marker, 3, c, s, outline, col_outline, fill, col_fill, weight); } -inline void RenderMarkerAsterisk(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) { +IMPLOT_INLINE void RenderMarkerAsterisk(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); @@ -856,14 +883,14 @@ inline void RenderMarkerAsterisk(ImDrawList& DrawList, const ImVec2& c, float s, DrawList.AddLine(marker[2], marker[3], col_outline, weight); } -inline void RenderMarkerPlus(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) { +IMPLOT_INLINE void RenderMarkerPlus(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 RenderMarkerCross(ImDrawList& DrawList, const ImVec2& c, float s, bool /*outline*/, ImU32 col_outline, bool /*fill*/, ImU32 /*col_fill*/, float weight) { +IMPLOT_INLINE void RenderMarkerCross(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); @@ -871,7 +898,7 @@ inline void RenderMarkerCross(ImDrawList& DrawList, const ImVec2& c, float s, bo } template <typename Transformer, typename Getter> -inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, ImPlotMarker marker, float size, bool rend_mk_line, ImU32 col_mk_line, float weight, bool rend_mk_fill, ImU32 col_mk_fill) { +IMPLOT_INLINE void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, ImPlotMarker marker, float size, bool rend_mk_line, ImU32 col_mk_line, float weight, bool rend_mk_fill, ImU32 col_mk_fill) { static void (*marker_table[ImPlotMarker_COUNT])(ImDrawList&, const ImVec2&, float s, bool, ImU32, bool, ImU32, float) = { RenderMarkerCircle, RenderMarkerSquare, @@ -885,9 +912,10 @@ inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& Dr RenderMarkerAsterisk }; ImPlotContext& gp = *GImPlot; + const ImRect& rect = gp.CurrentPlot->PlotRect; for (int i = 0; i < getter.Count; ++i) { ImVec2 c = transformer(getter(i)); - if (gp.CurrentPlot->PlotRect.Contains(c)) + if (c.x >= rect.Min.x && c.y >= rect.Min.y && c.x <= rect.Max.x && c.y <= rect.Max.y) marker_table[marker](DrawList, c, size, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, weight); } } @@ -897,7 +925,7 @@ inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& Dr //----------------------------------------------------------------------------- template <typename Getter> -inline void PlotLineEx(const char* label_id, const Getter& getter) { +IMPLOT_INLINE void PlotLineEx(const char* label_id, const Getter& getter) { if (BeginItem(label_id, ImPlotCol_Line)) { if (FitThisFrame()) { for (int i = 0; i < getter.Count; ++i) { @@ -918,8 +946,9 @@ inline void PlotLineEx(const char* label_id, const Getter& getter) { } // render markers if (s.Marker != ImPlotMarker_None) { - PopPlotClipRect(); - PushPlotClipRect(s.MarkerSize); + // uncomment lines below to render markers over plot rect border + // PopPlotClipRect(); + // PushPlotClipRect(s.MarkerSize); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]); switch (GetCurrentScale()) { @@ -933,9 +962,10 @@ inline void PlotLineEx(const char* label_id, const Getter& getter) { } } + template <typename T> void PlotLine(const char* label_id, const T* values, int count, double xscale, double x0, int offset, int stride) { - GetterYs<T> getter(values,count,xscale,x0,offset,stride); + GetterXY<GetterLin,GetterIdx<T>> getter(GetterLin(xscale,x0),GetterIdx<T>(values,count,offset,stride),count); PlotLineEx(label_id, getter); } @@ -952,7 +982,7 @@ template IMPLOT_API void PlotLine<double>(const char* label_id, const double* va template <typename T> void PlotLine(const char* label_id, const T* xs, const T* ys, int count, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); return PlotLineEx(label_id, getter); } @@ -968,8 +998,8 @@ template IMPLOT_API void PlotLine<float>(const char* label_id, const float* xs, template IMPLOT_API void PlotLine<double>(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride); // custom -void PlotLineG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) { - GetterFuncPtr getter(getter_func,data, count, offset); +void PlotLineG(const char* label_id, ImPlotGetter getter_func, void* data, int count) { + GetterFuncPtr getter(getter_func,data, count); return PlotLineEx(label_id, getter); } @@ -978,7 +1008,7 @@ void PlotLineG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int //----------------------------------------------------------------------------- template <typename Getter> -inline void PlotScatterEx(const char* label_id, const Getter& getter) { +IMPLOT_INLINE void PlotScatterEx(const char* label_id, const Getter& getter) { if (BeginItem(label_id, ImPlotCol_MarkerOutline)) { if (FitThisFrame()) { for (int i = 0; i < getter.Count; ++i) { @@ -991,8 +1021,9 @@ inline void PlotScatterEx(const char* label_id, const Getter& getter) { // render markers ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle : s.Marker; if (marker != ImPlotMarker_None) { - PopPlotClipRect(); - PushPlotClipRect(s.MarkerSize); + // uncomment lines below to render markers over plot rect border + // PopPlotClipRect(); + // PushPlotClipRect(s.MarkerSize); const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]); switch (GetCurrentScale()) { @@ -1008,7 +1039,7 @@ inline void PlotScatterEx(const char* label_id, const Getter& getter) { template <typename T> void PlotScatter(const char* label_id, const T* values, int count, double xscale, double x0, int offset, int stride) { - GetterYs<T> getter(values,count,xscale,x0,offset,stride); + GetterXY<GetterLin,GetterIdx<T>> getter(GetterLin(xscale,x0),GetterIdx<T>(values,count,offset,stride),count); PlotScatterEx(label_id, getter); } @@ -1025,7 +1056,7 @@ template IMPLOT_API void PlotScatter<double>(const char* label_id, const double* template <typename T> void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); return PlotScatterEx(label_id, getter); } @@ -1041,8 +1072,8 @@ template IMPLOT_API void PlotScatter<float>(const char* label_id, const float* x template IMPLOT_API void PlotScatter<double>(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride); // custom -void PlotScatterG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) { - GetterFuncPtr getter(getter_func,data, count, offset); +void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, int count) { + GetterFuncPtr getter(getter_func,data, count); return PlotScatterEx(label_id, getter); } @@ -1051,7 +1082,7 @@ void PlotScatterG(const char* label_id, ImPlotPoint (*getter_func)(void* data, i //----------------------------------------------------------------------------- template <typename Getter> -inline void PlotStairsEx(const char* label_id, const Getter& getter) { +IMPLOT_INLINE void PlotStairsEx(const char* label_id, const Getter& getter) { if (BeginItem(label_id, ImPlotCol_Line)) { if (FitThisFrame()) { for (int i = 0; i < getter.Count; ++i) { @@ -1089,7 +1120,7 @@ inline void PlotStairsEx(const char* label_id, const Getter& getter) { template <typename T> void PlotStairs(const char* label_id, const T* values, int count, double xscale, double x0, int offset, int stride) { - GetterYs<T> getter(values,count,xscale,x0,offset,stride); + GetterXY<GetterLin,GetterIdx<T>> getter(GetterLin(xscale,x0),GetterIdx<T>(values,count,offset,stride),count); PlotStairsEx(label_id, getter); } @@ -1106,7 +1137,7 @@ template IMPLOT_API void PlotStairs<double>(const char* label_id, const double* template <typename T> void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); return PlotStairsEx(label_id, getter); } @@ -1122,8 +1153,8 @@ template IMPLOT_API void PlotStairs<float>(const char* label_id, const float* xs template IMPLOT_API void PlotStairs<double>(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride); // custom -void PlotStairsG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) { - GetterFuncPtr getter(getter_func,data, count, offset); +void PlotStairsG(const char* label_id, ImPlotGetter getter_func, void* data, int count) { + GetterFuncPtr getter(getter_func,data, count); return PlotStairsEx(label_id, getter); } @@ -1132,7 +1163,7 @@ void PlotStairsG(const char* label_id, ImPlotPoint (*getter_func)(void* data, in //----------------------------------------------------------------------------- template <typename Getter1, typename Getter2> -inline void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, bool fit2) { +IMPLOT_INLINE void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, bool fit2) { if (BeginItem(label_id, ImPlotCol_Fill)) { if (FitThisFrame()) { for (int i = 0; i < getter1.Count; ++i) @@ -1160,16 +1191,16 @@ inline void PlotShadedEx(const char* label_id, const Getter1& getter1, const Get template <typename T> void PlotShaded(const char* label_id, const T* values, int count, double y_ref, double xscale, double x0, int offset, int stride) { bool fit2 = true; - if (y_ref == -HUGE_VAL) { + if (!(y_ref > -DBL_MAX)) { // filters out nans too fit2 = false; - y_ref = GetPlotLimits().Y.Min; + y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Min; } - if (y_ref == HUGE_VAL) { + if (!(y_ref < DBL_MAX)) { // filters out nans too fit2 = false; - y_ref = GetPlotLimits().Y.Max; + y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Max; } - GetterYs<T> getter1(values,count,xscale,x0,offset,stride); - GetterYRef getter2(y_ref,count,xscale,x0); + GetterXY<GetterLin,GetterIdx<T>> getter1(GetterLin(xscale,x0),GetterIdx<T>(values,count,offset,stride),count); + GetterXY<GetterLin,GetterRef> getter2(GetterLin(xscale,x0),GetterRef(y_ref),count); PlotShadedEx(label_id, getter1, getter2, fit2); } @@ -1187,16 +1218,16 @@ template IMPLOT_API void PlotShaded<double>(const char* label_id, const double* template <typename T> void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double y_ref, int offset, int stride) { bool fit2 = true; - if (y_ref == -HUGE_VAL) { + if (!(y_ref > -DBL_MAX)) { // filters out nans too fit2 = false; - y_ref = GetPlotLimits().Y.Min; + y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Min; } - if (y_ref == HUGE_VAL) { + if (!(y_ref < DBL_MAX)) { // filters out nans too fit2 = false; - y_ref = GetPlotLimits().Y.Max; + y_ref = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO).Y.Max; } - GetterXsYs<T> getter1(xs, ys, count, offset, stride); - GetterXsYRef<T> getter2(xs, y_ref, count, offset, stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter1(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); + GetterXY<GetterIdx<T>,GetterRef> getter2(GetterIdx<T>(xs,count,offset,stride),GetterRef(y_ref),count); PlotShadedEx(label_id, getter1, getter2, fit2); } @@ -1213,8 +1244,8 @@ template IMPLOT_API void PlotShaded<double>(const char* label_id, const double* template <typename T> void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, int offset, int stride) { - GetterXsYs<T> getter1(xs, ys1, count, offset, stride); - GetterXsYs<T> getter2(xs, ys2, count, offset, stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter1(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys1,count,offset,stride),count); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter2(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys2,count,offset,stride),count); PlotShadedEx(label_id, getter1, getter2, true); } @@ -1230,9 +1261,9 @@ template IMPLOT_API void PlotShaded<float>(const char* label_id, const float* xs template IMPLOT_API void PlotShaded<double>(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset, int stride); // custom -void PlotShadedG(const char* label_id, ImPlotPoint (*g1)(void* data, int idx), void* data1, ImPlotPoint (*g2)(void* data, int idx), void* data2, int count, int offset) { - GetterFuncPtr getter1(g1, data1, count, offset); - GetterFuncPtr getter2(g2, data2, count, offset); +void PlotShadedG(const char* label_id, ImPlotGetter getter_func1, void* data1, ImPlotGetter getter_func2, void* data2, int count) { + GetterFuncPtr getter1(getter_func1, data1, count); + GetterFuncPtr getter2(getter_func2, data2, count); PlotShadedEx(label_id, getter1, getter2, true); } @@ -1242,15 +1273,16 @@ void PlotShadedG(const char* label_id, ImPlotPoint (*g1)(void* data, int idx), v // TODO: Migrate to RenderPrimitives -template <typename Getter> -void PlotBarsEx(const char* label_id, const Getter& getter, double width) { +template <typename Getter1, typename Getter2> +void PlotBarsEx(const char* label_id, const Getter1& getter1, const Getter2 getter2, double width) { if (BeginItem(label_id, ImPlotCol_Fill)) { const double half_width = width / 2; if (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)); + for (int i = 0; i < getter1.Count; ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + FitPoint(ImPlotPoint(p1.x - half_width, p1.y)); + FitPoint(ImPlotPoint(p2.x + half_width, p2.y)); } } const ImPlotNextItemData& s = GetItemData(); @@ -1260,12 +1292,20 @@ void PlotBarsEx(const char* label_id, const Getter& getter, double width) { bool rend_line = s.RenderLine; if (s.RenderFill && col_line == col_fill) rend_line = false; - for (int i = 0; i < getter.Count; ++i) { - ImPlotPoint p = getter(i); - if (p.y == 0) + for (int i = 0; i < getter1.Count; ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + if (p1.y == p2.y) continue; - ImVec2 a = PlotToPixels(p.x - half_width, p.y); - ImVec2 b = PlotToPixels(p.x + half_width, 0); + ImVec2 a = PlotToPixels(p1.x - half_width, p1.y,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 b = PlotToPixels(p2.x + half_width, p2.y,IMPLOT_AUTO,IMPLOT_AUTO); + float width_px = ImAbs(a.x-b.x); + if (width_px < 1.0f) { + a.x += a.x > b.x ? (1-width_px) / 2 : (width_px-1) / 2; + b.x += b.x > a.x ? (1-width_px) / 2 : (width_px-1) / 2; + } + // a.x = IM_ROUND(a.x); + // b.x = IM_ROUND(b.x); if (s.RenderFill) DrawList.AddRectFilled(a, b, col_fill); if (rend_line) @@ -1277,8 +1317,9 @@ void PlotBarsEx(const char* label_id, const Getter& getter, double width) { template <typename T> void PlotBars(const char* label_id, const T* values, int count, double width, double shift, int offset, int stride) { - GetterBarV<T> getter(values,shift,count,offset,stride); - PlotBarsEx(label_id, getter, width); + GetterXY<GetterLin,GetterIdx<T>> getter1(GetterLin(1.0,shift),GetterIdx<T>(values,count,offset,stride),count); + GetterXY<GetterLin,GetterRef> getter2(GetterLin(1.0,shift),GetterRef(0),count); + PlotBarsEx(label_id, getter1, getter2, width); } template IMPLOT_API void PlotBars<ImS8>(const char* label_id, const ImS8* values, int count, double width, double shift, int offset, int stride); @@ -1294,8 +1335,9 @@ template IMPLOT_API void PlotBars<double>(const char* label_id, const double* va template <typename T> void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double width, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); - PlotBarsEx(label_id, getter, width); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter1(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); + GetterXY<GetterIdx<T>,GetterRef> getter2(GetterIdx<T>(xs,count,offset,stride),GetterRef(0),count); + PlotBarsEx(label_id, getter1, getter2, width); } template IMPLOT_API void PlotBars<ImS8>(const char* label_id, const ImS8* xs, const ImS8* ys, int count, double width, int offset, int stride); @@ -1310,9 +1352,10 @@ template IMPLOT_API void PlotBars<float>(const char* label_id, const float* xs, template IMPLOT_API void PlotBars<double>(const char* label_id, const double* xs, const double* ys, int count, double width, int offset, int stride); // custom -void PlotBarsG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double width, int offset) { - GetterFuncPtr getter(getter_func, data, count, offset); - PlotBarsEx(label_id, getter, width); +void PlotBarsG(const char* label_id, ImPlotGetter getter_func, void* data, int count, double width) { + GetterFuncPtr getter1(getter_func, data, count); + GetterOverrideY<GetterFuncPtr> getter2(getter1,0); + PlotBarsEx(label_id, getter1, getter2, width); } //----------------------------------------------------------------------------- @@ -1321,15 +1364,16 @@ void PlotBarsG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int // TODO: Migrate to RenderPrimitives -template <typename Getter, typename THeight> -void PlotBarsHEx(const char* label_id, const Getter& getter, THeight height) { +template <typename Getter1, typename Getter2> +void PlotBarsHEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, double height) { if (BeginItem(label_id, ImPlotCol_Fill)) { - const THeight half_height = height / 2; + const double half_height = height / 2; if (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)); + for (int i = 0; i < getter1.Count; ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + FitPoint(ImPlotPoint(p1.x, p1.y - half_height)); + FitPoint(ImPlotPoint(p2.x, p2.y + half_height)); } } const ImPlotNextItemData& s = GetItemData(); @@ -1339,12 +1383,13 @@ void PlotBarsHEx(const char* label_id, const Getter& getter, THeight height) { bool rend_line = s.RenderLine; if (s.RenderFill && col_line == col_fill) rend_line = false; - for (int i = 0; i < getter.Count; ++i) { - ImPlotPoint p = getter(i); - if (p.x == 0) + for (int i = 0; i < getter1.Count; ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + if (p1.x == p2.x) continue; - ImVec2 a = PlotToPixels(0, p.y - half_height); - ImVec2 b = PlotToPixels(p.x, p.y + half_height); + ImVec2 a = PlotToPixels(p1.x, p1.y - half_height,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 b = PlotToPixels(p2.x, p2.y + half_height,IMPLOT_AUTO,IMPLOT_AUTO); if (s.RenderFill) DrawList.AddRectFilled(a, b, col_fill); if (rend_line) @@ -1356,8 +1401,9 @@ void PlotBarsHEx(const char* label_id, const Getter& getter, THeight height) { template <typename T> void PlotBarsH(const char* label_id, const T* values, int count, double height, double shift, int offset, int stride) { - GetterBarH<T> getter(values,shift,count,offset,stride); - PlotBarsHEx(label_id, getter, height); + GetterXY<GetterIdx<T>,GetterLin> getter1(GetterIdx<T>(values,count,offset,stride),GetterLin(1.0,shift),count); + GetterXY<GetterRef,GetterLin> getter2(GetterRef(0),GetterLin(1.0,shift),count); + PlotBarsHEx(label_id, getter1, getter2, height); } template IMPLOT_API void PlotBarsH<ImS8>(const char* label_id, const ImS8* values, int count, double height, double shift, int offset, int stride); @@ -1373,8 +1419,9 @@ template IMPLOT_API void PlotBarsH<double>(const char* label_id, const double* v template <typename T> void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double height, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); - PlotBarsHEx(label_id, getter, height); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter1(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); + GetterXY<GetterRef, GetterIdx<T>> getter2(GetterRef(0),GetterIdx<T>(ys,count,offset,stride),count); + PlotBarsHEx(label_id, getter1, getter2, height); } template IMPLOT_API void PlotBarsH<ImS8>(const char* label_id, const ImS8* xs, const ImS8* ys, int count, double height, int offset, int stride); @@ -1389,11 +1436,122 @@ template IMPLOT_API void PlotBarsH<float>(const char* label_id, const float* xs, template IMPLOT_API void PlotBarsH<double>(const char* label_id, const double* xs, const double* ys, int count, double height, int offset, int stride); // custom -void PlotBarsHG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, double height, int offset) { - GetterFuncPtr getter(getter_func, data, count, offset); - PlotBarsHEx(label_id, getter, height); +void PlotBarsHG(const char* label_id, ImPlotGetter getter_func, void* data, int count, double height) { + GetterFuncPtr getter1(getter_func, data, count); + GetterOverrideX<GetterFuncPtr> getter2(getter1,0); + PlotBarsHEx(label_id, getter1, getter2, height); +} + +//----------------------------------------------------------------------------- +// PLOT BAR GROUPS +//----------------------------------------------------------------------------- + +template <typename T> +void PlotBarGroups(const char* const label_ids[], const T* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags) { + if (ImHasFlag(flags, ImPlotBarGroupsFlags_Stacked)) { + SetupLock(); + GImPlot->TempDouble1.resize(4*groups); + double* temp = GImPlot->TempDouble1.Data; + double* neg = &temp[0]; + double* pos = &temp[groups]; + double* curr_min = &temp[groups*2]; + double* curr_max = &temp[groups*3]; + for (int g = 0; g < groups*2; ++g) + temp[g] = 0; + for (int i = 0; i < items; ++i) { + if (!IsItemHidden(label_ids[i])) { + for (int g = 0; g < groups; ++g) { + double v = (double)values[i*groups+g]; + if (v > 0) { + curr_min[g] = pos[g]; + curr_max[g] = curr_min[g] + v; + pos[g] += v; + } + else { + curr_max[g] = neg[g]; + curr_min[g] = curr_max[g] + v; + neg[g] += v; + } + } + } + GetterXY<GetterLin,GetterIdx<double>> getter1(GetterLin(1.0,shift),GetterIdx<double>(curr_min,groups),groups); + GetterXY<GetterLin,GetterIdx<double>> getter2(GetterLin(1.0,shift),GetterIdx<double>(curr_max,groups),groups); + PlotBarsEx(label_ids[i],getter1,getter2,width); + } + } + else { + const double subwidth = width / items; + for (int i = 0; i < items; ++i) { + const double subshift = (i+0.5)*subwidth - width/2; + PlotBars(label_ids[i],&values[i*groups],groups,subwidth,subshift+shift); + } + } +} + +template IMPLOT_API void PlotBarGroups<ImS8>(const char* const label_ids[], const ImS8* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImU8>(const char* const label_ids[], const ImU8* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImS16>(const char* const label_ids[], const ImS16* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImU16>(const char* const label_ids[], const ImU16* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImS32>(const char* const label_ids[], const ImS32* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImU32>(const char* const label_ids[], const ImU32* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImS64>(const char* const label_ids[], const ImS64* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<ImU64>(const char* const label_ids[], const ImU64* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<float>(const char* const label_ids[], const float* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroups<double>(const char* const label_ids[], const double* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); + +template <typename T> +void PlotBarGroupsH(const char* const label_ids[], const T* values, int items, int groups, double height, double shift, ImPlotBarGroupsFlags flags) { + if (ImHasFlag(flags, ImPlotBarGroupsFlags_Stacked)) { + SetupLock(); + GImPlot->TempDouble1.resize(4*groups); + double* temp = GImPlot->TempDouble1.Data; + double* neg = &temp[0]; + double* pos = &temp[groups]; + double* curr_min = &temp[groups*2]; + double* curr_max = &temp[groups*3]; + for (int g = 0; g < groups*2; ++g) + temp[g] = 0; + for (int i = 0; i < items; ++i) { + if (!IsItemHidden(label_ids[i])) { + for (int g = 0; g < groups; ++g) { + double v = (double)values[i*groups+g]; + if (v > 0) { + curr_min[g] = pos[g]; + curr_max[g] = curr_min[g] + v; + pos[g] += v; + } + else { + curr_max[g] = neg[g]; + curr_min[g] = curr_max[g] + v; + neg[g] += v; + } + } + } + GetterXY<GetterIdx<double>,GetterLin> getter1(GetterIdx<double>(curr_min,groups),GetterLin(1.0,shift),groups); + GetterXY<GetterIdx<double>,GetterLin> getter2(GetterIdx<double>(curr_max,groups),GetterLin(1.0,shift),groups); + PlotBarsHEx(label_ids[i],getter1,getter2,height); + } + } + else { + const double subheight = height / items; + for (int i = 0; i < items; ++i) { + const double subshift = (i+0.5)*subheight - height/2; + PlotBarsH(label_ids[i],&values[i*groups],groups,subheight,subshift+shift); + } + } } +template IMPLOT_API void PlotBarGroupsH<ImS8>(const char* const label_ids[], const ImS8* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImU8>(const char* const label_ids[], const ImU8* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImS16>(const char* const label_ids[], const ImS16* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImU16>(const char* const label_ids[], const ImU16* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImS32>(const char* const label_ids[], const ImS32* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImU32>(const char* const label_ids[], const ImU32* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImS64>(const char* const label_ids[], const ImS64* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<ImU64>(const char* const label_ids[], const ImU64* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<float>(const char* const label_ids[], const float* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); +template IMPLOT_API void PlotBarGroupsH<double>(const char* const label_ids[], const double* values, int items, int groups, double width, double shift, ImPlotBarGroupsFlags flags); + //----------------------------------------------------------------------------- // PLOT ERROR BARS //----------------------------------------------------------------------------- @@ -1415,8 +1573,8 @@ void PlotErrorBarsEx(const char* label_id, const Getter& getter) { const float half_whisker = s.ErrorBarSize * 0.5f; 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); + ImVec2 p1 = PlotToPixels(e.X, e.Y - e.Neg,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 p2 = PlotToPixels(e.X, e.Y + e.Pos,IMPLOT_AUTO,IMPLOT_AUTO); DrawList.AddLine(p1,p2,col, s.ErrorBarWeight); if (rend_whisker) { DrawList.AddLine(p1 - ImVec2(half_whisker, 0), p1 + ImVec2(half_whisker, 0), col, s.ErrorBarWeight); @@ -1482,8 +1640,8 @@ void PlotErrorBarsHEx(const char* label_id, const Getter& getter) { const float half_whisker = s.ErrorBarSize * 0.5f; 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); + ImVec2 p1 = PlotToPixels(e.X - e.Neg, e.Y,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 p2 = PlotToPixels(e.X + e.Pos, e.Y,IMPLOT_AUTO,IMPLOT_AUTO); DrawList.AddLine(p1, p2, col, s.ErrorBarWeight); if (rend_whisker) { DrawList.AddLine(p1 - ImVec2(0, half_whisker), p1 + ImVec2(0, half_whisker), col, s.ErrorBarWeight); @@ -1533,7 +1691,7 @@ template IMPLOT_API void PlotErrorBarsH<double>(const char* label_id, const doub //----------------------------------------------------------------------------- template <typename GetterM, typename GetterB> -inline void PlotStemsEx(const char* label_id, const GetterM& get_mark, const GetterB& get_base) { +IMPLOT_INLINE void PlotStemsEx(const char* label_id, const GetterM& get_mark, const GetterB& get_base) { if (BeginItem(label_id, ImPlotCol_Line)) { if (FitThisFrame()) { for (int i = 0; i < get_base.Count; ++i) { @@ -1573,8 +1731,8 @@ inline void PlotStemsEx(const char* label_id, const GetterM& get_mark, const Get template <typename T> void PlotStems(const char* label_id, const T* values, int count, double y_ref, double xscale, double x0, int offset, int stride) { - GetterYs<T> get_mark(values,count,xscale,x0,offset,stride); - GetterYRef get_base(y_ref,count,xscale,x0); + GetterXY<GetterLin,GetterIdx<T>> get_mark(GetterLin(xscale,x0),GetterIdx<T>(values,count,offset,stride),count); + GetterXY<GetterLin,GetterRef> get_base(GetterLin(xscale,x0),GetterRef(y_ref),count); PlotStemsEx(label_id, get_mark, get_base); } @@ -1591,8 +1749,8 @@ template IMPLOT_API void PlotStems<double>(const char* label_id, const double* v template <typename T> void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double y_ref, int offset, int stride) { - GetterXsYs<T> get_mark(xs,ys,count,offset,stride); - GetterXsYRef<T> get_base(xs,y_ref,count,offset,stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> get_mark(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); + GetterXY<GetterIdx<T>,GetterRef> get_base(GetterIdx<T>(xs,count,offset,stride),GetterRef(y_ref),count); PlotStemsEx(label_id, get_mark, get_base); } @@ -1614,9 +1772,9 @@ template IMPLOT_API void PlotStems<double>(const char* label_id, const double* x template <typename T> void PlotVLines(const char* label_id, const T* xs, int count, int offset, int stride) { if (BeginItem(label_id, ImPlotCol_Line)) { - const ImPlotLimits lims = GetPlotLimits(); - GetterXsYRef<T> get_min(xs,lims.Y.Min,count,offset,stride); - GetterXsYRef<T> get_max(xs,lims.Y.Max,count,offset,stride); + const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); + GetterXY<GetterIdx<T>,GetterRef> get_min(GetterIdx<T>(xs,count,offset,stride),GetterRef(lims.Y.Min),count); + GetterXY<GetterIdx<T>,GetterRef> get_max(GetterIdx<T>(xs,count,offset,stride),GetterRef(lims.Y.Max),count); if (FitThisFrame()) { for (int i = 0; i < get_min.Count; ++i) FitPointX(get_min(i).x); @@ -1652,9 +1810,9 @@ template IMPLOT_API void PlotVLines<double>(const char* label_id, const double* template <typename T> void PlotHLines(const char* label_id, const T* ys, int count, int offset, int stride) { if (BeginItem(label_id, ImPlotCol_Line)) { - const ImPlotLimits lims = GetPlotLimits(); - GetterXRefYs<T> get_min(lims.X.Min,ys,count,offset,stride); - GetterXRefYs<T> get_max(lims.X.Max,ys,count,offset,stride); + const ImPlotRect lims = GetPlotLimits(IMPLOT_AUTO,IMPLOT_AUTO); + GetterXY<GetterRef,GetterIdx<T>> get_min(GetterRef(lims.X.Min),GetterIdx<T>(ys,count,offset,stride),count); + GetterXY<GetterRef,GetterIdx<T>> get_max(GetterRef(lims.X.Max),GetterIdx<T>(ys,count,offset,stride),count); if (FitThisFrame()) { for (int i = 0; i < get_min.Count; ++i) FitPointY(get_min(i).y); @@ -1690,15 +1848,15 @@ template IMPLOT_API void PlotHLines<double>(const char* label_id, const double* // PLOT PIE CHART //----------------------------------------------------------------------------- -inline void RenderPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) { +IMPLOT_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); + buffer[0] = PlotToPixels(center,IMPLOT_AUTO,IMPLOT_AUTO); 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)); + buffer[i + 1] = PlotToPixels(center.x + radius * cos(a), center.y + radius * sin(a),IMPLOT_AUTO,IMPLOT_AUTO); } DrawList.AddConvexPolyFilled(buffer, n + 1, col); } @@ -1719,6 +1877,10 @@ void PlotPieChart(const char* const label_ids[], const T* values, int count, dou double percent = normalize ? (double)values[i] / sum : (double)values[i]; a1 = a0 + 2 * IM_PI * percent; if (BeginItem(label_ids[i])) { + if (FitThisFrame()) { + FitPoint(ImPlotPoint(x-radius,y-radius)); + FitPoint(ImPlotPoint(x+radius,y+radius)); + } ImU32 col = GetCurrentItem()->Color; if (percent < 0.5) { RenderPieSlice(DrawList, center, radius, a0, a1, col); @@ -1740,10 +1902,10 @@ void PlotPieChart(const char* const label_ids[], const T* values, int count, dou double percent = normalize ? (double)values[i] / sum : (double)values[i]; a1 = a0 + 2 * IM_PI * percent; if (item->Show) { - sprintf(buffer, fmt, (double)values[i]); + ImFormatString(buffer, 32, fmt, (double)values[i]); ImVec2 size = ImGui::CalcTextSize(buffer); double angle = a0 + (a1 - a0) * 0.5; - ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle)); + ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle),IMPLOT_AUTO,IMPLOT_AUTO); ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color)); DrawList.AddText(pos - size * 0.5f, col, buffer); } @@ -1775,12 +1937,12 @@ struct RectInfo { template <typename TGetter, typename TTransformer> struct RectRenderer { - inline RectRenderer(const TGetter& getter, const TTransformer& transformer) : + IMPLOT_INLINE RectRenderer(const TGetter& getter, const TTransformer& transformer) : Getter(getter), Transformer(transformer), Prims(Getter.Count) {} - inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { + IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { RectInfo rect = Getter(prim); ImVec2 P1 = Transformer(rect.Min); ImVec2 P2 = Transformer(rect.Max); @@ -1837,7 +1999,7 @@ struct GetterHeatmap { HalfSize(Width*0.5, Height*0.5) { } - inline RectInfo operator()(int idx) const { + template <typename I> IMPLOT_INLINE RectInfo operator()(I idx) const { double val = (double)Values[idx]; const int r = idx / Cols; const int c = idx % Cols; @@ -1894,7 +2056,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - sprintf(buff, fmt, values[i]); + ImFormatString(buff, 32, fmt, values[i]); ImVec2 size = ImGui::CalcTextSize(buff); double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); ImVec4 color = SampleColormap((float)t); @@ -1958,8 +2120,8 @@ double PlotHistogram(const char* label_id, const T* values, int count, int bins, else width = range.Size() / bins; - ImVector<double>& bin_centers = GImPlot->Temp1; - ImVector<double>& bin_counts = GImPlot->Temp2; + ImVector<double>& bin_centers = GImPlot->TempDouble1; + ImVector<double>& bin_counts = GImPlot->TempDouble2; bin_centers.resize(bins); bin_counts.resize(bins); int below = 0; @@ -2026,7 +2188,7 @@ template IMPLOT_API double PlotHistogram<double>(const char* label_id, const dou //----------------------------------------------------------------------------- template <typename T> -double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers) { +double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers) { if (count <= 0 || x_bins == 0 || y_bins == 0) return 0; @@ -2056,7 +2218,7 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count const int bins = x_bins * y_bins; - ImVector<double>& bin_counts = GImPlot->Temp1; + ImVector<double>& bin_counts = GImPlot->TempDouble1; bin_counts.resize(bins); for (int b = 0; b < bins; ++b) @@ -2099,16 +2261,16 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count return max_count; } -template IMPLOT_API double PlotHistogram2D<ImS8>(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImU8>(const char* label_id, const ImU8* xs, const ImU8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImS16>(const char* label_id, const ImS16* xs, const ImS16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImU16>(const char* label_id, const ImU16* xs, const ImU16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImS32>(const char* label_id, const ImS32* xs, const ImS32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImU32>(const char* label_id, const ImU32* xs, const ImU32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImS64>(const char* label_id, const ImS64* xs, const ImS64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<ImU64>(const char* label_id, const ImU64* xs, const ImU64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<float>(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); -template IMPLOT_API double PlotHistogram2D<double>(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImS8>(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImU8>(const char* label_id, const ImU8* xs, const ImU8* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImS16>(const char* label_id, const ImS16* xs, const ImS16* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImU16>(const char* label_id, const ImU16* xs, const ImU16* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImS32>(const char* label_id, const ImS32* xs, const ImS32* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImU32>(const char* label_id, const ImU32* xs, const ImU32* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImS64>(const char* label_id, const ImS64* xs, const ImS64* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<ImU64>(const char* label_id, const ImU64* xs, const ImU64* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<float>(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); +template IMPLOT_API double PlotHistogram2D<double>(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density, ImPlotRect range, bool outliers); //----------------------------------------------------------------------------- // PLOT DIGITAL @@ -2117,13 +2279,16 @@ template IMPLOT_API double PlotHistogram2D<double>(const char* label_id, const d // TODO: Make this behave like all the other plot types (.e. not fixed in y axis) template <typename Getter> -inline void PlotDigitalEx(const char* label_id, Getter getter) { +IMPLOT_INLINE void PlotDigitalEx(const char* label_id, Getter getter) { if (BeginItem(label_id, ImPlotCol_Fill)) { ImPlotContext& gp = *GImPlot; ImDrawList& DrawList = *GetPlotDrawList(); const ImPlotNextItemData& s = GetItemData(); if (getter.Count > 1 && s.RenderFill) { - const int y_axis = GetCurrentYAxis(); + ImPlotPlot& plot = *gp.CurrentPlot; + ImPlotAxis& x_axis = plot.Axes[plot.CurrentX]; + ImPlotAxis& y_axis = plot.Axes[plot.CurrentY]; + int pixYMax = 0; ImPlotPoint itemData1 = getter(0); for (int i = 0; i < getter.Count; ++i) { @@ -2139,24 +2304,24 @@ inline void PlotDigitalEx(const char* label_id, Getter getter) { int pixY_1 = (int)(pixY_1_float); //allow only positive values int pixY_chPosOffset = (int)(ImMax(s.DigitalBitHeight, pixY_1_float) + s.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); + ImVec2 pMin = PlotToPixels(itemData1,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 pMax = PlotToPixels(itemData2,IMPLOT_AUTO,IMPLOT_AUTO); + int pixY_Offset = 0; //20 pixel from bottom due to mouse cursor label + pMin.y = (y_axis.PixelMin) + ((-gp.DigitalPlotOffset) - pixY_Offset); + pMax.y = (y_axis.PixelMin) + ((-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 (ImNanOrInf(itemData2.y)) break; - pMax.x = PlotToPixels(itemData2).x; + pMax.x = PlotToPixels(itemData2,IMPLOT_AUTO,IMPLOT_AUTO).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; + if (pMin.x < x_axis.PixelMin) pMin.x = x_axis.PixelMin; + if (pMax.x < x_axis.PixelMin) pMax.x = x_axis.PixelMin; + if (pMin.x > x_axis.PixelMax) pMin.x = x_axis.PixelMax; + if (pMax.x > x_axis.PixelMax) pMax.x = x_axis.PixelMax; //plot a rectangle that extends up to x2 with y1 height if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) { // ImVec4 colAlpha = item->Color; @@ -2175,7 +2340,7 @@ inline void PlotDigitalEx(const char* label_id, Getter getter) { template <typename T> void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset, int stride) { - GetterXsYs<T> getter(xs,ys,count,offset,stride); + GetterXY<GetterIdx<T>,GetterIdx<T>> getter(GetterIdx<T>(xs,count,offset,stride),GetterIdx<T>(ys,count,offset,stride),count); return PlotDigitalEx(label_id, getter); } @@ -2191,8 +2356,8 @@ template IMPLOT_API void PlotDigital<float>(const char* label_id, const float* x template IMPLOT_API void PlotDigital<double>(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride); // custom -void PlotDigitalG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) { - GetterFuncPtr getter(getter_func,data,count,offset); +void PlotDigitalG(const char* label_id, ImPlotGetter getter_func, void* data, int count) { + GetterFuncPtr getter(getter_func,data,count); return PlotDigitalEx(label_id, getter); } @@ -2209,8 +2374,8 @@ void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPo ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col); GetCurrentItem()->Color = tint_col32; ImDrawList& DrawList = *GetPlotDrawList(); - ImVec2 p1 = PlotToPixels(bmin.x, bmax.y); - ImVec2 p2 = PlotToPixels(bmax.x, bmin.y); + ImVec2 p1 = PlotToPixels(bmin.x, bmax.y,IMPLOT_AUTO,IMPLOT_AUTO); + ImVec2 p2 = PlotToPixels(bmax.x, bmin.y,IMPLOT_AUTO,IMPLOT_AUTO); PushPlotClipRect(); DrawList.AddImage(user_texture_id, p1, p2, uv0, uv1, tint_col32); PopPlotClipRect(); @@ -2225,16 +2390,27 @@ void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPo // double void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& pixel_offset) { IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "PlotText() needs to be called between BeginPlot() and EndPlot()!"); + SetupLock(); ImDrawList & DrawList = *GetPlotDrawList(); PushPlotClipRect(); ImU32 colTxt = GetStyleColorU32(ImPlotCol_InlayText); if (vertical) { - ImVec2 ctr = CalcTextSizeVertical(text) * 0.5f; - ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) + ImVec2(-ctr.x, ctr.y) + pixel_offset; + ImVec2 siz = CalcTextSizeVertical(text) * 0.5f; + ImVec2 ctr = siz * 0.5f; + ImVec2 pos = PlotToPixels(ImPlotPoint(x,y),IMPLOT_AUTO,IMPLOT_AUTO) + ImVec2(-ctr.x, ctr.y) + pixel_offset; + if (FitThisFrame()) { + FitPoint(PixelsToPlot(pos)); + FitPoint(PixelsToPlot(pos.x + siz.x, pos.y - siz.y)); + } AddTextVertical(&DrawList, pos, colTxt, text); } else { - ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) - ImGui::CalcTextSize(text) * 0.5f + pixel_offset; + ImVec2 siz = ImGui::CalcTextSize(text); + ImVec2 pos = PlotToPixels(ImPlotPoint(x,y),IMPLOT_AUTO,IMPLOT_AUTO) - siz * 0.5f + pixel_offset; + if (FitThisFrame()) { + FitPoint(PixelsToPlot(pos)); + FitPoint(PixelsToPlot(pos+siz)); + } DrawList.AddText(pos, colTxt, text); } PopPlotClipRect(); @@ -2249,4 +2425,4 @@ void PlotDummy(const char* label_id) { EndItem(); } -} // namespace ImPlot
\ No newline at end of file +} // namespace ImPlot |