From 7f4f3ecd21dcaf9fc7df54faed6feb1befd1809d Mon Sep 17 00:00:00 2001 From: Jaap Suter Date: Wed, 10 Jun 2020 13:50:00 -0700 Subject: [PATCH 1/5] Added prototype/rough-attempt to allow remapping input (mouse-buttons/modifiers-keys). --- implot.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++------------ implot.h | 26 +++++++++++++++++ 2 files changed, 91 insertions(+), 19 deletions(-) diff --git a/implot.cpp b/implot.cpp index fe1b2fc..035dd63 100644 --- a/implot.cpp +++ b/implot.cpp @@ -811,6 +811,46 @@ struct ImPlotAxisScale { ImPlotPoint Min, Max; }; +//----------------------------------------------------------------------------- +// Input Mapping +//----------------------------------------------------------------------------- + +static ImPlotInputMap GetInputMapDefault() { + ImPlotInputMap i{}; + + i.PanButton = ImGuiMouseButton_Left; + i.PanMod = ImGuiKeyModFlags_None; + + i.BoxSelectButton = ImGuiMouseButton_Right; + i.BoxSelectMod = ImGuiKeyModFlags_None; + + i.BoxCancelButton = ImGuiMouseButton_Left; + + i.QueryClickButton = ImGuiMouseButton_Left; + i.QueryClickMod = ImGuiKeyModFlags_Ctrl; + + i.QueryDragButton = ImGuiMouseButton_Middle; + i.QueryDragMod = ImGuiKeyModFlags_None; + + i.QueryDragButton2 = ImGuiMouseButton_Right; + i.QueryDragMod2 = ImGuiKeyModFlags_Ctrl; + + i.HorizontalSizeMod = ImGuiKeyModFlags_Alt; + i.VerticalSizeMod = ImGuiKeyModFlags_Shift; + + return i; +}; + + +// Global input mapping +static ImPlotInputMap gi = GetInputMapDefault(); + +ImPlotInputMap SetInputMap(const ImPlotInputMap& inputMap) { + ImPlotInputMap prevInputMap = gi; + gi = inputMap; + return prevInputMap; +} + //----------------------------------------------------------------------------- // BeginPlot() //----------------------------------------------------------------------------- @@ -1074,7 +1114,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // QUERY DRAG ------------------------------------------------------------- - if (plot.DraggingQuery && (IO.MouseReleased[0] || !IO.MouseDown[0])) { + if (plot.DraggingQuery && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { plot.DraggingQuery = false; } if (plot.DraggingQuery) { @@ -1085,7 +1125,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (gp.Hov_Frame && gp.Hov_Plot && hov_query && !plot.DraggingQuery && !plot.Selecting && !hov_legend) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; - if (IO.MouseDown[0] && !plot.XAxis.Dragging && !any_y_dragging) { + if (IO.MouseDown[gi.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { plot.DraggingQuery = true; } } @@ -1093,12 +1133,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // DRAG INPUT ------------------------------------------------------------- // end drags - if (plot.XAxis.Dragging && (IO.MouseReleased[0] || !IO.MouseDown[0])) { + if (plot.XAxis.Dragging && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { plot.XAxis.Dragging = false; G.IO.MouseDragMaxDistanceSqr[0] = 0; } for (int i = 0; i < MAX_Y_AXES; i++) { - if (plot.YAxis[i].Dragging && (IO.MouseReleased[0] || !IO.MouseDown[0])) { + if (plot.YAxis[i].Dragging && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { plot.YAxis[i].Dragging = false; G.IO.MouseDragMaxDistanceSqr[0] = 0; } @@ -1155,7 +1195,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } // start drag - if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[0] && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { + if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gi.PanButton] && IO.KeyMods == gi.PanMod && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { if (hov_x_axis_region) { plot.XAxis.Dragging = true; } @@ -1201,22 +1241,22 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // BOX-SELECTION AND QUERY ------------------------------------------------ // confirm selection - if (plot.Selecting && (IO.MouseReleased[1] || !IO.MouseDown[1])) { + if (plot.Selecting && (IO.MouseReleased[gi.BoxSelectButton] || !IO.MouseDown[gi.BoxSelectButton])) { UpdateTransformCache(); ImVec2 select_size = plot.SelectStart - IO.MousePos; if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) { ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); ImPlotPoint p2 = PixelsToPlot(IO.MousePos); - if (!gp.X.LockMin && !IO.KeyAlt) + if (!gp.X.LockMin && IO.KeyMods != gi.HorizontalSizeMod) plot.XAxis.Range.Min = ImMin(p1.x, p2.x); - if (!gp.X.LockMax && !IO.KeyAlt) + if (!gp.X.LockMax && IO.KeyMods != gi.HorizontalSizeMod) plot.XAxis.Range.Max = ImMax(p1.x, p2.x); for (int i = 0; i < MAX_Y_AXES; i++) { p1 = PixelsToPlot(plot.SelectStart, i); p2 = PixelsToPlot(IO.MousePos, i); - if (!gp.Y[i].LockMin && !IO.KeyShift) + if (!gp.Y[i].LockMin && IO.KeyMods != gi.VerticalSizeMod) plot.YAxis[i].Range.Min = ImMin(p1.y, p2.y); - if (!gp.Y[i].LockMax && !IO.KeyShift) + if (!gp.Y[i].LockMax && IO.KeyMods != gi.VerticalSizeMod) plot.YAxis[i].Range.Max = ImMax(p1.y, p2.y); } } @@ -1227,21 +1267,21 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); } // cancel selection - if (plot.Selecting && (IO.MouseClicked[0] || IO.MouseDown[0])) { + if (plot.Selecting && (IO.MouseClicked[gi.BoxCancelButton] || IO.MouseDown[gi.BoxCancelButton])) { plot.Selecting = false; } // begin selection or query - if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[1]) { + if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gi.BoxSelectButton] && IO.KeyMods == gi.BoxSelectMod) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } // update query if (plot.Querying) { UpdateTransformCache(); - plot.QueryRect.Min.x = IO.KeyAlt ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Max.x = IO.KeyAlt ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Min.y = IO.KeyShift ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Max.y = IO.KeyShift ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Min.x = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Max.x = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Min.y = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Max.y = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); plot.QueryRect.Min -= gp.BB_Plot.Min; plot.QueryRect.Max -= gp.BB_Plot.Min; @@ -1256,22 +1296,28 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Queried = false; } } + + const bool isSelectInput = IO.MouseDown[gi.BoxSelectButton] && IO.KeyMods == gi.BoxSelectButton; + const bool isQueryInput = (IO.MouseDown[gi.QueryDragButton] && IO.KeyMods == gi.QueryDragMod) || (IO.MouseDown[gi.QueryDragButton2] && IO.KeyMods == gi.QueryDragMod2); + const bool isQueryInputBegin = (IO.MouseClicked[gi.QueryDragButton] && IO.KeyMods == gi.QueryDragMod) || (IO.MouseClicked[gi.QueryDragButton2] && IO.KeyMods == gi.QueryDragMod2); + + // begin query - if (HasFlag(plot.Flags, ImPlotFlags_Query) && (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[2])) { + if (HasFlag(plot.Flags, ImPlotFlags_Query) && (gp.Hov_Frame && gp.Hov_Plot && isQueryInputBegin)) { plot.QueryRect = ImRect(0,0,0,0); plot.Querying = true; plot.Queried = true; plot.QueryStart = IO.MousePos; } // toggle between select/query - if (HasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && IO.KeyCtrl) { + if (HasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && isQueryInput) { plot.Selecting = false; plot.QueryRect = ImRect(0,0,0,0); plot.Querying = true; plot.Queried = true; plot.QueryStart = plot.SelectStart; } - if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && !IO.KeyCtrl && !IO.MouseDown[2]) { + if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && isSelectInput) { plot.Selecting = true; plot.Querying = false; plot.Queried = false; diff --git a/implot.h b/implot.h index f58b318..5cb3c28 100644 --- a/implot.h +++ b/implot.h @@ -172,12 +172,38 @@ struct ImPlotStyle { ImPlotStyle(); }; +// Input mapping structure. Comments show the default input mapping. +struct ImPlotInputMap { + ImGuiMouseButton PanButton; // left mouse + ImGuiKeyModFlags PanMod; // none + + ImGuiMouseButton BoxSelectButton; // right mouse + ImGuiKeyModFlags BoxSelectMod; // none + + ImGuiMouseButton BoxCancelButton; // left mouse + + ImGuiMouseButton QueryClickButton; // left mouse + ImGuiKeyModFlags QueryClickMod; // ctrl + + ImGuiMouseButton QueryDragButton; // middle mouse + ImGuiKeyModFlags QueryDragMod; // none + + ImGuiMouseButton QueryDragButton2; // right mouse, alternative way to query drag, useful when middle mouse is not available + ImGuiKeyModFlags QueryDragMod2; // ctrl + + ImGuiKeyModFlags HorizontalSizeMod; // alt + ImGuiKeyModFlags VerticalSizeMod; // shift +}; + //----------------------------------------------------------------------------- // Begin/End Plot //----------------------------------------------------------------------------- namespace ImPlot { +// Overrides the input mapping. Returns the previous input mapping. +ImPlotInputMap SetInputMap(const ImPlotInputMap& inputMap); + // Starts a 2D plotting context. If this function returns true, EndPlot() must // be called, e.g. "if (BeginPlot(...)) { ... EndPlot(); }"". #title_id must // be unique. If you need to avoid ID collisions or don't want to display a From f02088fef16cd984bd9ff509acf17f8350ed7f6d Mon Sep 17 00:00:00 2001 From: Jaap Suter Date: Mon, 15 Jun 2020 08:48:22 -0700 Subject: [PATCH 2/5] Changed from `SetInputMap(ImPlotInputMap)` to `ImPlotInputMap& GetInputMap()` per Evan's suggestion. --- README.md | 1 + implot.cpp | 1174 +++++++++++++++++++++++++---------------------- implot.h | 79 ++-- implot_demo.cpp | 85 ++-- 4 files changed, 724 insertions(+), 615 deletions(-) diff --git a/README.md b/README.md index f719b39..7e85441 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - multiple plot types: - line plots + - shaded plots - scatter plots - vertical/horizontal bars graphs - vertical/horizontal error bars diff --git a/implot.cpp b/implot.cpp index 035dd63..496b2c6 100644 --- a/implot.cpp +++ b/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.3 WIP +// ImPlot v0.4 WIP /* @@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. +- 2020/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. @@ -75,8 +76,7 @@ You can read releases logs https://github.com/epezent/implot/releases for more d } \ } -// Special Color used to specific that a plot item color should set determined automatically. -#define IM_COL_AUTO ImVec4(0,0,0,-1) + // The maximum number of support y-axes #define MAX_Y_AXES 3 @@ -88,23 +88,24 @@ ImPlotStyle::ImPlotStyle() { Marker = ImPlotMarker_None; MarkerSize = 4; MarkerWeight = 1; + FillAlpha = 1; ErrorBarSize = 5; ErrorBarWeight = 1.5; DigitalBitHeight = 8; DigitalBitGap = 4; - Colors[ImPlotCol_Line] = IM_COL_AUTO; - Colors[ImPlotCol_Fill] = IM_COL_AUTO; - Colors[ImPlotCol_MarkerOutline] = IM_COL_AUTO; - Colors[ImPlotCol_MarkerFill] = IM_COL_AUTO; - Colors[ImPlotCol_ErrorBar] = IM_COL_AUTO; - Colors[ImPlotCol_FrameBg] = IM_COL_AUTO; - Colors[ImPlotCol_PlotBg] = IM_COL_AUTO; - Colors[ImPlotCol_PlotBorder] = IM_COL_AUTO; - Colors[ImPlotCol_XAxis] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis2] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis3] = IM_COL_AUTO; + Colors[ImPlotCol_Line] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_Fill] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_MarkerOutline] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_MarkerFill] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_ErrorBar] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_FrameBg] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_PlotBg] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_PlotBorder] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_XAxis] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis2] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis3] = IMPLOT_COL_AUTO; Colors[ImPlotCol_Selection] = ImVec4(1,1,0,1); Colors[ImPlotCol_Query] = ImVec4(0,1,0,1); } @@ -177,6 +178,20 @@ inline T Remap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } +// Returns always positive modulo +inline int PosMod(int l, int r) { + return (l % r + r) % r; +} + +// Returns the intersection point of two lines A and B (assumes they are not parallel!) +inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { + float v1 = (a1.x * a2.y - a1.y * a2.x); + float v2 = (b1.x * b2.y - b1.y * b2.x); + float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); + return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, + (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); +} + // Turns NANs to 0s inline double ConstrainNan(double val) { return isnan(val) ? 0 : val; @@ -235,6 +250,11 @@ inline ImVec2 CalcTextSizeVertical(const char *text) { return ImVec2(sz.y, sz.x); } +/// Returns white or black text given background color +inline ImU32 CalcTextColor(const ImVec4& bg) { + return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.729 ? IM_COL32_BLACK : IM_COL32_WHITE; +} + } // private namespace //----------------------------------------------------------------------------- @@ -318,6 +338,7 @@ struct ImPlotAxisColor { struct ImPlotItem { ImPlotItem() { Show = true; + SeenThisFrame = false; Highlight = false; Color = NextColor(); NameOffset = -1; @@ -325,6 +346,7 @@ struct ImPlotItem { } ~ImPlotItem() { ID = 0; } bool Show; + bool SeenThisFrame; bool Highlight; ImVec4 Color; int NameOffset; @@ -473,6 +495,7 @@ struct ImPlotContext { ImVec4* Colormap; int ColormapSize; ImPlotStyle Style; + ImPlotInputMap InputMap; ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImPlotNextPlotData NextPlotData; @@ -488,6 +511,10 @@ static ImPlotContext gp; // Context Utils //----------------------------------------------------------------------------- +ImPlotInputMap& GetInputMap() { + return gp.InputMap; +} + // Returns the next unused default plot color ImVec4 NextColor() { ImVec4 col = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.ColormapSize]; @@ -573,63 +600,6 @@ ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) { return PlotToPixels(plt.x, plt.y, y_axis); } -// Transformer functors - -struct TransformerLinLin { - TransformerLinLin(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - -struct TransformerLogLin { - TransformerLogLin(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - -struct TransformerLinLog { - TransformerLinLog(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - int YAxis; -}; - -struct TransformerLogLog { - TransformerLogLog(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); - t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - //----------------------------------------------------------------------------- // Legend Utils //----------------------------------------------------------------------------- @@ -637,6 +607,9 @@ struct TransformerLogLog { ImPlotItem* RegisterItem(const char* label_id) { ImGuiID id = ImGui::GetID(label_id); ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id); + if (item->SeenThisFrame) + return item; + item->SeenThisFrame = true; int idx = gp.CurrentPlot->Items.GetIndex(item); item->ID = id; gp.LegendIndices.push_back(idx); @@ -655,6 +628,11 @@ ImPlotItem* GetLegendItem(int i) { return gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]); } +ImPlotItem* GetLegendItem(const char* label_id) { + ImGuiID id = ImGui::GetID(label_id); + return gp.CurrentPlot->Items.GetByKey(id); +} + const char* GetLegendLabel(int i) { ImPlotItem* item = gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]); IM_ASSERT(item->NameOffset != -1 && item->NameOffset < gp.LegendLabels.Buf.Size); @@ -811,46 +789,6 @@ struct ImPlotAxisScale { ImPlotPoint Min, Max; }; -//----------------------------------------------------------------------------- -// Input Mapping -//----------------------------------------------------------------------------- - -static ImPlotInputMap GetInputMapDefault() { - ImPlotInputMap i{}; - - i.PanButton = ImGuiMouseButton_Left; - i.PanMod = ImGuiKeyModFlags_None; - - i.BoxSelectButton = ImGuiMouseButton_Right; - i.BoxSelectMod = ImGuiKeyModFlags_None; - - i.BoxCancelButton = ImGuiMouseButton_Left; - - i.QueryClickButton = ImGuiMouseButton_Left; - i.QueryClickMod = ImGuiKeyModFlags_Ctrl; - - i.QueryDragButton = ImGuiMouseButton_Middle; - i.QueryDragMod = ImGuiKeyModFlags_None; - - i.QueryDragButton2 = ImGuiMouseButton_Right; - i.QueryDragMod2 = ImGuiKeyModFlags_Ctrl; - - i.HorizontalSizeMod = ImGuiKeyModFlags_Alt; - i.VerticalSizeMod = ImGuiKeyModFlags_Shift; - - return i; -}; - - -// Global input mapping -static ImPlotInputMap gi = GetInputMapDefault(); - -ImPlotInputMap SetInputMap(const ImPlotInputMap& inputMap) { - ImPlotInputMap prevInputMap = gi; - gi = inputMap; - return prevInputMap; -} - //----------------------------------------------------------------------------- // BeginPlot() //----------------------------------------------------------------------------- @@ -975,17 +913,15 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // adaptive divisions - if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Adaptive)) { - plot.XAxis.Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth()); - if (plot.XAxis.Divisions < 2) - plot.XAxis.Divisions = 2; - } + plot.XAxis.Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth()); + if (plot.XAxis.Divisions < 2) + plot.XAxis.Divisions = 2; + for (int i = 0; i < MAX_Y_AXES; i++) { - if (HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Adaptive)) { - plot.YAxis[i].Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight()); - if (plot.YAxis[i].Divisions < 2) - plot.YAxis[i].Divisions = 2; - } + plot.YAxis[i].Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight()); + if (plot.YAxis[i].Divisions < 2) + plot.YAxis[i].Divisions = 2; + } // COLORS ----------------------------------------------------------------- @@ -1114,7 +1050,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // QUERY DRAG ------------------------------------------------------------- - if (plot.DraggingQuery && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { + if (plot.DraggingQuery && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { plot.DraggingQuery = false; } if (plot.DraggingQuery) { @@ -1125,7 +1061,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (gp.Hov_Frame && gp.Hov_Plot && hov_query && !plot.DraggingQuery && !plot.Selecting && !hov_legend) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; - if (IO.MouseDown[gi.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { + if (IO.MouseDown[gp.InputMap.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { plot.DraggingQuery = true; } } @@ -1133,12 +1069,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // DRAG INPUT ------------------------------------------------------------- // end drags - if (plot.XAxis.Dragging && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { + 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 < MAX_Y_AXES; i++) { - if (plot.YAxis[i].Dragging && (IO.MouseReleased[gi.PanButton] || !IO.MouseDown[gi.PanButton])) { + 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; } @@ -1195,7 +1131,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } // start drag - if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gi.PanButton] && IO.KeyMods == gi.PanMod && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { + if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && IO.KeyMods == gp.InputMap.PanMod && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { if (hov_x_axis_region) { plot.XAxis.Dragging = true; } @@ -1241,22 +1177,22 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // BOX-SELECTION AND QUERY ------------------------------------------------ // confirm selection - if (plot.Selecting && (IO.MouseReleased[gi.BoxSelectButton] || !IO.MouseDown[gi.BoxSelectButton])) { + if (plot.Selecting && (IO.MouseReleased[gp.InputMap.BoxSelectButton] || !IO.MouseDown[gp.InputMap.BoxSelectButton])) { UpdateTransformCache(); ImVec2 select_size = plot.SelectStart - IO.MousePos; if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) { ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); ImPlotPoint p2 = PixelsToPlot(IO.MousePos); - if (!gp.X.LockMin && IO.KeyMods != gi.HorizontalSizeMod) + if (!gp.X.LockMin && IO.KeyMods != gp.InputMap.HorizontalSizeMod) plot.XAxis.Range.Min = ImMin(p1.x, p2.x); - if (!gp.X.LockMax && IO.KeyMods != gi.HorizontalSizeMod) + if (!gp.X.LockMax && IO.KeyMods != gp.InputMap.HorizontalSizeMod) plot.XAxis.Range.Max = ImMax(p1.x, p2.x); for (int i = 0; i < MAX_Y_AXES; i++) { p1 = PixelsToPlot(plot.SelectStart, i); p2 = PixelsToPlot(IO.MousePos, i); - if (!gp.Y[i].LockMin && IO.KeyMods != gi.VerticalSizeMod) + if (!gp.Y[i].LockMin && IO.KeyMods != gp.InputMap.VerticalSizeMod) plot.YAxis[i].Range.Min = ImMin(p1.y, p2.y); - if (!gp.Y[i].LockMax && IO.KeyMods != gi.VerticalSizeMod) + if (!gp.Y[i].LockMax && IO.KeyMods != gp.InputMap.VerticalSizeMod) plot.YAxis[i].Range.Max = ImMax(p1.y, p2.y); } } @@ -1267,21 +1203,21 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); } // cancel selection - if (plot.Selecting && (IO.MouseClicked[gi.BoxCancelButton] || IO.MouseDown[gi.BoxCancelButton])) { + if (plot.Selecting && (IO.MouseClicked[gp.InputMap.BoxCancelButton] || IO.MouseDown[gp.InputMap.BoxCancelButton])) { plot.Selecting = false; } // begin selection or query - if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gi.BoxSelectButton] && IO.KeyMods == gi.BoxSelectMod) { + if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && IO.KeyMods == gp.InputMap.BoxSelectMod) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } // update query if (plot.Querying) { UpdateTransformCache(); - plot.QueryRect.Min.x = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Max.x = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Min.y = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Max.y = IO.KeyMods == gi.HorizontalSizeMod ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Min.x = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Max.x = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Min.y = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Max.y = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); plot.QueryRect.Min -= gp.BB_Plot.Min; plot.QueryRect.Max -= gp.BB_Plot.Min; @@ -1297,9 +1233,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } - const bool isSelectInput = IO.MouseDown[gi.BoxSelectButton] && IO.KeyMods == gi.BoxSelectButton; - const bool isQueryInput = (IO.MouseDown[gi.QueryDragButton] && IO.KeyMods == gi.QueryDragMod) || (IO.MouseDown[gi.QueryDragButton2] && IO.KeyMods == gi.QueryDragMod2); - const bool isQueryInputBegin = (IO.MouseClicked[gi.QueryDragButton] && IO.KeyMods == gi.QueryDragMod) || (IO.MouseClicked[gi.QueryDragButton2] && IO.KeyMods == gi.QueryDragMod2); + const bool isSelectInput = IO.MouseDown[gp.InputMap.BoxSelectButton] && IO.KeyMods == gp.InputMap.BoxSelectButton; + const bool isQueryInput = (IO.MouseDown[gp.InputMap.QueryDragButton] && IO.KeyMods == gp.InputMap.QueryDragMod) || (IO.MouseDown[gp.InputMap.QueryDragButton2] && IO.KeyMods == gp.InputMap.QueryDragMod2); + const bool isQueryInputBegin = (IO.MouseClicked[gp.InputMap.QueryDragButton] && IO.KeyMods == gp.InputMap.QueryDragMod) || (IO.MouseClicked[gp.InputMap.QueryDragButton2] && IO.KeyMods == gp.InputMap.QueryDragMod2); // begin query @@ -1564,9 +1500,6 @@ void PlotContextMenu(ImPlotState& plot) { if (ImGui::MenuItem("Mouse Position",NULL,HasFlag(plot.Flags, ImPlotFlags_MousePos))) { FlipFlag(plot.Flags, ImPlotFlags_MousePos); } - if (ImGui::MenuItem("Cull Data",NULL,HasFlag(plot.Flags, ImPlotFlags_CullData))) { - FlipFlag(plot.Flags, ImPlotFlags_CullData); - } if (ImGui::MenuItem("Anti-Aliased Lines",NULL,HasFlag(plot.Flags, ImPlotFlags_AntiAliased))) { FlipFlag(plot.Flags, ImPlotFlags_AntiAliased); } @@ -1848,6 +1781,11 @@ void EndPlot() { } // CLEANUP ---------------------------------------------------------------- + // reset the plot items for the next frame + for (int i = 0; i < gp.CurrentPlot->Items.GetSize(); ++i) { + gp.CurrentPlot->Items.GetByIndex(i)->SeenThisFrame = false; + } + // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // Reset context for next plot @@ -1999,6 +1937,7 @@ static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight @@ -2091,9 +2030,243 @@ void PopStyleVar(int count) { } //----------------------------------------------------------------------------- -// RENDERING FUNCTIONS +// GETTERS //----------------------------------------------------------------------------- +template +inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) { + idx = PosMod(offset + idx, count); + return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); +} + +template +struct GetterYs { + GetterYs(const T* ys, int count, int offset, int stride) { + Ys = ys; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Ys; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride)); + } +}; + +template +struct GetterXsYs { + GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) { + Xs = xs; Ys = ys; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Xs; + const T* Ys; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride)); + } +}; + +template +struct GetterXsYRef { + GetterXsYRef(const T* xs, T y_ref, int count, int offset, int stride) { + Xs = xs; + YRef = y_ref; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Xs; + T YRef; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef); + } +}; + +struct GetterImVec2 { + GetterImVec2(const ImVec2* data, int count, int offset) { + Data = data; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); } + const ImVec2* Data; + int Count; + int Offset; +}; + +struct GetterImPlotPoint { + GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) { + Data = data; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return Data[idx]; } + const ImPlotPoint* Data; + int Count; + int Offset; +}; + +struct GetterFuncPtrImPlotPoint { + GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) { + getter = g; + Data = d; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return getter(Data, idx); } + ImPlotPoint (*getter)(void* data, int idx); + void* Data; + int Count; + int Offset; +}; + +template +struct GetterBarV { + const T* Ys; T XShift; int Count; int Offset; int Stride; + GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); } +}; + +template +struct GetterBarH { + const T* Xs; T YShift; int Count; int Offset; int Stride; + GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); } +}; + +struct ImPlotPointError { + ImPlotPointError(double _x, double _y, double _neg, double _pos) { + x = _x; y = _y; neg = _neg; pos = _pos; + } + double x, y, neg, pos; +}; + +template +struct GetterError { + const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride; + GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) { + Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride; + } + ImPlotPointError operator()(int idx) { + return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride), + OffsetAndStride(Ys, idx, Count, Offset, Stride), + OffsetAndStride(Neg, idx, Count, Offset, Stride), + OffsetAndStride(Pos, idx, Count, Offset, Stride)); + } +}; + +//----------------------------------------------------------------------------- +// TRANSFORMERS +//----------------------------------------------------------------------------- + +struct TransformerLinLin { + TransformerLinLin(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +struct TransformerLogLin { + TransformerLogLin(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; + x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +struct TransformerLinLog { + TransformerLinLog(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; + y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + int YAxis; +}; + +struct TransformerLogLog { + TransformerLogLog(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; + x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); + t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; + y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +//----------------------------------------------------------------------------- +// RENDERERS +//----------------------------------------------------------------------------- + +/// Renders primitive shapes in bulk as efficiently as possible. +template +inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList) { + int prims = renderer.Prims; + int prims_culled = 0; + int idx = 0; + const ImVec2 uv = DrawList._Data->TexUvWhitePixel; + while (prims) { + // find how many can be reserved up to end of current draw command's limit + int cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed)); + // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time + if (cnt >= ImMin(64, prims)) { + if (prims_culled >= cnt) + prims_culled -= cnt; // reuse previous reservation + else { + DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation + prims_culled = 0; + } + } + else + { + if (prims_culled > 0) { + DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); + prims_culled = 0; + } + cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed)); + DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command + } + prims -= cnt; + for (int ie = idx + cnt; idx != ie; ++idx) { + if (!renderer(DrawList, uv, idx)) + prims_culled++; + } + } + if (prims_culled > 0) + DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); +} + #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f @@ -2181,10 +2354,10 @@ inline void MarkerCross(ImDrawList& DrawList, const ImVec2& c, float s, bool /*o } template -inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, bool rend_mk_line, ImU32 col_mk_line, bool rend_mk_fill, ImU32 col_mk_fill, bool cull) { +inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& DrawList, bool rend_mk_line, ImU32 col_mk_line, bool rend_mk_fill, ImU32 col_mk_fill) { for (int i = 0; i < getter.Count; ++i) { ImVec2 c = transformer(getter(i)); - if (!cull || gp.BB_Plot.Contains(c)) { + if (gp.BB_Plot.Contains(c)) { // TODO: Optimize the loop and if statements, this is atrocious if (HasFlag(gp.Style.Marker, ImPlotMarker_Circle)) MarkerCircle(DrawList, c, gp.Style.MarkerSize, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, gp.Style.MarkerWeight); @@ -2210,9 +2383,23 @@ inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& Dr } } +template struct LineRenderer { - LineRenderer(ImU32 col, float weight) { Col = col; Weight = weight; } - inline void render(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImVec2 uv) { + inline LineRenderer(Getter _getter, Transformer _transformer, ImU32 col, float weight) : + getter(_getter), + transformer(_transformer) + { + Prims = getter.Count - 1; + Col = col; + Weight = weight; + p1 = transformer(getter(0)); + } + inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) { + ImVec2 p2 = transformer(getter(prim + 1)); + if (!gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) { + p1 = p2; + return false; + } float dx = p2.x - p1.x; float dy = p2.y - p1.y; IM_NORMALIZE2F_OVER_ZERO(dx, dy); @@ -2243,272 +2430,158 @@ struct LineRenderer { DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); DrawList._IdxWritePtr += 6; DrawList._VtxCurrentIdx += 4; + p1 = p2; + return true; } + Getter getter; + Transformer transformer; + int Prims; ImU32 Col; float Weight; + ImVec2 p1; static const int IdxConsumed = 6; static const int VtxConsumed = 4; }; -struct FillRenderer { - FillRenderer(ImU32 col, float zero) { Col = col; Zero = zero; } - inline void render(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImVec2 uv) { - const int crosses_zero = (p1.y > Zero && p2.y < Zero) || (p1.y < Zero && p2.y > Zero); // could do y*y < 0 earlier on - const float xmid = p1.x + (p2.x - p1.x) / (p2.y-p1.y) * (Zero - p1.y); - DrawList._VtxWritePtr[0].pos = p1; - DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = Col; - DrawList._VtxWritePtr[1].pos = p2; - DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = Col; - DrawList._VtxWritePtr[2].pos = ImVec2(xmid, Zero); - DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = Col; - DrawList._VtxWritePtr[3].pos = ImVec2(p1.x, Zero); - DrawList._VtxWritePtr[3].uv = uv; - DrawList._VtxWritePtr[3].col = Col; - DrawList._VtxWritePtr[4].pos = ImVec2(p2.x, Zero);; - DrawList._VtxWritePtr[4].uv = uv; - DrawList._VtxWritePtr[4].col = Col; - DrawList._VtxWritePtr += 5; - DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); - DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1 + crosses_zero); - DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); - DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); - DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - crosses_zero); - DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4); - DrawList._IdxWritePtr += 6; - DrawList._VtxCurrentIdx += 5; - } - ImU32 Col; - float Zero; - static const int IdxConsumed = 6; - static const int VtxConsumed = 5; -}; - -struct RectRenderer { - - RectRenderer(ImU32 col) { Col = col; } - - inline void render(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImVec2 uv) { - DrawList._VtxWritePtr[0].pos.x = p1.x; - DrawList._VtxWritePtr[0].pos.y = p1.y; - DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = Col; - DrawList._VtxWritePtr[1].pos.x = p2.x; - DrawList._VtxWritePtr[1].pos.y = p1.y; - DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = Col; - DrawList._VtxWritePtr[2].pos.x = p2.x; - DrawList._VtxWritePtr[2].pos.y = p2.y; - DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = Col; - DrawList._VtxWritePtr[3].pos.x = p1.x; - DrawList._VtxWritePtr[3].pos.y = p2.y; - DrawList._VtxWritePtr[3].uv = uv; - DrawList._VtxWritePtr[3].col = Col; - DrawList._VtxWritePtr += 4; - DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); - DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); - DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); - DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); - DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2); - DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); - DrawList._IdxWritePtr += 6; - DrawList._VtxCurrentIdx += 4; - } - ImU32 Col; - static const int IdxConsumed = 6; - static const int VtxConsumed = 4; -}; - -template -inline void RenderPrimitives(Getter getter, Transformer transformer, Renderer renderer, ImDrawList& DrawList, bool cull) { - ImVec2 p1 = transformer(getter(0)); - int prims = getter.Count - 1; - int i1 = 1; - int prims_culled = 0; - - const ImVec2 uv = DrawList._Data->TexUvWhitePixel; - while (prims) { - // find how many can be reserved up to end of current draw command's limit - int cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed)); - // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time - if (cnt >= ImMin(64, prims)) { - if (prims_culled >= cnt) - prims_culled -= cnt; // reuse previous reservation - else { - DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation - prims_culled = 0; - } - } - else - { - if (prims_culled > 0) { - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); - prims_culled = 0; - } - - cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed)); - DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command - } - prims -= cnt; - for (int ie = i1 + cnt; i1 != ie; ++i1) { - - ImVec2 p2 = transformer(getter(i1)); - // TODO: Put the cull check inside of each Renderer - if (!cull || gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) - renderer.render(DrawList, p1, p2, uv); - else - prims_culled++; - p1 = p2; - } - } - if (prims_culled > 0) - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); -} - template -inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col, bool cull) { +inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { if (HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased)) { ImVec2 p1 = transformer(getter(0)); for (int i = 0; i < getter.Count; ++i) { ImVec2 p2 = transformer(getter(i)); - if (!cull || gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) + if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) DrawList.AddLine(p1, p2, col, line_weight); p1 = p2; } } else { - RenderPrimitives(getter, transformer, LineRenderer(col, line_weight), DrawList, cull); + RenderPrimitives(LineRenderer(getter, transformer, col, line_weight), DrawList); } } -template -inline void RenderLineFill(Getter getter, Transformer transformer, ImDrawList& DrawList, ImU32 col_fill) { - // TODO: Culling - float zero = transformer(0,0).y; - RenderPrimitives(getter, transformer, FillRenderer(col_fill, zero), DrawList, false); -} - -//----------------------------------------------------------------------------- -// DATA GETTERS -//----------------------------------------------------------------------------- - -inline int PosMod(int l, int r) { - return (l % r + r) % r; -} - -// template -// inline T StrideIndex(const T* data, int idx, int stride) { -// return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); -// } - -template -inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) { - idx = PosMod(offset + idx, count); - return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); -} - -template -struct GetterYs { - GetterYs(const T* ys, int count, int offset, int stride) { - Ys = ys; - Count = count; - Offset = PosMod(offset, count);; - Stride = stride; +template +struct ShadedRenderer { + ShadedRenderer(Getter1 _getter1, Getter2 _getter2, Transformer _transformer, ImU32 col) : + getter1(_getter1), + getter2(_getter2), + transformer(_transformer), + Col(col) + { + Prims = ImMin(getter1.Count, getter2.Count) - 1; + p11 = transformer(getter1(0)); + p12 = transformer(getter2(0)); } - const T* Ys; - int Count; - int Offset; - int Stride; - inline ImPlotPoint operator()(int idx) { - return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride)); - } -}; -template -struct GetterXsYs { - GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) { - Xs = xs; Ys = ys; - Count = count; - Offset = PosMod(offset, count);; - Stride = stride; + inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) { + ImVec2 p21 = transformer(getter1(prim+1)); + ImVec2 p22 = transformer(getter2(prim+1)); + const int intersect = (p11.y > p12.y && p22.y > p21.y) || (p12.y > p11.y && p21.y > p22.y); + ImVec2 intersection = Intersection(p11,p21,p12,p22); + DrawList._VtxWritePtr[0].pos = p11; + DrawList._VtxWritePtr[0].uv = uv; + DrawList._VtxWritePtr[0].col = Col; + DrawList._VtxWritePtr[1].pos = p21; + DrawList._VtxWritePtr[1].uv = uv; + DrawList._VtxWritePtr[1].col = Col; + DrawList._VtxWritePtr[2].pos = intersection; + DrawList._VtxWritePtr[2].uv = uv; + DrawList._VtxWritePtr[2].col = Col; + DrawList._VtxWritePtr[3].pos = p12; + DrawList._VtxWritePtr[3].uv = uv; + DrawList._VtxWritePtr[3].col = Col; + DrawList._VtxWritePtr[4].pos = p22; + DrawList._VtxWritePtr[4].uv = uv; + DrawList._VtxWritePtr[4].col = Col; + DrawList._VtxWritePtr += 5; + DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); + DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1 + intersect); + DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); + DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); + DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - intersect); + DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4); + DrawList._IdxWritePtr += 6; + DrawList._VtxCurrentIdx += 5; + p11 = p21; + p12 = p22; + return true; } - const T* Xs; - const T* Ys; - int Count; - int Offset; - int Stride; - inline ImPlotPoint operator()(int idx) { - return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride)); - } -}; - -struct GetterImVec2 { - GetterImVec2(const ImVec2* data, int count, int offset) { - Data = data; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); } - const ImVec2* Data; - int Count; - int Offset; -}; - -struct GetterImPlotPoint { - GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) { - Data = data; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return Data[idx]; } - const ImPlotPoint* Data; - int Count; - int Offset; -}; - -struct GetterFuncPtrImPlotPoint { - GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) { - getter = g; - Data = d; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return getter(Data, idx); } - ImPlotPoint (*getter)(void* data, int idx); - void* Data; - int Count; - int Offset; + Getter1 getter1; + Getter2 getter2; + Transformer transformer; + int Prims; + ImU32 Col; + ImVec2 p11, p12; + static const int IdxConsumed = 6; + static const int VtxConsumed = 5; }; //----------------------------------------------------------------------------- -// PLOT +// RENDERING UTILS +//----------------------------------------------------------------------------- + +// Returns true if a style color is set to be automaticaly determined +inline bool ColorIsAuto(ImPlotCol idx) { + return gp.Style.Colors[idx].w == -1; +} +// Recolors an item from an the current ImPlotCol if it is not automatic (i.e. alpha != -1) +inline void TryRecolorItem(ImPlotItem* item, ImPlotCol idx) { + if (gp.Style.Colors[idx].w != -1) + item->Color = gp.Style.Colors[idx]; +} +// Returns true if lines will render (e.g. basic lines, bar outlines) +inline bool WillLineRender() { + return gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; +} +// Returns true if fills will render (e.g. shaded plots, bar fills) +inline bool WillFillRender() { + return gp.Style.Colors[ImPlotCol_Fill].w != 0 && gp.Style.FillAlpha > 0; +} +// Returns true if marker outlines will render +inline bool WillMarkerOutlineRender() { + return gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0; +} +// Returns true if mark fill will render +inline bool WillMarkerFillRender() { + return gp.Style.Colors[ImPlotCol_MarkerFill].w != 0 && gp.Style.FillAlpha > 0; +} +// Gets the line color for an item +inline ImVec4 GetLineColor(ImPlotItem* item) { + return ColorIsAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line]; +} +// Gets the fill color for an item +inline ImVec4 GetItemFillColor(ImPlotItem* item) { + ImVec4 col = ColorIsAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill]; + col.w *= gp.Style.FillAlpha; + return col; +} +// Gets the marker outline color for an item +inline ImVec4 GetMarkerOutlineColor(ImPlotItem* item) { + return ColorIsAuto(ImPlotCol_MarkerOutline) ? GetLineColor(item) : gp.Style.Colors[ImPlotCol_MarkerOutline]; +} +// Gets the marker fill color for an item +inline ImVec4 GetMarkerFillColor(ImPlotItem* item) { + ImVec4 col = ColorIsAuto(ImPlotCol_MarkerFill) ? GetLineColor(item) :gp.Style.Colors[ImPlotCol_MarkerFill]; + col.w *= gp.Style.FillAlpha; + return col; +} +// Gets the error bar color +inline ImVec4 GetErrorBarColor() { + return ColorIsAuto(ImPlotCol_ErrorBar) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : gp.Style.Colors[ImPlotCol_ErrorBar]; +} + +//----------------------------------------------------------------------------- +// PLOT LINES / MARKERS //----------------------------------------------------------------------------- template inline void PlotEx(const char* label_id, Getter getter) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotEx() needs to be called between BeginPlot() and EndPlot()!"); - ImPlotState* plot = gp.CurrentPlot; - const int y_axis = plot->CurrentYAxis; ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; - - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - const bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - const bool rend_fill = gp.Style.Colors[ImPlotCol_Fill].w > 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - bool cull = HasFlag(plot->Flags, ImPlotFlags_CullData); + TryRecolorItem(item, ImPlotCol_Line); // find data extents if (gp.FitThisFrame) { @@ -2516,52 +2589,40 @@ inline void PlotEx(const char* label_id, Getter getter) ImPlotPoint p = getter(i); FitPoint(p); } - if (rend_fill) { - ImPlotPoint p1 = getter(0); - ImPlotPoint p2 = getter(getter.Count - 1); - p1.y = 0; p2.y = 0; - FitPoint(p1); FitPoint(p2); - } } + + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + ImPlotState* plot = gp.CurrentPlot; + const int y_axis = plot->CurrentYAxis; + PushPlotClipRect(); - // render fill - if (getter.Count > 1 && rend_fill) { - const ImU32 col_fill = ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Fill]); - if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderLineFill(getter, TransformerLogLog(y_axis), DrawList, col_fill); - else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) - RenderLineFill(getter, TransformerLogLin(y_axis), DrawList, col_fill); - else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderLineFill(getter, TransformerLinLog(y_axis), DrawList, col_fill); - else - RenderLineFill(getter, TransformerLinLin(y_axis), DrawList, col_fill); - } // render line - if (getter.Count > 1 && rend_line) { + if (getter.Count > 1 && WillLineRender()) { + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderLineStrip(getter, TransformerLogLog(y_axis), DrawList, line_weight, col_line, cull); + RenderLineStrip(getter, TransformerLogLog(y_axis), DrawList, line_weight, col_line); else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) - RenderLineStrip(getter, TransformerLogLin(y_axis), DrawList, line_weight, col_line, cull); + RenderLineStrip(getter, TransformerLogLin(y_axis), DrawList, line_weight, col_line); else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderLineStrip(getter, TransformerLinLog(y_axis), DrawList, line_weight, col_line, cull); + RenderLineStrip(getter, TransformerLinLog(y_axis), DrawList, line_weight, col_line); else - RenderLineStrip(getter, TransformerLinLin(y_axis), DrawList, line_weight, col_line, cull); + RenderLineStrip(getter, TransformerLinLin(y_axis), DrawList, line_weight, col_line); } // render markers if (gp.Style.Marker != ImPlotMarker_None) { - const bool rend_mk_line = gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0; - const bool rend_mk_fill = gp.Style.Colors[ImPlotCol_MarkerFill].w != 0; - const ImU32 col_mk_line = gp.Style.Colors[ImPlotCol_MarkerOutline].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_MarkerOutline]); - const ImU32 col_mk_fill = gp.Style.Colors[ImPlotCol_MarkerFill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_MarkerFill]); + const bool rend_mk_line = WillMarkerOutlineRender(); + const bool rend_mk_fill = WillMarkerFillRender(); + const ImU32 col_mk_line = ImGui::GetColorU32(GetMarkerOutlineColor(item)); + const ImU32 col_mk_fill = ImGui::GetColorU32(GetMarkerFillColor(item)); if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderMarkers(getter, TransformerLogLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, cull); + RenderMarkers(getter, TransformerLogLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill); else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) - RenderMarkers(getter, TransformerLogLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, cull); + RenderMarkers(getter, TransformerLogLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill); else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderMarkers(getter, TransformerLinLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, cull); + RenderMarkers(getter, TransformerLinLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill); else - RenderMarkers(getter, TransformerLinLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, cull); + RenderMarkers(getter, TransformerLinLin(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill); } PopPlotClipRect(); } @@ -2584,7 +2645,6 @@ void PlotLine(const char* label_id, const ImVec2* data, int count, int offset) { return PlotEx(label_id, getter); } - //----------------------------------------------------------------------------- // double @@ -2676,54 +2736,94 @@ void PlotScatter(const char* label_id, ImPlotPoint (*getter)(void* data, int idx PopStyleVar(vars); } +//----------------------------------------------------------------------------- +// PLOT SHADED +//----------------------------------------------------------------------------- + +template +inline void PlotShadedEx(const char* label_id, Getter1 getter1, Getter2 getter2) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotShaded() needs to be called between BeginPlot() and EndPlot()!"); + + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) + return; + TryRecolorItem(item, ImPlotCol_Fill); + + if (!WillFillRender()) + return; + + // find data extents + if (gp.FitThisFrame) { + for (int i = 0; i < ImMin(getter1.Count, getter2.Count); ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + FitPoint(p1); + FitPoint(p2); + } + } + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + ImPlotState* plot = gp.CurrentPlot; + const int y_axis = plot->CurrentYAxis; + + ImU32 col = ImGui::GetColorU32(GetItemFillColor(item)); + + PushPlotClipRect(); + if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(y_axis), col), DrawList); + else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(y_axis), col), DrawList); + else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(y_axis), col), DrawList); + else + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(y_axis), col), DrawList); + PopPlotClipRect(); +} + +//----------------------------------------------------------------------------- +// float + +void PlotShaded(const char* label_id, const float* xs, const float* ys1, const float* ys2, int count, int offset, int stride) { + GetterXsYs getter1(xs, ys1, count, offset, stride); + GetterXsYs getter2(xs, ys2, count, offset, stride); + PlotShadedEx(label_id, getter1, getter2); +} + +void PlotShaded(const char* label_id, const float* xs, const float* ys, int count, float y_ref, int offset, int stride) { + GetterXsYs getter1(xs, ys, count, offset, stride); + GetterXsYRef getter2(xs, y_ref, count, offset, stride); + PlotShadedEx(label_id, getter1, getter2); +} + +//----------------------------------------------------------------------------- +// double + +void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset, int stride) { + GetterXsYs getter1(xs, ys1, count, offset, stride); + GetterXsYs getter2(xs, ys2, count, offset, stride); + PlotShadedEx(label_id, getter1, getter2); +} + +void PlotShaded(const char* label_id, const double* xs, const double* ys, int count, double y_ref, int offset, int stride) { + GetterXsYs getter1(xs, ys, count, offset, stride); + GetterXsYRef getter2(xs, y_ref, count, offset, stride); + PlotShadedEx(label_id, getter1, getter2); +} + //----------------------------------------------------------------------------- // PLOT BAR V //----------------------------------------------------------------------------- -// TODO: Migrate to RenderPrimitives - -template -struct GetterBarV { - const T* Ys; T XShift; int Count; int Offset; int Stride; - GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); } -}; - -template -struct GetterBarH { - const T* Xs; T YShift; int Count; int Offset; int Stride; - GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); } -}; - - template void PlotBarsEx(const char* label_id, Getter getter, TWidth width) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; + TryRecolorItem(item, ImPlotCol_Fill); - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - bool rend_fill = gp.Style.Colors[ImPlotCol_Fill].w != 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - ImU32 col_fill = gp.Style.Colors[ImPlotCol_Fill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Fill]); - - if (rend_fill && col_line == col_fill) - rend_line = false; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - PushPlotClipRect(); - - TWidth half_width = width / 2; - + const TWidth half_width = width / 2; // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2733,6 +2833,15 @@ void PlotBarsEx(const char* label_id, Getter getter, TWidth width) { } } + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); + ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item)); + const bool rend_fill = WillFillRender(); + bool rend_line = WillLineRender(); + if (rend_fill && col_line == col_fill) + rend_line = false; + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPoint p = getter(i); if (p.y == 0) @@ -2789,31 +2898,14 @@ void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int i template void PlotBarsHEx(const char* label_id, Getter getter, THeight height) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; + TryRecolorItem(item, ImPlotCol_Fill); - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - bool rend_fill = gp.Style.Colors[ImPlotCol_Fill].w != 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - ImU32 col_fill = gp.Style.Colors[ImPlotCol_Fill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Fill]); - - if (rend_fill && col_line == col_fill) - rend_line = false; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - PushPlotClipRect(); - - THeight half_height = height / 2; - + const THeight half_height = height / 2; // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2823,6 +2915,15 @@ void PlotBarsHEx(const char* label_id, Getter getter, THeight height) { } } + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); + ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item)); + const bool rend_fill = WillFillRender(); + bool rend_line = WillLineRender(); + if (rend_fill && col_line == col_fill) + rend_line = false; + + PushPlotClipRect(); + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); for (int i = 0; i < getter.Count; ++i) { ImPlotPoint p = getter(i); if (p.x == 0) @@ -2875,45 +2976,14 @@ void PlotBarsH(const char* label_id, ImPlotPoint (*getter_func)(void* data, int // PLOT ERROR BARS //----------------------------------------------------------------------------- -struct ImPlotPointError { - ImPlotPointError(double _x, double _y, double _neg, double _pos) { - x = _x; y = _y; neg = _neg; pos = _pos; - } - double x, y, neg, pos; -}; - -template -struct GetterError { - const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride; - GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) { - Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride; - } - ImPlotPointError operator()(int idx) { - return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride), - OffsetAndStride(Ys, idx, Count, Offset, Stride), - OffsetAndStride(Neg, idx, Count, Offset, Stride), - OffsetAndStride(Pos, idx, Count, Offset, Stride)); - } -}; - template void PlotErrorBarsEx(const char* label_id, Getter getter) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); - if (item != NULL && item->Show == false) + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) return; - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - PushPlotClipRect(); - - const ImU32 col = gp.Style.Colors[ImPlotCol_ErrorBar].w == -1 ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_ErrorBar]); - const bool rend_whisker = gp.Style.ErrorBarSize > 0; - - const float half_whisker = gp.Style.ErrorBarSize * 0.5f; - // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2923,6 +2993,13 @@ void PlotErrorBarsEx(const char* label_id, Getter getter) { } } + const ImU32 col = ImGui::GetColorU32(GetErrorBarColor()); + const bool rend_whisker = gp.Style.ErrorBarSize > 0; + const float half_whisker = gp.Style.ErrorBarSize * 0.5f; + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPointError e = getter(i); ImVec2 p1 = PlotToPixels(e.x, e.y - e.neg); @@ -2970,20 +3047,10 @@ template void PlotErrorBarsHEx(const char* label_id, Getter getter) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBarsH() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); - if (item != NULL && item->Show == false) + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) return; - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - PushPlotClipRect(); - - const ImU32 col = gp.Style.Colors[ImPlotCol_ErrorBar].w == -1 ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_ErrorBar]); - const bool rend_whisker = gp.Style.ErrorBarSize > 0; - - const float half_whisker = gp.Style.ErrorBarSize * 0.5f; - // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2993,6 +3060,13 @@ void PlotErrorBarsHEx(const char* label_id, Getter getter) { } } + const ImU32 col = ImGui::GetColorU32(GetErrorBarColor()); + const bool rend_whisker = gp.Style.ErrorBarSize > 0; + const float half_whisker = gp.Style.ErrorBarSize * 0.5f; + + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPointError e = getter(i); ImVec2 p1 = PlotToPixels(e.x - e.neg, e.y); @@ -3037,7 +3111,7 @@ void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, co // PLOT PIE CHART //----------------------------------------------------------------------------- -inline void DrawPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) { +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); @@ -3068,16 +3142,16 @@ void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y T a1 = angle0 * 2 * IM_PI / 360.0f; for (int i = 0; i < count; ++i) { ImPlotItem* item = RegisterItem(label_ids[i]); - ImU32 col = ImGui::GetColorU32(item->Color); + ImU32 col = ImGui::GetColorU32(GetItemFillColor(item)); T percent = normalize ? values[i] / sum : values[i]; a1 = a0 + 2 * IM_PI * percent; if (item->Show) { if (percent < 0.5) { - DrawPieSlice(DrawList, center, radius, a0, a1, col); + RenderPieSlice(DrawList, center, radius, a0, a1, col); } else { - DrawPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col); - DrawPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col); + RenderPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col); + RenderPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col); } } a0 = a1; @@ -3087,14 +3161,17 @@ void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y a1 = angle0 * 2 * IM_PI / 360.0f; char buffer[32]; for (int i = 0; i < count; ++i) { + ImPlotItem* item = GetLegendItem(label_ids[i]); T percent = normalize ? values[i] / sum : values[i]; a1 = a0 + 2 * IM_PI * percent; - sprintf(buffer, fmt, values[i]); - ImVec2 size = ImGui::CalcTextSize(buffer); - T angle = a0 + (a1 - a0) * 0.5f; - ImVec2 pos = PlotToPixels(center.x + 0.5f * radius * cos(angle), center.y + 0.5f * radius * sin(angle)); - DrawList.AddText(pos - size * 0.5f + ImVec2(1,1), IM_COL32(0,0,0,255), buffer); - DrawList.AddText(pos - size * 0.5f, IM_COL32(255,255,255,255), buffer); + if (item->Show) { + sprintf(buffer, fmt, values[i]); + ImVec2 size = ImGui::CalcTextSize(buffer); + T angle = a0 + (a1 - a0) * 0.5f; + ImVec2 pos = PlotToPixels(center.x + 0.5f * radius * cos(angle), center.y + 0.5f * radius * sin(angle)); + ImU32 col = CalcTextColor(GetItemFillColor(item)); + DrawList.AddText(pos - size * 0.5f, col, buffer); + } a0 = a1; } } @@ -3134,13 +3211,13 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value ImVec2 b = transformer(p.x + half_size.x, p.y + half_size.y); float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1)); ImVec4 color = LerpColormap(t); + color.w *= gp.Style.FillAlpha; ImU32 col = ImGui::GetColorU32(color); DrawList.AddRectFilled(a, b, col); i++; } } if (fmt != NULL) { - // this has to go in its own loop due to PrimReserve above i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { @@ -3151,7 +3228,10 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value char buff[32]; sprintf(buff, fmt, values[i]); ImVec2 size = ImGui::CalcTextSize(buff); - DrawList.AddText(px - size * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buff); + float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1)); + ImVec4 color = LerpColormap(t); + ImU32 col = CalcTextColor(color); + DrawList.AddText(px - size * 0.5f, col, buff); i++; } } @@ -3210,24 +3290,14 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; - - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - const bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true); - bool cull = HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_CullData); - - const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; - - const int ax = gp.CurrentPlot->CurrentYAxis; + TryRecolorItem(item, ImPlotCol_Line); // render digital signals as "pixel bases" rectangles - if (count > 1 && rend_line) { - // + PushPlotClipRect(); + if (count > 1 && WillLineRender()) { + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; + const int y_axis = gp.CurrentPlot->CurrentYAxis; const int segments = count - 1; int i1 = offset; int pixYMax = 0; @@ -3245,9 +3315,8 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of 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[ax].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset); - pMax.y = (gp.PixelRange[ax].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset); + pMin.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset); + pMax.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset); //plot only one rectangle for same digital state while (((s+2) < segments) && (itemData1.y == itemData2.y)) { const int i3 = (i1 + 1) % count; @@ -3257,12 +3326,12 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of s++; } //do not extend plot outside plot range - if (pMin.x < gp.PixelRange[ax].Min.x) pMin.x = gp.PixelRange[ax].Min.x; - if (pMax.x < gp.PixelRange[ax].Min.x) pMax.x = gp.PixelRange[ax].Min.x; - if (pMin.x > gp.PixelRange[ax].Max.x) pMin.x = gp.PixelRange[ax].Max.x; - if (pMax.x > gp.PixelRange[ax].Max.x) pMax.x = gp.PixelRange[ax].Max.x; + if (pMin.x < gp.PixelRange[y_axis].Min.x) pMin.x = gp.PixelRange[y_axis].Min.x; + if (pMax.x < gp.PixelRange[y_axis].Min.x) pMax.x = gp.PixelRange[y_axis].Min.x; + if (pMin.x > gp.PixelRange[y_axis].Max.x) pMin.x = gp.PixelRange[y_axis].Max.x; + if (pMax.x > gp.PixelRange[y_axis].Max.x) pMax.x = gp.PixelRange[y_axis].Max.x; //plot a rectangle that extends up to x2 with y1 height - if ((pMax.x > pMin.x) && (!cull || gp.BB_Plot.Contains(pMin) || gp.BB_Plot.Contains(pMax))) { + if ((pMax.x > pMin.x) && (gp.BB_Plot.Contains(pMin) || gp.BB_Plot.Contains(pMax))) { ImVec4 colAlpha = item->Color; colAlpha.w = item->Highlight ? 1.0f : 0.9f; DrawList.AddRectFilled(pMin, pMax, ImGui::GetColorU32(colAlpha)); @@ -3271,8 +3340,7 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of gp.DigitalPlotItemCnt++; gp.DigitalPlotOffset += pixYMax; } - - ImGui::PopClipRect(); + PopPlotClipRect(); } //----------------------------------------------------------------------------- @@ -3417,7 +3485,7 @@ void ShowColormapScale(double scale_min, double scale_max, float height) { void SetColormap(ImPlotColormap colormap, int samples) { static int csizes[ImPlotColormap_COUNT] = {10,9,9,12,11,11,11,11,11,11}; static OffsetCalculator coffs(csizes); - static ImVec4 cdata[] { + static ImVec4 cdata[] = { // ImPlotColormap_Default // X11 Named Colors ImVec4(0.0f, 0.7490196228f, 1.0f, 1.0f), // Blues::DeepSkyBlue, ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // Reds::Red, @@ -3535,10 +3603,8 @@ void SetColormap(ImPlotColormap colormap, int samples) { ImVec4(1.0000f, 0.3333f, 0.f, 1.0f), ImVec4(1.0000f, 0.f, 0.f, 1.0f) }; - // TODO: Calculate offsets at compile time gp.Colormap = &cdata[coffs.Offsets[colormap]]; gp.ColormapSize = csizes[colormap]; - if (samples > 1) { static ImVector resampled; resampled.resize(samples); diff --git a/implot.h b/implot.h index 5cb3c28..c042ccb 100644 --- a/implot.h +++ b/implot.h @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.3 WIP +// ImPlot v0.4 WIP #pragma once #include "imgui.h" @@ -45,12 +45,11 @@ enum ImPlotFlags_ { ImPlotFlags_Query = 1 << 4, // the user will be able to draw query rects with middle-mouse ImPlotFlags_ContextMenu = 1 << 5, // the user will be able to open a context menu with double-right click ImPlotFlags_Crosshairs = 1 << 6, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_CullData = 1 << 7, // plot data outside the plot area will be culled from rendering - ImPlotFlags_AntiAliased = 1 << 8, // lines and fills will be anti-aliased (not recommended) - ImPlotFlags_NoChild = 1 << 9, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_YAxis2 = 1 << 10, // enable a 2nd y axis - ImPlotFlags_YAxis3 = 1 << 11, // enable a 3rd y axis - ImPlotFlags_Default = ImPlotFlags_MousePos | ImPlotFlags_Legend | ImPlotFlags_Highlight | ImPlotFlags_BoxSelect | ImPlotFlags_ContextMenu | ImPlotFlags_CullData + ImPlotFlags_AntiAliased = 1 << 7, // lines and fills will be anti-aliased (not recommended) + ImPlotFlags_NoChild = 1 << 8, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) + ImPlotFlags_YAxis2 = 1 << 9, // enable a 2nd y-axis + ImPlotFlags_YAxis3 = 1 << 10, // enable a 3rd y-axis + ImPlotFlags_Default = ImPlotFlags_MousePos | ImPlotFlags_Legend | ImPlotFlags_Highlight | ImPlotFlags_BoxSelect | ImPlotFlags_ContextMenu }; // Options for plot axes (X and Y). @@ -61,10 +60,9 @@ enum ImPlotAxisFlags_ { ImPlotAxisFlags_Invert = 1 << 3, // the axis will be inverted ImPlotAxisFlags_LockMin = 1 << 4, // the axis minimum value will be locked when panning/zooming ImPlotAxisFlags_LockMax = 1 << 5, // the axis maximum value will be locked when panning/zooming - ImPlotAxisFlags_Adaptive = 1 << 6, // grid divisions will adapt to the current pixel size the axis - ImPlotAxisFlags_LogScale = 1 << 7, // a logartithmic (base 10) axis scale will be used - ImPlotAxisFlags_Scientific = 1 << 8, // scientific notation will be used for tick labels if displayed (WIP, not very good yet) - ImPlotAxisFlags_Default = ImPlotAxisFlags_GridLines | ImPlotAxisFlags_TickMarks | ImPlotAxisFlags_TickLabels | ImPlotAxisFlags_Adaptive, + ImPlotAxisFlags_LogScale = 1 << 6, // a logartithmic (base 10) axis scale will be used + ImPlotAxisFlags_Scientific = 1 << 7, // scientific notation will be used for tick labels if displayed (WIP, not very good yet) + ImPlotAxisFlags_Default = ImPlotAxisFlags_GridLines | ImPlotAxisFlags_TickMarks | ImPlotAxisFlags_TickLabels, ImPlotAxisFlags_Auxiliary = ImPlotAxisFlags_Default & ~ImPlotAxisFlags_GridLines, }; @@ -93,6 +91,7 @@ enum ImPlotStyleVar_ { ImPlotStyleVar_Marker, // int, marker specification ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") ImPlotStyleVar_MarkerWeight, // float, outline weight of markers in pixels + ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels @@ -164,6 +163,7 @@ struct ImPlotStyle { ImPlotMarker Marker; // = ImPlotMarker_None, marker specification float MarkerSize; // = 4, marker size in pixels (roughly the marker's "radius") float MarkerWeight; // = 1, outline weight of markers in pixels + float FillAlpha; // = 1, alpha modifier applied to plot fills float ErrorBarSize; // = 5, error bar whisker width in pixels float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels float DigitalBitHeight; // = 8, digital channels bit height (at y = 1.0f) in pixels @@ -172,27 +172,27 @@ struct ImPlotStyle { ImPlotStyle(); }; -// Input mapping structure. Comments show the default input mapping. +// Input mapping structure, and their default values. struct ImPlotInputMap { - ImGuiMouseButton PanButton; // left mouse - ImGuiKeyModFlags PanMod; // none - - ImGuiMouseButton BoxSelectButton; // right mouse - ImGuiKeyModFlags BoxSelectMod; // none - - ImGuiMouseButton BoxCancelButton; // left mouse + ImGuiMouseButton PanButton = ImGuiMouseButton_Left; + ImGuiKeyModFlags PanMod = ImGuiKeyModFlags_None; - ImGuiMouseButton QueryClickButton; // left mouse - ImGuiKeyModFlags QueryClickMod; // ctrl + ImGuiMouseButton BoxSelectButton = ImGuiMouseButton_Right; + ImGuiKeyModFlags BoxSelectMod = ImGuiKeyModFlags_None; - ImGuiMouseButton QueryDragButton; // middle mouse - ImGuiKeyModFlags QueryDragMod; // none + ImGuiMouseButton BoxCancelButton = ImGuiMouseButton_Left; - ImGuiMouseButton QueryDragButton2; // right mouse, alternative way to query drag, useful when middle mouse is not available - ImGuiKeyModFlags QueryDragMod2; // ctrl + ImGuiMouseButton QueryClickButton = ImGuiMouseButton_Left; + ImGuiKeyModFlags QueryClickMod = ImGuiKeyModFlags_Ctrl; - ImGuiKeyModFlags HorizontalSizeMod; // alt - ImGuiKeyModFlags VerticalSizeMod; // shift + ImGuiMouseButton QueryDragButton = ImGuiMouseButton_Middle; + ImGuiKeyModFlags QueryDragMod = ImGuiKeyModFlags_None; + + ImGuiMouseButton QueryDragButton2 = ImGuiMouseButton_Right; + ImGuiKeyModFlags QueryDragMod2 = ImGuiKeyModFlags_Ctrl; + + ImGuiKeyModFlags HorizontalSizeMod = ImGuiKeyModFlags_Alt; + ImGuiKeyModFlags VerticalSizeMod = ImGuiKeyModFlags_Shift; }; //----------------------------------------------------------------------------- @@ -201,9 +201,6 @@ struct ImPlotInputMap { namespace ImPlot { -// Overrides the input mapping. Returns the previous input mapping. -ImPlotInputMap SetInputMap(const ImPlotInputMap& inputMap); - // Starts a 2D plotting context. If this function returns true, EndPlot() must // be called, e.g. "if (BeginPlot(...)) { ... EndPlot(); }"". #title_id must // be unique. If you need to avoid ID collisions or don't want to display a @@ -244,6 +241,12 @@ void PlotScatter(const char* label_id, const ImVec2* data, int count, int offset void PlotScatter(const char* label_id, const ImPlotPoint* data, int count, int offset = 0); void PlotScatter(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset = 0); +// Plots a shaded (filled) region between two lines, or a line and a horizontal reference. +void PlotShaded(const char* label_id, const float* xs, const float* ys1, const float* ys2, int count, int offset = 0, int stride = sizeof(float)); +void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset = 0, int stride = sizeof(double)); +void PlotShaded(const char* label_id, const float* xs, const float* ys, int count, float y_ref = 0, int offset = 0, int stride = sizeof(float)); +void PlotShaded(const char* label_id, const double* xs, const double* ys, int count, double y_ref = 0, int offset = 0, int stride = sizeof(double)); + // Plots a vertical bar graph. void PlotBars(const char* label_id, const float* values, int count, float width = 0.67f, float shift = 0, int offset = 0, int stride = sizeof(float)); void PlotBars(const char* label_id, const double* values, int count, double width = 0.67f, double shift = 0, int offset = 0, int stride = sizeof(double)); @@ -303,12 +306,22 @@ bool IsPlotQueried(); ImPlotLimits GetPlotQuery(int y_axis = -1); //----------------------------------------------------------------------------- -// Plot Styling +// Plot Input Mapping +//----------------------------------------------------------------------------- + +// Allows changing how keyboard/mouse interaction works. +ImPlotInputMap& GetInputMap(); + +//----------------------------------------------------------------------------- +// Plot Styling and Behaviour //----------------------------------------------------------------------------- // Provides access to plot style structure for permanant modifications to colors, sizes, etc. ImPlotStyle& GetStyle(); +// Special color used to indicate that a style color should be deduced automatically from defaults or colormaps. +#define IMPLOT_COL_AUTO ImVec4(0,0,0,-1) + // Temporarily modify a plot color. Don't forget to call PopStyleColor! void PushStyleColor(ImPlotCol idx, ImU32 col); // Temporarily modify a plot color. Don't forget to call PopStyleColor! @@ -327,9 +340,9 @@ void PopStyleVar(int count = 1); void SetColormap(ImPlotColormap colormap, int samples = 0); // Sets a custom colormap. void SetColormap(const ImVec4* colors, int num_colors); -// Returns the size of the current colormap +// Returns the size of the current colormap. int GetColormapSize(); -// Returns a color from the Color map given an index > 0 (modulo will be performed) +// Returns a color from the Color map given an index >= 0 (modulo will be performed) ImVec4 GetColormapColor(int index); // Linearly interpolates a color from the current colormap given t between 0 and 1. ImVec4 LerpColormap(float t); diff --git a/implot_demo.cpp b/implot_demo.cpp index d145d5b..d17d4fa 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.3 WIP +// ImPlot v0.4 WIP #include "implot.h" #include @@ -31,6 +31,8 @@ #define sprintf sprintf_s #endif +namespace ImPlot { + /// Choose whether the demo uses double or float versions of the ImPlot API. /// NB: You don't ever need to typdef of define values for ImPlot. This /// is only being done here for the sake of demoing both precision types. @@ -54,8 +56,6 @@ typedef ImVec2 t_float2; #define Fmod fmodf #endif -namespace ImPlot { - t_float RandomRange(t_float min, t_float max) { t_float scale = rand() / (t_float) RAND_MAX; return min + scale * ( max - min ); @@ -114,7 +114,7 @@ struct BenchmarkItem { } Col = ImVec4((float)RandomRange(0,1),(float)RandomRange(0,1),(float)RandomRange(0,1),1); } - ~BenchmarkItem() { delete Data; } + ~BenchmarkItem() { delete[] Data; } t_float2* Data; ImVec4 Col; }; @@ -137,7 +137,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::EndMenuBar(); } //------------------------------------------------------------------------- - ImGui::Text("ImPlot says hello. (0.3 WIP)"); + ImGui::Text("ImPlot says hello. (0.4 WIP)"); if (ImGui::CollapsingHeader("Help")) { ImGui::Text("USER GUIDE:"); ImGui::BulletText("Left click and drag within the plot area to pan X and Y axes."); @@ -171,7 +171,6 @@ void ShowDemoWindow(bool* p_open) { #else ImGui::BulletText("The demo data precision is: float"); #endif - } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Line Plots")) { @@ -198,7 +197,7 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Filled Plots")) { + if (ImGui::CollapsingHeader("Filled Line Plots")) { static t_float xs1[101], ys1[101], ys2[101], ys3[101]; srand(0); for (int i = 0; i < 101; ++i) { @@ -207,20 +206,51 @@ void ShowDemoWindow(bool* p_open) { ys2[i] = RandomRange(275,350); ys3[i] = RandomRange(150,225); } + static bool show_lines = true; + static bool show_fills = true; + static float fill_ref = 0; + ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); + ImGui::Checkbox("Fills",&show_fills); + ImGui::DragFloat("Reference",&fill_ref, 1, -100, 500); ImPlot::SetNextPlotLimits(0,100,0,500); if (ImPlot::BeginPlot("Stock Prices", "Days", "Price")) { - ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(1,1,0,1)); - ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4(1,1,0,0.25f)); - ImPlot::PlotLine("Stock 1", xs1, ys1, 101); - ImPlot::PopStyleColor(2); - ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(1,0,1,1)); - ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4(1,0,1,0.25f)); - ImPlot::PlotLine("Stock 2", xs1, ys2, 101); - ImPlot::PopStyleColor(2); - ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(1,0,0,1)); - ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4(1,0,0,0.25f)); - ImPlot::PlotLine("Stock 3", xs1, ys3, 101); - ImPlot::PopStyleColor(2); + if (show_fills) { + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, fill_ref); + ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, fill_ref); + ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, 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(); + } + } + //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Shaded Plots")) { + static t_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 * Sin(25 * xs[i]) * Sin(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 * Sin(25 * xs[i]); + ys4[i] = 0.75f + 0.1f * Cos(25 * xs[i]); + } + static float alpha = 0.25f; + ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); + 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(); } } @@ -241,11 +271,9 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotScatter("Data 1", xs1, ys1, 100); ImPlot::PushStyleVar(ImPlotStyleVar_MarkerSize, 6); ImPlot::PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Square); - ImPlot::PushStyleColor(ImPlotCol_MarkerFill, ImVec4(1,0,0,0.25f)); - ImPlot::PushStyleColor(ImPlotCol_MarkerOutline, ImVec4(0,0,0,0)); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotScatter("Data 2", xs2, ys2, 50); - ImPlot::PopStyleColor(2); - ImPlot::PopStyleVar(2); + ImPlot::PopStyleVar(3); ImPlot::EndPlot(); } } @@ -277,7 +305,6 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotBars("Final Exam", final, 10, 0.2f, 0); ImPlot::PlotBars("Course Grade", grade, 10, 0.2f, 0.2f); } - ImPlot::SetColormap(ImPlotColormap_Default); ImPlot::EndPlot(); } } @@ -300,7 +327,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PushStyleVar(ImPlotStyleVar_ErrorBarSize, size); ImPlot::PushStyleVar(ImPlotStyleVar_ErrorBarWeight, weight); ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); - // error bars should have the same label ID as the associated plot + // error bars can be grouped with the associated item by using the same label ID ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); ImPlot::PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Circle); ImPlot::PushStyleVar(ImPlotStyleVar_MarkerSize, 3); @@ -415,9 +442,9 @@ void ShowDemoWindow(bool* p_open) { static int rt_axis = ImPlotAxisFlags_Default & ~ImPlotAxisFlags_TickLabels; if (ImPlot::BeginPlot("##Scrolling", NULL, NULL, ImVec2(-1,150), ImPlotFlags_Default, rt_axis, rt_axis | ImPlotAxisFlags_LockMin)) { ImPlot::PlotLine("Data 1", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), sdata1.Offset, 2 * sizeof(t_float)); - ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4(1,0,0,0.25f)); - ImPlot::PlotLine("Data 2", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), sdata2.Offset, 2 * sizeof(t_float)); - ImPlot::PopStyleColor(); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::PlotShaded("Data 2", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2 * sizeof(t_float)); + ImPlot::PopStyleVar(); ImPlot::EndPlot(); } ImPlot::SetNextPlotLimitsX(0, history, ImGuiCond_Always); @@ -712,6 +739,7 @@ void ShowDemoWindow(bool* p_open) { } ImGui::EndGroup(); ImGui::SameLine(); + srand((unsigned int)ImGui::GetTime()*10000000); static t_float t = 0; if (!paused) { t += ImGui::GetIO().DeltaTime; @@ -972,6 +1000,7 @@ void ShowDemoWindow(bool* p_open) { static BenchmarkItem items[n_items]; ImGui::BulletText("Make sure VSync is disabled."); ImGui::BulletText("%d lines with %d points each @ %.3f FPS.",n_items,1000,ImGui::GetIO().Framerate); + ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); if (ImPlot::BeginPlot("##Bench",NULL,NULL,ImVec2(-1,0),ImPlotFlags_Default | ImPlotFlags_NoChild)) { char buff[16]; for (int i = 0; i < 100; ++i) { From ce79420146ba088978a9a9a103163326b6cc20db Mon Sep 17 00:00:00 2001 From: Jaap Suter Date: Mon, 15 Jun 2020 09:55:30 -0700 Subject: [PATCH 3/5] Switched from in-class initialization to setting the default input mapping in a constructor in the .cpp file. --- implot.cpp | 24 ++++++++++++++++++++++++ implot.h | 39 +++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/implot.cpp b/implot.cpp index 496b2c6..f1ecfac 100644 --- a/implot.cpp +++ b/implot.cpp @@ -133,6 +133,30 @@ bool ImPlotLimits::Contains(double x, double y) const { return X.Contains(x) && Y.Contains(y); } +ImPlotInputMap::ImPlotInputMap() { + + PanButton = ImGuiMouseButton_Left; + PanMod = ImGuiKeyModFlags_None; + + BoxSelectButton = ImGuiMouseButton_Right; + BoxSelectMod = ImGuiKeyModFlags_None; + + BoxCancelButton = ImGuiMouseButton_Left; + + QueryClickButton = ImGuiMouseButton_Left; + QueryClickMod = ImGuiKeyModFlags_Ctrl; + + QueryDragButton = ImGuiMouseButton_Middle; + QueryDragMod = ImGuiKeyModFlags_None; + + QueryDragButton2 = ImGuiMouseButton_Right; + QueryDragMod2 = ImGuiKeyModFlags_Ctrl; + + HorizontalSizeMod = ImGuiKeyModFlags_Alt; + VerticalSizeMod = ImGuiKeyModFlags_Shift; +} + + namespace ImPlot { namespace { diff --git a/implot.h b/implot.h index c042ccb..2174fca 100644 --- a/implot.h +++ b/implot.h @@ -172,27 +172,30 @@ struct ImPlotStyle { ImPlotStyle(); }; -// Input mapping structure, and their default values. +// Input mapping structure, default values listed in the comments. struct ImPlotInputMap { - ImGuiMouseButton PanButton = ImGuiMouseButton_Left; - ImGuiKeyModFlags PanMod = ImGuiKeyModFlags_None; - ImGuiMouseButton BoxSelectButton = ImGuiMouseButton_Right; - ImGuiKeyModFlags BoxSelectMod = ImGuiKeyModFlags_None; + ImPlotInputMap(); - ImGuiMouseButton BoxCancelButton = ImGuiMouseButton_Left; - - ImGuiMouseButton QueryClickButton = ImGuiMouseButton_Left; - ImGuiKeyModFlags QueryClickMod = ImGuiKeyModFlags_Ctrl; - - ImGuiMouseButton QueryDragButton = ImGuiMouseButton_Middle; - ImGuiKeyModFlags QueryDragMod = ImGuiKeyModFlags_None; - - ImGuiMouseButton QueryDragButton2 = ImGuiMouseButton_Right; - ImGuiKeyModFlags QueryDragMod2 = ImGuiKeyModFlags_Ctrl; - - ImGuiKeyModFlags HorizontalSizeMod = ImGuiKeyModFlags_Alt; - ImGuiKeyModFlags VerticalSizeMod = ImGuiKeyModFlags_Shift; + ImGuiMouseButton PanButton; // left mouse + ImGuiKeyModFlags PanMod; // none + + ImGuiMouseButton BoxSelectButton; // right mouse + ImGuiKeyModFlags BoxSelectMod; // none + + ImGuiMouseButton BoxCancelButton; // left mouse + + ImGuiMouseButton QueryClickButton; // left mouse + ImGuiKeyModFlags QueryClickMod; // ctrl + + ImGuiMouseButton QueryDragButton; // middle mouse + ImGuiKeyModFlags QueryDragMod; // none + + ImGuiMouseButton QueryDragButton2; // right mouse, alternative way to query drag, useful when middle mouse is not available + ImGuiKeyModFlags QueryDragMod2; // ctrl + + ImGuiKeyModFlags HorizontalSizeMod; // alt + ImGuiKeyModFlags VerticalSizeMod; // shift }; //----------------------------------------------------------------------------- From 4bb00c71da0d72637190829dc68e392ed9642f70 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Mon, 15 Jun 2020 17:08:42 -0500 Subject: [PATCH 4/5] typo in header --- implot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot.h b/implot.h index 7bc1eb3..1904234 100644 --- a/implot.h +++ b/implot.h @@ -47,7 +47,7 @@ enum ImPlotFlags_ { ImPlotFlags_Crosshairs = 1 << 6, // the default mouse cursor will be replaced with a crosshair when hovered ImPlotFlags_AntiAliased = 1 << 7, // lines and fills will be anti-aliased (not recommended) ImPlotFlags_NoChild = 1 << 8, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_YAxis2 = 1 << 9, // enable a 2nd y-axis + ImPlotFlags_YAxis2 = 1 << 9, // enable a 2nd y-axis ImPlotFlags_YAxis3 = 1 << 10, // enable a 3rd y-axis ImPlotFlags_Default = ImPlotFlags_MousePos | ImPlotFlags_Legend | ImPlotFlags_Highlight | ImPlotFlags_BoxSelect | ImPlotFlags_ContextMenu }; From c6ad6e9c7f5cfa7ae7948bd8a301e9149931e1d6 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Mon, 15 Jun 2020 19:54:58 -0500 Subject: [PATCH 5/5] clean up and debug input mappings --- implot.cpp | 66 +++++++++++++++++++++--------------------------------- implot.h | 33 ++++++++++----------------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/implot.cpp b/implot.cpp index f1ecfac..c2a6daf 100644 --- a/implot.cpp +++ b/implot.cpp @@ -134,29 +134,20 @@ bool ImPlotLimits::Contains(double x, double y) const { } ImPlotInputMap::ImPlotInputMap() { - PanButton = ImGuiMouseButton_Left; PanMod = ImGuiKeyModFlags_None; - + FitButton = ImGuiMouseButton_Left; + ContextMenuButton = ImGuiMouseButton_Right; BoxSelectButton = ImGuiMouseButton_Right; BoxSelectMod = ImGuiKeyModFlags_None; - - BoxCancelButton = ImGuiMouseButton_Left; - - QueryClickButton = ImGuiMouseButton_Left; - QueryClickMod = ImGuiKeyModFlags_Ctrl; - - QueryDragButton = ImGuiMouseButton_Middle; - QueryDragMod = ImGuiKeyModFlags_None; - - QueryDragButton2 = ImGuiMouseButton_Right; - QueryDragMod2 = ImGuiKeyModFlags_Ctrl; - - HorizontalSizeMod = ImGuiKeyModFlags_Alt; - VerticalSizeMod = ImGuiKeyModFlags_Shift; + BoxSelectCancelButton = ImGuiMouseButton_Left; + QueryButton = ImGuiMouseButton_Middle; + QueryMod = ImGuiKeyModFlags_None; + QueryToggleMod = ImGuiKeyModFlags_Ctrl; + HorizontalMod = ImGuiKeyModFlags_Alt; + VerticalMod = ImGuiKeyModFlags_Shift; } - namespace ImPlot { namespace { @@ -1155,7 +1146,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } // start drag - if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && IO.KeyMods == gp.InputMap.PanMod && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { + if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && HasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { if (hov_x_axis_region) { plot.XAxis.Dragging = true; } @@ -1207,16 +1198,16 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && ImFabs(select_size.x) > 2 && ImFabs(select_size.y) > 2) { ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); ImPlotPoint p2 = PixelsToPlot(IO.MousePos); - if (!gp.X.LockMin && IO.KeyMods != gp.InputMap.HorizontalSizeMod) + if (!gp.X.LockMin && IO.KeyMods != gp.InputMap.HorizontalMod) plot.XAxis.Range.Min = ImMin(p1.x, p2.x); - if (!gp.X.LockMax && IO.KeyMods != gp.InputMap.HorizontalSizeMod) + if (!gp.X.LockMax && IO.KeyMods != gp.InputMap.HorizontalMod) plot.XAxis.Range.Max = ImMax(p1.x, p2.x); for (int i = 0; i < MAX_Y_AXES; i++) { p1 = PixelsToPlot(plot.SelectStart, i); p2 = PixelsToPlot(IO.MousePos, i); - if (!gp.Y[i].LockMin && IO.KeyMods != gp.InputMap.VerticalSizeMod) + if (!gp.Y[i].LockMin && IO.KeyMods != gp.InputMap.VerticalMod) plot.YAxis[i].Range.Min = ImMin(p1.y, p2.y); - if (!gp.Y[i].LockMax && IO.KeyMods != gp.InputMap.VerticalSizeMod) + if (!gp.Y[i].LockMax && IO.KeyMods != gp.InputMap.VerticalMod) plot.YAxis[i].Range.Max = ImMax(p1.y, p2.y); } } @@ -1227,27 +1218,27 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); } // cancel selection - if (plot.Selecting && (IO.MouseClicked[gp.InputMap.BoxCancelButton] || IO.MouseDown[gp.InputMap.BoxCancelButton])) { + if (plot.Selecting && (IO.MouseClicked[gp.InputMap.BoxSelectCancelButton] || IO.MouseDown[gp.InputMap.BoxSelectCancelButton])) { plot.Selecting = false; } // begin selection or query - if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && IO.KeyMods == gp.InputMap.BoxSelectMod) { + if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && HasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } // update query if (plot.Querying) { UpdateTransformCache(); - plot.QueryRect.Min.x = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Max.x = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Min.y = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Max.y = IO.KeyMods == gp.InputMap.HorizontalSizeMod ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Min.x = HasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Max.x = HasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Min.y = HasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Max.y = HasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); plot.QueryRect.Min -= gp.BB_Plot.Min; plot.QueryRect.Max -= gp.BB_Plot.Min; } // end query - if (plot.Querying && (IO.MouseReleased[2] || IO.MouseReleased[1])) { + if (plot.Querying && (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; @@ -1257,27 +1248,22 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } - const bool isSelectInput = IO.MouseDown[gp.InputMap.BoxSelectButton] && IO.KeyMods == gp.InputMap.BoxSelectButton; - const bool isQueryInput = (IO.MouseDown[gp.InputMap.QueryDragButton] && IO.KeyMods == gp.InputMap.QueryDragMod) || (IO.MouseDown[gp.InputMap.QueryDragButton2] && IO.KeyMods == gp.InputMap.QueryDragMod2); - const bool isQueryInputBegin = (IO.MouseClicked[gp.InputMap.QueryDragButton] && IO.KeyMods == gp.InputMap.QueryDragMod) || (IO.MouseClicked[gp.InputMap.QueryDragButton2] && IO.KeyMods == gp.InputMap.QueryDragMod2); - - // begin query - if (HasFlag(plot.Flags, ImPlotFlags_Query) && (gp.Hov_Frame && gp.Hov_Plot && isQueryInputBegin)) { + if (HasFlag(plot.Flags, ImPlotFlags_Query) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.QueryButton] && HasFlag(IO.KeyMods, gp.InputMap.QueryMod)) { plot.QueryRect = ImRect(0,0,0,0); plot.Querying = true; plot.Queried = true; plot.QueryStart = IO.MousePos; } // toggle between select/query - if (HasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && isQueryInput) { + if (HasFlag(plot.Flags, ImPlotFlags_Query) && plot.Selecting && HasFlag(IO.KeyMods,gp.InputMap.QueryToggleMod)) { plot.Selecting = false; plot.QueryRect = ImRect(0,0,0,0); plot.Querying = true; plot.Queried = true; plot.QueryStart = plot.SelectStart; } - if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && isSelectInput) { + if (HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && plot.Querying && !HasFlag(IO.KeyMods, gp.InputMap.QueryToggleMod) && !IO.MouseDown[gp.InputMap.QueryButton]) { plot.Selecting = true; plot.Querying = false; plot.Queried = false; @@ -1286,7 +1272,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // DOUBLE CLICK ----------------------------------------------------------- - if ( IO.MouseDoubleClicked[0] && gp.Hov_Frame && (hov_x_axis_region || any_hov_y_axis_region) && !hov_legend && !hov_query) { + if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && gp.Hov_Frame && (hov_x_axis_region || any_hov_y_axis_region) && !hov_legend && !hov_query) { gp.FitThisFrame = true; gp.FitX = hov_x_axis_region; for (int i = 0; i < MAX_Y_AXES; i++) { @@ -1304,7 +1290,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // FOCUS ------------------------------------------------------------------ // focus window - if ((IO.MouseClicked[0] || IO.MouseClicked[1]) && gp.Hov_Frame) + if ((IO.MouseClicked[0] || IO.MouseClicked[1] || IO.MouseClicked[2]) && gp.Hov_Frame) ImGui::FocusWindow(ImGui::GetCurrentWindow()); UpdateTransformCache(); @@ -1797,7 +1783,7 @@ void EndPlot() { // CONTEXT MENU ----------------------------------------------------------- - if (HasFlag(plot.Flags, ImPlotFlags_ContextMenu) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[1] && !hov_legend) + if (HasFlag(plot.Flags, ImPlotFlags_ContextMenu) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend) ImGui::OpenPopup("##Context"); if (ImGui::BeginPopup("##Context")) { PlotContextMenu(plot); diff --git a/implot.h b/implot.h index d54202a..08af014 100644 --- a/implot.h +++ b/implot.h @@ -174,28 +174,19 @@ struct ImPlotStyle { // 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 double 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 ImPlotInputMap(); - - ImGuiMouseButton PanButton; // left mouse - ImGuiKeyModFlags PanMod; // none - - ImGuiMouseButton BoxSelectButton; // right mouse - ImGuiKeyModFlags BoxSelectMod; // none - - ImGuiMouseButton BoxCancelButton; // left mouse - - ImGuiMouseButton QueryClickButton; // left mouse - ImGuiKeyModFlags QueryClickMod; // ctrl - - ImGuiMouseButton QueryDragButton; // middle mouse - ImGuiKeyModFlags QueryDragMod; // none - - ImGuiMouseButton QueryDragButton2; // right mouse, alternative way to query drag, useful when middle mouse is not available - ImGuiKeyModFlags QueryDragMod2; // ctrl - - ImGuiKeyModFlags HorizontalSizeMod; // alt - ImGuiKeyModFlags VerticalSizeMod; // shift }; //-----------------------------------------------------------------------------