diff --git a/TODO.md b/TODO.md index 1b3b44a..895443c 100644 --- a/TODO.md +++ b/TODO.md @@ -2,36 +2,32 @@ The list below represents a combination of high-priority work, nice-to-have feat ## API -- add shortcut/legacy overloads for BeginPlot - ## Axes - add flag to remove weekends on Time axis -- pixel space scale (`ImPlotTransform_Display`), normalized space scale (`ImPlotTransform_Axes`), data space scale (`ImPloTransform_Data`) +- pixel space scale (`ImPlotTransform_Display`), normalized space scale (`ImPlotTransform_Axes`), data space scale (`ImPlotTransform_Data`) - make ImPlotFlags_Equal not a flag -> `SetupEqual(ImPlotAxis x, ImPlotAxis y)` - allow inverted arguments `SetAxes` to transpose data? - `SetupAxisColors()` -- `SetupAxisConstraints()` - `SetupAxisHome()` ## Plot Items -- add `ImPlotLineFlags`, `ImPlotBarsFlags`, etc. for each plot type -- add `PlotBarGroups` wrapper that makes rendering groups of bars easier, with stacked bar support - add `PlotBubbles` (see MATLAB bubble chart) - add non-zero references for `PlotBars` etc. - add exploding to `PlotPieChart` (on hover-highlight?) +- fix appearance of `PlotBars` spacing ## Styling - support gradient and/or colormap sampled fills (e.g. ImPlotFillStyle_) -- add hover/active color for plot axes - API for setting different fonts for plot elements ## Colormaps - gradient editing tool - `RemoveColormap` +- `enum ImPlotColorRule_ { Solid, Faded, XValue, YValue, ZValue }` ## Legend @@ -48,6 +44,7 @@ The list below represents a combination of high-priority work, nice-to-have feat - add box selection to axes - first frame render delay might fix "fit pop" effect - move some code to new `implot_tools.cpp` +- ColormapSlider (see metrics) ## Optimizations @@ -55,6 +52,32 @@ The list below represents a combination of high-priority work, nice-to-have feat - reduce number of calls to `PushClipRect` - explore SIMD operations for high density plot items +## Plotter Pipeline + +Ideally every `PlotX` function should use our faster rendering pipeline when it is applicable. + +` User Data > Getter > Fitter > Renderer > RenderPrimitives` + +|Plotter|Getter|Fitter|Renderer|RenderPrimitives| +|---|:-:|:-:|:-:|:-:| +|PlotLine|Yes|Yes|Yes|Yes| +|PlotScatter|Yes|Yes|Yes|Yes| +|PlotStairs|Yes|Yes|Yes|Yes| +|PlotShaded|Yes|Yes|Yes|Yes| +|PlotBars|Yes|Yes|Yes|Yes| +|PlotBarGroups|:|:|:|:| +|PlotHistogram|:|:|:|:| +|PlotErrorBars|Yes|Yes|No|No| +|PlotStems|Yes|Yes|Yes|Yes| +|PlotInfLines|Yes|Yes|Yes|Yes| +|PlotPieChart|No|No|No|No| +|PlotHeatmap|Yes|No|Yes|Mixed| +|PlotHistogram2D|:|:|:|:| +|PlotDigital|Yes|No|No|No| +|PlotImage|-|-|-|-| +|PlotText|-|-|-|-| +|PlotDummy|-|-|-|-| + ## Completed - make BeginPlot take fewer args: - make query a tool -> `DragRect` @@ -67,3 +90,10 @@ The list below represents a combination of high-priority work, nice-to-have feat - legend items can be hovered even if plot is not - fix frame delay on DragX tools - remove tag from drag line/point -> add `Tag` tool +- add shortcut/legacy overloads for BeginPlot +- `SetupAxisConstraints()` +- `SetupAxisScale()` +- add `ImPlotLineFlags`, `ImPlotBarsFlags`, etc. for each plot type +- add `PlotBarGroups` wrapper that makes rendering groups of bars easier, with stacked bar support +- `PlotBars` restore outlines +- add hover/active color for plot axes diff --git a/implot.cpp b/implot.cpp index ec6dcdb..04591d0 100644 --- a/implot.cpp +++ b/implot.cpp @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2021 Evan Pezent +// Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.13 WIP +// ImPlot v0.14 /* @@ -31,7 +31,20 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. -- 2021/10/19 (0.13) MAJOR API OVERHAUL! +- 2022/06/17 (0.14) - **IMPORTANT** All PlotX functions now take an ImPlotX_Flags `flags` parameter. Where applicable, it is located before the existing `offset` and `stride` parameters. + If you were providing offset and stride values, you will need to update your function call to include a `flags` value. If you fail to do this, you will likely see + unexpected results or crashes without a compiler warning since these three are all default args. We apologize for the inconvenience, but this was a necessary evil. + - PlotBarsH has been removed; use PlotBars + ImPlotBarsFlags_Horizontal instead + - PlotErrorBarsH has been removed; use PlotErrorBars + ImPlotErrorBarsFlags_Horizontal + - PlotHistogram/PlotHistogram2D signatures changed; `cumulative`, `density`, and `outliers` options now specified via ImPlotHistogramFlags + - PlotPieChart signature changed; `normalize` option now specified via ImPlotPieChartFlags + - PlotText signature changes; `vertical` option now specified via `ImPlotTextFlags_Vertical` + - `PlotVLines` and `PlotHLines` replaced with `PlotInfLines` (+ ImPlotInfLinesFlags_Horizontal ) + - arguments of ImPlotGetter have been reversed to be consistent with other API callbacks + - SetupAxisScale + ImPlotScale have replaced ImPlotAxisFlags_LogScale and ImPlotAxisFlags_Time flags + - ImPlotFormatters should now return an int indicating the size written + - the signature of ImPlotGetter has been reversed so that void* user_data is the last argument and consistent with other callbacks +- 2021/10/19 (0.13) - MAJOR API OVERHAUL! See #168 and #272 - TRIVIAL RENAME: - ImPlotLimits -> ImPlotRect - ImPlotYAxis_ -> ImAxis_ @@ -178,7 +191,6 @@ ImPlotStyle::ImPlotStyle() { Colormap = ImPlotColormap_Deep; - AntiAliasedLines = false; UseLocalTime = false; Use24HourClock = false; UseISO8601 = false; @@ -663,22 +675,24 @@ bool ShowLegendEntries(ImPlotItemGroup& items, const ImRect& legend_bb, bool hov } //----------------------------------------------------------------------------- -// Tick Utils +// Locators //----------------------------------------------------------------------------- static const float TICK_FILL_X = 0.8f; static const float TICK_FILL_Y = 1.0f; -void AddTicksDefault(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { - const int idx0 = ticks.Size; +void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { + if (range.Min == range.Max) + return; const int nMinor = 10; - const int nMajor = ImMax(2, (int)IM_ROUND(pix / (vertical ? 300.0f : 400.0f))); + const int nMajor = ImMax(2, (int)IM_ROUND(pixels / (vertical ? 300.0f : 400.0f))); const double nice_range = NiceNum(range.Size() * 0.99, false); const double interval = NiceNum(nice_range / (nMajor - 1), true); const double graphmin = floor(range.Min / interval) * interval; const double graphmax = ceil(range.Max / interval) * interval; bool first_major_set = false; int first_major_idx = 0; + const int idx0 = ticker.TickCount(); // ticker may have user custom ticks ImVec2 total_size(0,0); for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { // is this zero? combat zero formatting issues @@ -686,72 +700,113 @@ void AddTicksDefault(const ImPlotRange& range, float pix, bool vertical, ImPlotT major = 0; if (range.Contains(major)) { if (!first_major_set) { - first_major_idx = ticks.Size; + first_major_idx = ticker.TickCount(); first_major_set = true; } - total_size += ticks.Append(major, true, true, formatter, data).LabelSize; + total_size += ticker.AddTick(major, true, 0, true, formatter, formatter_data).LabelSize; } for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (range.Contains(minor)) { - total_size += ticks.Append(minor, false, true, formatter, data).LabelSize; + total_size += ticker.AddTick(minor, false, 0, true, formatter, formatter_data).LabelSize; } } } // prune if necessary - if ((!vertical && total_size.x > pix*TICK_FILL_X) || (vertical && total_size.y > pix*TICK_FILL_Y)) { + if ((!vertical && total_size.x > pixels*TICK_FILL_X) || (vertical && total_size.y > pixels*TICK_FILL_Y)) { for (int i = first_major_idx-1; i >= idx0; i -= 2) - ticks.Ticks[i].ShowLabel = false; - for (int i = first_major_idx+1; i < ticks.Size; i += 2) - ticks.Ticks[i].ShowLabel = false; + ticker.Ticks[i].ShowLabel = false; + for (int i = first_major_idx+1; i < ticker.TickCount(); i += 2) + ticker.Ticks[i].ShowLabel = false; } } -void AddTicksLogarithmic(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { - if (range.Min <= 0 || range.Max <= 0) - return; - const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); - double log_min = ImLog10(range.Min); - double log_max = ImLog10(range.Max); - int exp_step = ImMax(1,(int)(log_max - log_min) / nMajor); - int exp_min = (int)log_min; - int exp_max = (int)log_max; - if (exp_step != 1) { - while(exp_step % 3 != 0) exp_step++; // make step size multiple of three - while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 +bool CalcLogarithmicExponents(const ImPlotRange& range, float pix, bool vertical, int& exp_min, int& exp_max, int& exp_step) { + if (range.Min * range.Max > 0) { + const int nMajor = vertical ? ImMax(2, (int)IM_ROUND(pix * 0.02f)) : ImMax(2, (int)IM_ROUND(pix * 0.01f)); // TODO: magic numbers + double log_min = ImLog10(ImAbs(range.Min)); + double log_max = ImLog10(ImAbs(range.Max)); + double log_a = ImMin(log_min,log_max); + double log_b = ImMax(log_min,log_max); + exp_step = ImMax(1,(int)(log_b - log_a) / nMajor); + exp_min = (int)log_a; + exp_max = (int)log_b; + if (exp_step != 1) { + while(exp_step % 3 != 0) exp_step++; // make step size multiple of three + while(exp_min % exp_step != 0) exp_min--; // decrease exp_min until exp_min + N * exp_step will be 0 + } + return true; } + return false; +} + +void AddTicksLogarithmic(const ImPlotRange& range, int exp_min, int exp_max, int exp_step, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { + const double sign = ImSign(range.Max); for (int e = exp_min - exp_step; e < (exp_max + exp_step); e += exp_step) { - double major1 = ImPow(10, (double)(e)); - double major2 = ImPow(10, (double)(e + 1)); + double major1 = sign*ImPow(10, (double)(e)); + double major2 = sign*ImPow(10, (double)(e + 1)); double interval = (major2 - major1) / 9; if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) - ticks.Append(major1, true, true, formatter, data); + ticker.AddTick(major1, true, 0, true, formatter, data); for (int j = 0; j < exp_step; ++j) { - major1 = ImPow(10, (double)(e+j)); - major2 = ImPow(10, (double)(e+j+1)); + major1 = sign*ImPow(10, (double)(e+j)); + major2 = sign*ImPow(10, (double)(e+j+1)); interval = (major2 - major1) / 9; for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) { double minor = major1 + i * interval; if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) - ticks.Append(minor, false, false, formatter, data); - + ticker.AddTick(minor, false, 0, false, formatter, data); } } } } -void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data) { +void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { + int exp_min, exp_max, exp_step; + if (CalcLogarithmicExponents(range, pixels, vertical, exp_min, exp_max, exp_step)) + AddTicksLogarithmic(range, exp_min, exp_max, exp_step, ticker, formatter, formatter_data); +} + +float CalcSymLogPixel(double plt, const ImPlotRange& range, float pixels) { + double scaleToPixels = pixels / range.Size(); + double scaleMin = TransformForward_SymLog(range.Min,NULL); + double scaleMax = TransformForward_SymLog(range.Max,NULL); + double s = TransformForward_SymLog(plt, NULL); + double t = (s - scaleMin) / (scaleMax - scaleMin); + plt = range.Min + range.Size() * t; + + return (float)(0 + scaleToPixels * (plt - range.Min)); +} + +void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { + if (range.Min >= -1 && range.Max <= 1) { + Locator_Default(ticker, range, pixels, vertical, formatter, formatter_data); + } + else if (range.Min * range.Max < 0) { // cross zero + const float pix_min = 0; + const float pix_max = pixels; + const float pix_p1 = CalcSymLogPixel(1, range, pixels); + const float pix_n1 = CalcSymLogPixel(-1, range, pixels); + int exp_min_p, exp_max_p, exp_step_p; + int exp_min_n, exp_max_n, exp_step_n; + CalcLogarithmicExponents(ImPlotRange(1,range.Max), ImAbs(pix_max-pix_p1),vertical,exp_min_p,exp_max_p,exp_step_p); + CalcLogarithmicExponents(ImPlotRange(range.Min,-1),ImAbs(pix_n1-pix_min),vertical,exp_min_n,exp_max_n,exp_step_n); + int exp_step = ImMax(exp_step_n, exp_step_p); + ticker.AddTick(0,true,0,true,formatter,formatter_data); + AddTicksLogarithmic(ImPlotRange(1,range.Max), exp_min_p,exp_max_p,exp_step,ticker,formatter,formatter_data); + AddTicksLogarithmic(ImPlotRange(range.Min,-1),exp_min_n,exp_max_n,exp_step,ticker,formatter,formatter_data); + } + else { + Locator_Log10(ticker, range, pixels, vertical, formatter, formatter_data); + } +} + +void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTicker& ticker, ImPlotFormatter formatter, void* data) { for (int i = 0; i < n; ++i) { - if (labels != NULL) { - ImPlotTick tick(values[i], false, true); - tick.TextOffset = ticks.TextBuffer.size(); - ticks.TextBuffer.append(labels[i], labels[i] + strlen(labels[i]) + 1); - tick.LabelSize = ImGui::CalcTextSize(labels[i]); - ticks.Append(tick); - } - else { - ticks.Append(values[i], false, true, formatter, data); - } + if (labels != NULL) + ticker.AddTick(values[i], false, 0, true, labels[i]); + else + ticker.AddTick(values[i], false, 0, true, formatter, data); } } @@ -1052,7 +1107,7 @@ int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, b } } -int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeFmt fmt) { +int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt) { int written = 0; if (fmt.Date != ImPlotDateFmt_None) written += FormatDate(t, buffer, size, fmt.Date, fmt.UseISO8601); @@ -1064,23 +1119,13 @@ int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeFm return written; } -inline float GetDateTimeWidth(ImPlotDateTimeFmt fmt) { +inline float GetDateTimeWidth(ImPlotDateTimeSpec fmt) { static const ImPlotTime t_max_width = MakeTime(2888, 12, 22, 12, 58, 58, 888888); // best guess at time that maximizes pixel width char buffer[32]; FormatDateTime(t_max_width, buffer, 32, fmt); return ImGui::CalcTextSize(buffer).x; } -void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotDateTimeFmt fmt) { - char temp[32]; - if (tick.ShowLabel) { - tick.TextOffset = buffer.size(); - FormatDateTime(t, temp, 32, fmt); - buffer.append(temp, temp + strlen(temp) + 1); - tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset); - } -} - inline bool TimeLabelSame(const char* l1, const char* l2) { size_t len1 = strlen(l1); size_t len2 = strlen(l2); @@ -1088,66 +1133,68 @@ inline bool TimeLabelSame(const char* l1, const char* l2) { return strcmp(l1 + len1 - n, l2 + len2 - n) == 0; } -static const ImPlotDateTimeFmt TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_Us), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_S), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) +static const ImPlotDateTimeSpec TimeFormatLevel0[ImPlotTimeUnit_COUNT] = { + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_S), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Hr), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Mo, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; -static const ImPlotDateTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) +static const ImPlotDateTimeSpec TimeFormatLevel1[ImPlotTimeUnit_COUNT] = { + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; -static const ImPlotDateTimeFmt TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) +static const ImPlotDateTimeSpec TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = { + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMinS), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_Yr, ImPlotTimeFmt_None) }; -static const ImPlotDateTimeFmt TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_Us), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), - ImPlotDateTimeFmt(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), - ImPlotDateTimeFmt(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), - ImPlotDateTimeFmt(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) +static const ImPlotDateTimeSpec TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = { + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_Us), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SUs), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_SMs), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMinS), + ImPlotDateTimeSpec(ImPlotDateFmt_None, ImPlotTimeFmt_HrMin), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMo, ImPlotTimeFmt_Hr), + ImPlotDateTimeSpec(ImPlotDateFmt_DayMoYr, ImPlotTimeFmt_None), + ImPlotDateTimeSpec(ImPlotDateFmt_MoYr, ImPlotTimeFmt_None) }; -inline ImPlotDateTimeFmt GetDateTimeFmt(const ImPlotDateTimeFmt* ctx, ImPlotTimeUnit idx) { +inline ImPlotDateTimeSpec GetDateTimeFmt(const ImPlotDateTimeSpec* ctx, ImPlotTimeUnit idx) { ImPlotStyle& style = GetStyle(); - ImPlotDateTimeFmt fmt = ctx[idx]; + ImPlotDateTimeSpec fmt = ctx[idx]; fmt.UseISO8601 = style.UseISO8601; fmt.Use24HourClock = style.Use24HourClock; return fmt; } -void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks) { +void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data) { + IM_ASSERT_USER_ERROR(vertical == false, "Cannot locate Time ticks on vertical axis!"); + (void)vertical; // get units for level 0 and level 1 labels - const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (plot_width / 100)); // level = 0 (top) + const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (pixels / 100)); // level = 0 (top) const ImPlotTimeUnit unit1 = unit0 + 1; // level = 1 (bottom) // get time format specs - const ImPlotDateTimeFmt fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); - const ImPlotDateTimeFmt fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); - const ImPlotDateTimeFmt fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); + const ImPlotDateTimeSpec fmt0 = GetDateTimeFmt(TimeFormatLevel0, unit0); + const ImPlotDateTimeSpec fmt1 = GetDateTimeFmt(TimeFormatLevel1, unit1); + const ImPlotDateTimeSpec fmtf = GetDateTimeFmt(TimeFormatLevel1First, unit1); // min max times const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min); const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max); @@ -1155,9 +1202,13 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti const float max_density = 0.5f; // book keeping int last_major_offset = -1; + // formatter data + Formatter_Time_Data ftd; + ftd.UserFormatter = formatter; + ftd.UserFormatterData = formatter_data; if (unit0 != ImPlotTimeUnit_Yr) { // pixels per major (level 1) division - const float pix_per_major_div = plot_width / (float)(range.Size() / TimeUnitSpans[unit1]); + const float pix_per_major_div = pixels / (float)(range.Size() / TimeUnitSpans[unit1]); // nominal pixels taken up by labels const float fmt0_width = GetDateTimeWidth(fmt0); const float fmt1_width = GetDateTimeWidth(fmt1); @@ -1174,36 +1225,28 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti // add major tick if (t1 >= t_min && t1 <= t_max) { // minor level 0 tick - ImPlotTick tick_min(t1.ToDouble(),true,true); - tick_min.Level = 0; - LabelTickTime(tick_min,ticks.TextBuffer,t1,fmt0); - ticks.Append(tick_min); + ftd.Time = t1; ftd.Spec = fmt0; + ticker.AddTick(t1.ToDouble(), true, 0, true, Formatter_Time, &ftd); // major level 1 tick - ImPlotTick tick_maj(t1.ToDouble(),true,true); - tick_maj.Level = 1; - LabelTickTime(tick_maj,ticks.TextBuffer,t1, last_major_offset < 0 ? fmtf : fmt1); - const char* this_major = ticks.TextBuffer.Buf.Data + tick_maj.TextOffset; - if (last_major_offset >= 0 && TimeLabelSame(ticks.TextBuffer.Buf.Data + last_major_offset, this_major)) + ftd.Time = t1; ftd.Spec = last_major_offset < 0 == NULL ? fmtf : fmt1; + ImPlotTick& tick_maj = ticker.AddTick(t1.ToDouble(), true, 1, true, Formatter_Time, &ftd); + const char* this_major = ticker.GetText(tick_maj); + if (last_major_offset >= 0 && TimeLabelSame(ticker.TextBuffer.Buf.Data + last_major_offset, this_major)) tick_maj.ShowLabel = false; last_major_offset = tick_maj.TextOffset; - ticks.Append(tick_maj); } // add minor ticks up until next major if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { ImPlotTime t12 = AddTime(t1, unit0, step); while (t12 < t2) { - float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * plot_width; + float px_to_t2 = (float)((t2 - t12).ToDouble()/range.Size()) * pixels; if (t12 >= t_min && t12 <= t_max) { - ImPlotTick tick(t12.ToDouble(),false,px_to_t2 >= fmt0_width); - tick.Level = 0; - LabelTickTime(tick,ticks.TextBuffer,t12,fmt0); - ticks.Append(tick); + ftd.Time = t12; ftd.Spec = fmt0; + ticker.AddTick(t12.ToDouble(), false, 0, px_to_t2 >= fmt0_width, Formatter_Time, &ftd); if (last_major_offset < 0 && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { - ImPlotTick tick_maj(t12.ToDouble(),true,true); - tick_maj.Level = 1; - LabelTickTime(tick_maj,ticks.TextBuffer,t12,fmtf); + ftd.Time = t12; ftd.Spec = fmtf; + ImPlotTick& tick_maj = ticker.AddTick(t12.ToDouble(), true, 1, true, Formatter_Time, &ftd); last_major_offset = tick_maj.TextOffset; - ticks.Append(tick_maj); } } t12 = AddTime(t12, unit0, step); @@ -1213,9 +1256,9 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti } } else { - const ImPlotDateTimeFmt fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); + const ImPlotDateTimeSpec fmty = GetDateTimeFmt(TimeFormatLevel0, ImPlotTimeUnit_Yr); const float label_width = GetDateTimeWidth(fmty); - const int max_labels = (int)(max_density * plot_width / label_width); + const int max_labels = (int)(max_density * pixels / label_width); const int year_min = GetYear(t_min); const int year_max = GetYear(CeilTime(t_max, ImPlotTimeUnit_Yr)); const double nice_range = NiceNum((year_max - year_min)*0.99,false); @@ -1227,10 +1270,8 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti for (int y = graphmin; y < graphmax; y += step) { ImPlotTime t = MakeTime(y); if (t >= t_min && t <= t_max) { - ImPlotTick tick(t.ToDouble(), true, true); - tick.Level = 0; - LabelTickTime(tick, ticks.TextBuffer, t, fmty); - ticks.Append(tick); + ftd.Time = t; ftd.Spec = fmty; + ticker.AddTick(t.ToDouble(), true, 0, true, Formatter_Time, &ftd); } } } @@ -1269,7 +1310,7 @@ inline void EndDisabledControls(bool cond) { } } -void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed) { +void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool /*time_allowed*/) { ImGui::PushItemWidth(75); bool always_locked = axis.IsRangeLocked() || axis.IsAutoFitting(); @@ -1279,7 +1320,7 @@ void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_all bool labels = axis.HasTickLabels(); double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. - if (axis.IsTime()) { + if (axis.Scale == ImPlotScale_Time) { ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); @@ -1358,14 +1399,15 @@ void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_all ImGui::Separator(); ImGui::CheckboxFlags("Auto-Fit",(unsigned int*)&axis.Flags, ImPlotAxisFlags_AutoFit); - BeginDisabledControls(axis.IsTime() && time_allowed); - ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); - EndDisabledControls(axis.IsTime() && time_allowed); - if (time_allowed) { - BeginDisabledControls(axis.IsLog()); - ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); - EndDisabledControls(axis.IsLog()); - } + // TODO + // BeginDisabledControls(axis.IsTime() && time_allowed); + // ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); + // EndDisabledControls(axis.IsTime() && time_allowed); + // if (time_allowed) { + // BeginDisabledControls(axis.IsLog() || axis.IsSymLog()); + // ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); + // EndDisabledControls(axis.IsLog() || axis.IsSymLog()); + // } ImGui::Separator(); ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); ImGui::CheckboxFlags("Opposite",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Opposite); @@ -1483,8 +1525,6 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { } } if ((ImGui::BeginMenu("Settings"))) { - if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) - ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased); if (ImGui::MenuItem("Equal", NULL, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) ImFlipFlag(plot.Flags, ImPlotFlags_Equal); if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) @@ -1512,13 +1552,8 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { // Axis Utils //----------------------------------------------------------------------------- -static inline void DefaultFormatter(double value, char* buff, int size, void* data) { - char* fmt = (char*)data; - ImFormatString(buff, size, fmt, value); -} - static inline int AxisPrecision(const ImPlotAxis& axis) { - const double range = axis.Ticks.Size > 1 ? (axis.Ticks.Ticks[1].PlotPos - axis.Ticks.Ticks[0].PlotPos) : axis.Range.Size(); + const double range = axis.Ticker.TickCount() > 1 ? (axis.Ticker.Ticks[1].PlotPos - axis.Ticker.Ticks[0].PlotPos) : axis.Range.Size(); return Precision(range); } @@ -1527,20 +1562,9 @@ static inline double RoundAxisValue(const ImPlotAxis& axis, double value) { } void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round) { - ImPlotContext& gp = *GImPlot; - if (axis.IsTime()) { - ImPlotTimeUnit unit = axis.Vertical - ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)) // TODO: magic value! - : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)); // TODO: magic value! - FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); - } - else { - if (round) - value = RoundAxisValue(axis, value); - ImPlotFormatter formatter = axis.Formatter ? axis.Formatter : DefaultFormatter; - void* data = (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? (void*)axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT; - formatter(value, buff, size, data); - } + if (round) + value = RoundAxisValue(axis, value); + axis.Formatter(value, buff, size, axis.FormatterData); } void UpdateAxisColors(ImPlotAxis& axis) { @@ -1575,14 +1599,14 @@ void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignm const bool label = axis.HasLabel(); const bool ticks = axis.HasTickLabels(); const bool opp = axis.IsOpposite(); - const bool time = axis.IsTime(); + const bool time = axis.Scale == ImPlotScale_Time; if (opp) { if (count_T++ > 0) pad_T += K + P; if (label) pad_T += T + P; if (ticks) - pad_T += ImMax(T, axis.Ticks.MaxSize.y) + P + (time ? T + P : 0); + pad_T += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Min.y + pad_T; axis.Datum2 = last_T; last_T = axis.Datum1; @@ -1593,7 +1617,7 @@ void PadAndDatumAxesX(ImPlotPlot& plot, float& pad_T, float& pad_B, ImPlotAlignm if (label) pad_B += T + P; if (ticks) - pad_B += ImMax(T, axis.Ticks.MaxSize.y) + P + (time ? T + P : 0); + pad_B += ImMax(T, axis.Ticker.MaxSize.y) + P + (time ? T + P : 0); axis.Datum1 = plot.CanvasRect.Max.y - pad_B; axis.Datum2 = last_B; last_B = axis.Datum1; @@ -1661,7 +1685,7 @@ void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignm if (label) pad_R += T + P; if (ticks) - pad_R += axis.Ticks.MaxSize.x + P; + pad_R += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Max.x - pad_R; axis.Datum2 = last_R; last_R = axis.Datum1; @@ -1672,7 +1696,7 @@ void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignm if (label) pad_L += T + P; if (ticks) - pad_L += axis.Ticks.MaxSize.x + P; + pad_L += axis.Ticker.MaxSize.x + P; axis.Datum1 = plot.CanvasRect.Min.x + pad_L; axis.Datum2 = last_L; last_L = axis.Datum1; @@ -1706,13 +1730,13 @@ void PadAndDatumAxesY(ImPlotPlot& plot, float& pad_L, float& pad_R, ImPlotAlignm // RENDERING //----------------------------------------------------------------------------- -static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTickCollection& ticks, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { - const float density = ticks.Size / rect.GetWidth(); +static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { + const float density = ticker.TickCount() / rect.GetWidth(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); - for (int t = 0; t < ticks.Size; t++) { - const ImPlotTick& xt = ticks.Ticks[t]; + for (int t = 0; t < ticker.TickCount(); t++) { + const ImPlotTick& xt = ticker.Ticks[t]; if (xt.PixelPos < rect.Min.x || xt.PixelPos > rect.Max.x) continue; if (xt.Level == 0) { @@ -1724,13 +1748,13 @@ static inline void RenderGridLinesX(ImDrawList& DrawList, const ImPlotTickCollec } } -static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTickCollection& ticks, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { - const float density = ticks.Size / rect.GetHeight(); +static inline void RenderGridLinesY(ImDrawList& DrawList, const ImPlotTicker& ticker, const ImRect& rect, ImU32 col_maj, ImU32 col_min, float size_maj, float size_min) { + const float density = ticker.TickCount() / rect.GetHeight(); ImVec4 col_min4 = ImGui::ColorConvertU32ToFloat4(col_min); col_min4.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); col_min = ImGui::ColorConvertFloat4ToU32(col_min4); - for (int t = 0; t < ticks.Size; t++) { - const ImPlotTick& yt = ticks.Ticks[t]; + for (int t = 0; t < ticker.TickCount(); t++) { + const ImPlotTick& yt = ticker.Ticks[t]; if (yt.PixelPos < rect.Min.y || yt.PixelPos > rect.Max.y) continue; if (yt.Major) @@ -1866,26 +1890,32 @@ bool UpdateInput(ImPlotPlot& plot) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_held[i] && !x_axis.IsInputLocked()) { drag_direction |= (1 << 1); - const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); - const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); - x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); - x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); - if (axis_equal && x_axis.OrthoAxis != NULL) - x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); - changed = true; + bool increasing = x_axis.IsInverted() ? IO.MouseDelta.x > 0 : IO.MouseDelta.x < 0; + if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { + const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); + const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); + x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); + x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); + if (axis_equal && x_axis.OrthoAxis != NULL) + x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); + changed = true; + } } } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_held[i] && !y_axis.IsInputLocked()) { drag_direction |= (1 << 2); - const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); - const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); - y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); - y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); - if (axis_equal && y_axis.OrthoAxis != NULL) - y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); - changed = true; + bool increasing = y_axis.IsInverted() ? IO.MouseDelta.y < 0 : IO.MouseDelta.y > 0; + if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { + const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); + const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); + y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); + y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); + if (axis_equal && y_axis.OrthoAxis != NULL) + y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); + changed = true; + } } } if (IO.MouseDragMaxDistanceSqr[gp.InputMap.Pan] > MOUSE_CURSOR_DRAG_THRESHOLD) { @@ -2038,10 +2068,6 @@ void ApplyNextPlotData(ImAxis idx) { void SetupAxis(ImAxis idx, const char* label, ImPlotAxisFlags flags) { IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); - IM_ASSERT_USER_ERROR(!(ImHasFlag(flags, ImPlotAxisFlags_Time) && ImHasFlag(flags, ImPlotAxisFlags_LogScale)), - "ImPlotAxisFlags_Time and ImPlotAxisFlags_LogScale cannot be enabled at the same time!"); - IM_ASSERT_USER_ERROR(!(ImHasFlag(flags, ImPlotAxisFlags_Time) && idx >= ImAxis_Y1), - "Y axes cannot display time formatted labels!"); // get plot and axis ImPlotPlot& plot = *GImPlot->CurrentPlot; ImPlotAxis& axis = plot.Axes[idx]; @@ -2113,8 +2139,8 @@ void SetupAxisTicks(ImAxis idx, const double* values, int n_ticks, const char* c AddTicksCustom(values, labels, n_ticks, - axis.Ticks, - axis.Formatter ? axis.Formatter : DefaultFormatter, + axis.Ticker, + axis.Formatter ? axis.Formatter : Formatter_Default, (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); } @@ -2126,6 +2152,79 @@ void SetupAxisTicks(ImAxis idx, double v_min, double v_max, int n_ticks, const c SetupAxisTicks(idx, GImPlot->TempDouble1.Data, n_ticks, labels, show_default); } +void SetupAxisScale(ImAxis idx, ImPlotScale scale) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.Scale = scale; + switch (scale) + { + case ImPlotScale_Time: + axis.TransformForward = NULL; + axis.TransformInverse = NULL; + axis.TransformData = NULL; + axis.Locator = Locator_Time; + axis.ConstraintRange = ImPlotRange(IMPLOT_MIN_TIME, IMPLOT_MAX_TIME); + axis.Ticker.Levels = 2; + break; + case ImPlotScale_Log10: + axis.TransformForward = TransformForward_Log10; + axis.TransformInverse = TransformInverse_Log10; + axis.TransformData = NULL; + axis.Locator = Locator_Log10; + axis.ConstraintRange = ImPlotRange(DBL_MIN, INFINITY); + break; + case ImPlotScale_SymLog: + axis.TransformForward = TransformForward_SymLog; + axis.TransformInverse = TransformInverse_SymLog; + axis.TransformData = NULL; + axis.Locator = Locator_SymLog; + axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); + break; + default: + axis.TransformForward = NULL; + axis.TransformInverse = NULL; + axis.TransformData = NULL; + axis.Locator = NULL; + axis.ConstraintRange = ImPlotRange(-INFINITY, INFINITY); + break; + } +} + +void SetupAxisScale(ImAxis idx, ImPlotTransform fwd, ImPlotTransform inv, void* data) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.Scale = IMPLOT_AUTO; + axis.TransformForward = fwd; + axis.TransformInverse = inv; + axis.TransformData = data; +} + +void SetupAxisLimitsConstraints(ImAxis idx, double v_min, double v_max) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.ConstraintRange.Min = v_min; + axis.ConstraintRange.Max = v_max; +} + +void SetupAxisZoomConstraints(ImAxis idx, double z_min, double z_max) { + IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL && !GImPlot->CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlotPlot& plot = *GImPlot->CurrentPlot; + ImPlotAxis& axis = plot.Axes[idx]; + IM_ASSERT_USER_ERROR(axis.Enabled, "Axis is not enabled! Did you forget to call SetupAxis()?"); + axis.ConstraintZoom.Min = z_min; + axis.ConstraintZoom.Max = z_max; +} + void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags) { SetupAxis(ImAxis_X1, x_label, x_flags); SetupAxis(ImAxis_Y1, y_label, y_flags); @@ -2306,6 +2405,10 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlotFlags flags) { return true; } +//----------------------------------------------------------------------------- +// SetupFinish +//----------------------------------------------------------------------------- + void SetupFinish() { IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); IM_ASSERT_USER_ERROR(GImPlot->CurrentPlot != NULL, "SetupFinish needs to be called after BeginPlot!"); @@ -2320,12 +2423,23 @@ void SetupFinish() { // lock setup plot.SetupLocked = true; - // finalize axes + // finalize axes and set default formatter/locator for (int i = 0; i < ImAxis_COUNT; ++i) { - if (plot.Axes[i].Enabled) { - plot.Axes[i].Constrain(); - if (!plot.Initialized && plot.Axes[i].CanInitFit()) - plot.FitThisFrame = plot.Axes[i].FitThisFrame = true; + ImPlotAxis& axis = plot.Axes[i]; + if (axis.Enabled) { + axis.Constrain(); + if (!plot.Initialized && axis.CanInitFit()) + plot.FitThisFrame = axis.FitThisFrame = true; + } + if (axis.Formatter == NULL) { + axis.Formatter = Formatter_Default; + if (axis.HasFormatSpec) + axis.FormatterData = axis.FormatSpec; + else + axis.FormatterData = (void*)IMPLOT_LABEL_FORMAT; + } + if (axis.Locator == NULL) { + axis.Locator = Locator_Default; } } @@ -2397,28 +2511,13 @@ void SetupFinish() { // (1) calc addition top padding and bot padding PadAndDatumAxesX(plot,pad_top,pad_bot,gp.CurrentAlignmentH); - - const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; // (2) get y tick labels (needed for left/right pad) for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& axis = plot.YAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks) { - if (axis.IsLog()) - AddTicksLogarithmic(axis.Range, - plot_height, - true, - axis.Ticks, - axis.Formatter ? axis.Formatter : DefaultFormatter, - (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); - else - AddTicksDefault(axis.Range, - plot_height, - true, - axis.Ticks, - axis.Formatter ? axis.Formatter : DefaultFormatter, - (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); + axis.Locator(axis.Ticker, axis.Range, plot_height, true, axis.Formatter, axis.FormatterData); } } @@ -2431,22 +2530,7 @@ void SetupFinish() { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& axis = plot.XAxis(i); if (axis.WillRender() && axis.ShowDefaultTicks) { - if (axis.IsTime()) - AddTicksTime(axis.Range, plot_width, axis.Ticks); - else if (axis.IsLog()) - AddTicksLogarithmic(axis.Range, - plot_width, - false, - axis.Ticks, - axis.Formatter ? axis.Formatter : DefaultFormatter, - (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); - else - AddTicksDefault(axis.Range, - plot_width, - false, - axis.Ticks, - axis.Formatter ? axis.Formatter : DefaultFormatter, - (axis.Formatter && axis.FormatterData) ? axis.FormatterData : axis.HasFormatSpec ? axis.FormatSpec : (void*)IMPLOT_LABEL_FORMAT); + axis.Locator(axis.Ticker, axis.Range, plot_width, false, axis.Formatter, axis.FormatterData); } } @@ -2519,8 +2603,8 @@ void SetupFinish() { for (int i = 0; i < ImAxis_COUNT; i++) { ImPlotAxis& axis = plot.Axes[i]; if (axis.WillRender()) { - for (int t = 0; t < axis.Ticks.Size; t++) { - ImPlotTick& tk = axis.Ticks.Ticks[t]; + for (int t = 0; t < axis.Ticker.TickCount(); t++) { + ImPlotTick& tk = axis.Ticker.Ticks[t]; tk.PixelPos = IM_ROUND(axis.PlotToPixels(tk.PlotPos)); } } @@ -2530,12 +2614,12 @@ void SetupFinish() { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && !x_axis.IsForeground()) - RenderGridLinesX(DrawList, x_axis.Ticks, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); + RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && !y_axis.IsForeground()) - RenderGridLinesY(DrawList, y_axis.Ticks, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); + RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } // render x axis button, label, tick labels @@ -2552,26 +2636,26 @@ void SetupFinish() { else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } - const ImPlotTickCollection& tkc = ax.Ticks; + const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = ImGui::CalcTextSize(label); - const float label_offset = (ax.HasTickLabels() ? ax.Ticks.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) - + (ax.IsTime() ? txt_height + gp.Style.LabelPadding.y : 0) + const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.y + gp.Style.LabelPadding.y : 0.0f) + + (tkr.Levels - 1) * (txt_height + gp.Style.LabelPadding.y) + gp.Style.LabelPadding.y; const ImVec2 label_pos(plot.PlotRect.GetCenter().x - label_size.x * 0.5f, opp ? ax.Datum1 - label_offset - label_size.y : ax.Datum1 + label_offset); DrawList.AddText(label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { - for (int j = 0; j < tkc.Size; ++j) { - const ImPlotTick& tk = tkc.Ticks[j]; + for (int j = 0; j < tkr.TickCount(); ++j) { + const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? (-gp.Style.LabelPadding.y -txt_height -tk.Level * (txt_height + gp.Style.LabelPadding.y)) : gp.Style.LabelPadding.y + tk.Level * (txt_height + gp.Style.LabelPadding.y)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.x - 1 && tk.PixelPos <= plot.PlotRect.Max.x + 1) { ImVec2 start(tk.PixelPos - 0.5f * tk.LabelSize.x, datum); - DrawList.AddText(start, ax.ColorTxt, tkc.GetText(j)); + DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } @@ -2591,24 +2675,24 @@ void SetupFinish() { else if (ax.ColorBg != IM_COL32_BLACK_TRANS) { DrawList.AddRectFilled(ax.HoverRect.Min, ax.HoverRect.Max, ax.ColorBg); } - const ImPlotTickCollection& tkc = ax.Ticks; + const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); if (ax.HasLabel()) { const char* label = plot.GetAxisLabel(ax); const ImVec2 label_size = CalcTextSizeVertical(label); - const float label_offset = (ax.HasTickLabels() ? ax.Ticks.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) + const float label_offset = (ax.HasTickLabels() ? tkr.MaxSize.x + gp.Style.LabelPadding.x : 0.0f) + gp.Style.LabelPadding.x; const ImVec2 label_pos(opp ? ax.Datum1 + label_offset : ax.Datum1 - label_offset - label_size.x, plot.PlotRect.GetCenter().y + label_size.y * 0.5f); AddTextVertical(&DrawList, label_pos, ax.ColorTxt, label); } if (ax.HasTickLabels()) { - for (int j = 0; j < tkc.Size; ++j) { - const ImPlotTick& tk = tkc.Ticks[j]; + for (int j = 0; j < tkr.TickCount(); ++j) { + const ImPlotTick& tk = tkr.Ticks[j]; const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x)); if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1) { ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y); - DrawList.AddText(start, ax.ColorTxt, tkc.GetText(j)); + DrawList.AddText(start, ax.ColorTxt, tkr.GetText(j)); } } } @@ -2650,12 +2734,12 @@ void EndPlot() { for (int i = 0; i < IMPLOT_NUM_X_AXES; i++) { ImPlotAxis& x_axis = plot.XAxis(i); if (x_axis.Enabled && x_axis.HasGridLines() && x_axis.IsForeground()) - RenderGridLinesX(DrawList, x_axis.Ticks, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); + RenderGridLinesX(DrawList, x_axis.Ticker, plot.PlotRect, x_axis.ColorMaj, x_axis.ColorMin, gp.Style.MajorGridSize.x, gp.Style.MinorGridSize.x); } for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) { ImPlotAxis& y_axis = plot.YAxis(i); if (y_axis.Enabled && y_axis.HasGridLines() && y_axis.IsForeground()) - RenderGridLinesY(DrawList, y_axis.Ticks, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); + RenderGridLinesY(DrawList, y_axis.Ticker, plot.PlotRect, y_axis.ColorMaj, y_axis.ColorMin, gp.Style.MajorGridSize.y, gp.Style.MinorGridSize.y); } @@ -2671,13 +2755,13 @@ void EndPlot() { const ImPlotAxis& ax = plot.XAxis(i); if (!ax.Enabled) continue; - const ImPlotTickCollection& tkc = ax.Ticks; + const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_T > 0)||(!opp && count_B > 0)); if (ax.HasTickMarks()) { const float direction = opp ? 1.0f : -1.0f; - for (int j = 0; j < tkc.Size; ++j) { - const ImPlotTick& tk = tkc.Ticks[j]; + for (int j = 0; j < tkr.TickCount(); ++j) { + const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.x || tk.PixelPos > plot.PlotRect.Max.x) continue; const ImVec2 start(tk.PixelPos, ax.Datum1); @@ -2698,13 +2782,13 @@ void EndPlot() { const ImPlotAxis& ax = plot.YAxis(i); if (!ax.Enabled) continue; - const ImPlotTickCollection& tkc = ax.Ticks; + const ImPlotTicker& tkr = ax.Ticker; const bool opp = ax.IsOpposite(); const bool aux = ((opp && count_R > 0)||(!opp && count_L > 0)); if (ax.HasTickMarks()) { const float direction = opp ? -1.0f : 1.0f; - for (int j = 0; j < tkc.Size; ++j) { - const ImPlotTick& tk = tkc.Ticks[j]; + for (int j = 0; j < tkr.TickCount(); ++j) { + const ImPlotTick& tk = tkr.Ticks[j]; if (tk.Level != 0 || tk.PixelPos < plot.PlotRect.Min.y || tk.PixelPos > plot.PlotRect.Max.y) continue; const ImVec2 start(ax.Datum1, tk.PixelPos); @@ -2801,8 +2885,10 @@ void EndPlot() { if (i > 0) builder.append(", ("); double v = x_axis.PixelsToPlot(IO.MousePos.x); - no_fmt ? DefaultFormatter(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT) - : LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); + if (no_fmt) + Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); + else + LabelAxisValue(x_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); @@ -2816,8 +2902,10 @@ void EndPlot() { if (i > 0) builder.append(", ("); double v = y_axis.PixelsToPlot(IO.MousePos.y); - no_fmt ? DefaultFormatter(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT) - : LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); + if (no_fmt) + Formatter_Default(v,buff,IMPLOT_LABEL_MAX_SIZE,(void*)IMPLOT_LABEL_FORMAT); + else + LabelAxisValue(y_axis,v,buff,IMPLOT_LABEL_MAX_SIZE,true); builder.append(buff); if (i > 0) builder.append(")"); @@ -2940,7 +3028,7 @@ void EndPlot() { ImVec2 text_size = ImGui::CalcTextSize(txt); ImVec2 size = text_size + gp.Style.AnnotationPadding * 2; ImVec2 pos; - axis.Ticks.OverrideSizeLate(size); + axis.Ticker.OverrideSizeLate(size); float pix = IM_ROUND(axis.PlotToPixels(tag.Value)); if (axis.Vertical) { if (axis.IsOpposite()) { @@ -3718,7 +3806,7 @@ bool DragPoint(int n_id, double* x, double* y, const ImVec4& col, float radius, } bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPlotDragToolFlags flags) { - ImGui::PushID("#IMPLOT_DRAG_LINE_X"); + // ImGui::PushID("#IMPLOT_DRAG_LINE_X"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); @@ -3764,7 +3852,7 @@ bool DragLineX(int n_id, double* value, const ImVec4& col, float thickness, ImPl DrawList.AddLine(ImVec2(x,yb), ImVec2(x,yb-len), col32, 3*thickness); PopPlotClipRect(); - ImGui::PopID(); + // ImGui::PopID(); return dragging; } @@ -4382,11 +4470,11 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const frame_size.y = gp.Style.PlotMinSize.y; ImPlotRange range(scale_min,scale_max); - gp.CTicks.Reset(); - AddTicksDefault(range, frame_size.y, true, gp.CTicks, DefaultFormatter, (void*)fmt); + gp.CTicker.Reset(); + Locator_Default(gp.CTicker, range, frame_size.y, true, Formatter_Default, (void*)fmt); const float txt_off = gp.Style.LabelPadding.x; - const float pad_right = txt_off + gp.CTicks.MaxSize.x + (label_size.x > 0 ? txt_off + label_size.y : 0); + const float pad_right = txt_off + gp.CTicker.MaxSize.x + (label_size.x > 0 ? txt_off + label_size.y : 0); float bar_w = 20; if (frame_size.x == 0) @@ -4410,16 +4498,16 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, true, !gp.ColormapData.IsQual(cmap)); const ImU32 col_tick = GetStyleColorU32(ImPlotCol_AxisText); const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); - for (int i = 0; i < gp.CTicks.Size; ++i) { - const float ypos = ImRemap((float)gp.CTicks.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); - const float tick_width = gp.CTicks.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; - const float tick_thick = gp.CTicks.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; + for (int i = 0; i < gp.CTicker.TickCount(); ++i) { + const float ypos = ImRemap((float)gp.CTicker.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); + const float tick_width = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; + const float tick_thick = gp.CTicker.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y; if (ypos < bb_grad.Max.y - 2 && ypos > bb_grad.Min.y + 2) DrawList.AddLine(ImVec2(bb_grad.Max.x-1, ypos), ImVec2(bb_grad.Max.x - tick_width, ypos), col_tick, tick_thick); - DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -gp.CTicks.Ticks[i].LabelSize.y * 0.5f), col_text, gp.CTicks.GetText(i)); + DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -gp.CTicker.Ticks[i].LabelSize.y * 0.5f), col_text, gp.CTicker.GetText(i)); } if (label_size.x > 0) { - ImVec2 label_pos(bb_grad.Max.x - 1 + 2*txt_off + gp.CTicks.MaxSize.x, bb_grad.GetCenter().y + label_size.x*0.5f ); + ImVec2 label_pos(bb_grad.Max.x - 1 + 2*txt_off + gp.CTicker.MaxSize.x, bb_grad.GetCenter().y + label_size.x*0.5f ); const char* label_end = ImGui::FindRenderedTextEnd(label); AddTextVertical(&DrawList,label_pos,col_text,label,label_end); } @@ -4674,10 +4762,6 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::SliderFloat("ErrorBarWeight", &style.ErrorBarWeight, 0.0f, 5.0f, "%.1f"); ImGui::SliderFloat("DigitalBitHeight", &style.DigitalBitHeight, 0.0f, 20.0f, "%.1f"); ImGui::SliderFloat("DigitalBitGap", &style.DigitalBitGap, 0.0f, 20.0f, "%.1f"); - float indent = ImGui::CalcItemWidth() - ImGui::GetFrameHeight(); - ImGui::Indent(ImGui::CalcItemWidth() - ImGui::GetFrameHeight()); - ImGui::Checkbox("AntiAliasedLines", &style.AntiAliasedLines); - ImGui::Unindent(indent); ImGui::Text("Plot Styling"); ImGui::SliderFloat("PlotBorderSize", &style.PlotBorderSize, 0.0f, 2.0f, "%.0f"); ImGui::SliderFloat("MinorAlpha", &style.MinorAlpha, 0.0f, 1.0f, "%.2f"); @@ -4913,9 +4997,9 @@ void ShowUserGuide() { ImGui::BulletText("Click legend label icons to show/hide plot items."); } -void ShowTicksMetrics(const ImPlotTickCollection& ticks) { - ImGui::BulletText("Size: %d", ticks.Size); - ImGui::BulletText("MaxSize: [%f,%f]", ticks.MaxSize.x, ticks.MaxSize.y); +void ShowTicksMetrics(const ImPlotTicker& ticker) { + ImGui::BulletText("Size: %d", ticker.TickCount()); + ImGui::BulletText("MaxSize: [%f,%f]", ticker.MaxSize.x, ticker.MaxSize.y); } void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { @@ -4934,13 +5018,13 @@ void ShowAxisMetrics(const ImPlotPlot& plot, const ImPlotAxis& axis) { if (ImGui::TreeNode("Transform")) { ImGui::BulletText("PixelMin: %f", axis.PixelMin); ImGui::BulletText("PixelMax: %f", axis.PixelMax); - ImGui::BulletText("LinM: %f", axis.LinM); - ImGui::BulletText("LogD: %f", axis.LogD); + ImGui::BulletText("ScaleToPixel: %f", axis.ScaleToPixel); + ImGui::BulletText("ScaleMax: %f", axis.ScaleMax); ImGui::TreePop(); } if (ImGui::TreeNode("Ticks")) { - ShowTicksMetrics(axis.Ticks); + ShowTicksMetrics(axis.Ticker); ImGui::TreePop(); } } diff --git a/implot.h b/implot.h index 07f13af..8edb34d 100644 --- a/implot.h +++ b/implot.h @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2021 Evan Pezent +// Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.13 WIP +// ImPlot v0.14 // Table of Contents: // @@ -60,7 +60,7 @@ #endif // ImPlot version string. -#define IMPLOT_VERSION "0.13 WIP" +#define IMPLOT_VERSION "0.14" // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. @@ -83,11 +83,29 @@ typedef int ImPlotSubplotFlags; // -> enum ImPlotSubplotFlags_ typedef int ImPlotLegendFlags; // -> enum ImPlotLegendFlags_ typedef int ImPlotMouseTextFlags; // -> enum ImPlotMouseTextFlags_ typedef int ImPlotDragToolFlags; // -> ImPlotDragToolFlags_ + +typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ +typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ +typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags +typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ +typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ +typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ typedef int ImPlotBarGroupsFlags; // -> ImPlotBarGroupsFlags_ +typedef int ImPlotErrorBarsFlags; // -> ImPlotErrorBarsFlags_ +typedef int ImPlotStemsFlags; // -> ImPlotStemsFlags_ +typedef int ImPlotInfLinesFlags; // -> ImPlotInfLinesFlags_ +typedef int ImPlotPieChartFlags; // -> ImPlotPieChartFlags_ +typedef int ImPlotHeatmapFlags; // -> ImPlotHeatmapFlags_ +typedef int ImPlotHistogramFlags; // -> ImPlotHistogramFlags_ +typedef int ImPlotDigitalFlags; // -> ImPlotDigitalFlags_ +typedef int ImPlotImageFlags; // -> ImPlotImageFlags_ +typedef int ImPlotTextFlags; // -> ImPlotTextFlags_ +typedef int ImPlotDummyFlags; // -> ImPlotDummyFlags_ typedef int ImPlotCond; // -> enum ImPlotCond_ typedef int ImPlotCol; // -> enum ImPlotCol_ typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ +typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotMarker; // -> enum ImPlotMarker_ typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotLocation; // -> enum ImPlotLocation_ @@ -120,7 +138,6 @@ enum ImPlotFlags_ { ImPlotFlags_NoFrame = 1 << 7, // the ImGui frame will not be rendered ImPlotFlags_Equal = 1 << 8, // x and y axes pairs will be constrained to have the same units/pixel ImPlotFlags_Crosshairs = 1 << 9, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_AntiAliased = 1 << 10, // plot items will be software anti-aliased (not recommended for high density plots, prefer MSAA) ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText }; @@ -133,15 +150,14 @@ enum ImPlotAxisFlags_ { ImPlotAxisFlags_NoTickLabels = 1 << 3, // no text labels will be displayed ImPlotAxisFlags_NoInitialFit = 1 << 4, // axis will not be initially fit to data extents on the first rendered frame ImPlotAxisFlags_NoMenus = 1 << 5, // the user will not be able to open context menus with right-click - ImPlotAxisFlags_Opposite = 1 << 6, // axis ticks and labels will be rendered on conventionally opposite side (i.e, right or top) + ImPlotAxisFlags_Opposite = 1 << 6, // axis ticks and labels will be rendered on the conventionally opposite side (i.e, right or top) ImPlotAxisFlags_Foreground = 1 << 7, // grid lines will be displayed in the foreground (i.e. on top of data) in stead of the background - ImPlotAxisFlags_LogScale = 1 << 8, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) - ImPlotAxisFlags_Time = 1 << 9, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) - ImPlotAxisFlags_Invert = 1 << 10, // the axis will be inverted - ImPlotAxisFlags_AutoFit = 1 << 11, // axis will be auto-fitting to data extents - ImPlotAxisFlags_RangeFit = 1 << 12, // axis will only fit points if the point is in the visible range of the **orthogonal** axis - ImPlotAxisFlags_LockMin = 1 << 13, // the axis minimum value will be locked when panning/zooming - ImPlotAxisFlags_LockMax = 1 << 14, // the axis maximum value will be locked when panning/zooming + ImPlotAxisFlags_Invert = 1 << 8, // the axis will be inverted + ImPlotAxisFlags_AutoFit = 1 << 9, // axis will be auto-fitting to data extents + ImPlotAxisFlags_RangeFit = 1 << 10, // axis will only fit points if the point is in the visible range of the **orthogonal** axis + ImPlotAxisFlags_PanStretch = 1 << 11, // panning in a locked or constrained state will cause the axis to stretch if possible + ImPlotAxisFlags_LockMin = 1 << 12, // the axis minimum value will be locked when panning/zooming + ImPlotAxisFlags_LockMax = 1 << 13, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_AuxDefault = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_Opposite @@ -191,10 +207,111 @@ enum ImPlotDragToolFlags_ { ImPlotDragToolFlags_Delayed = 1 << 3, // tool rendering will be delayed one frame; useful when applying position-constraints }; -// Flags for ImPlot::PlotBarGroups +// Flags for ANY PlotX function +enum ImPlotItemFlags_ { + ImPlotItemFlags_None = 0, + ImPlotItemFlags_NoLegend = 1 << 0, // the item won't have a legend entry displayed + ImPlotItemFlags_NoFit = 1 << 1, // the item won't be considered for plot fits +}; + +// Flags for PlotLine +enum ImPlotLineFlags_ { + ImPlotLineFlags_None = 0, // default + ImPlotLineFlags_Segments = 1 << 10, // a line segment will be rendered from every two consecutive points + ImPlotLineFlags_Loop = 1 << 11, // the last and first point will be connected to form a closed loop + ImPlotLineFlags_SkipNaN = 1 << 12, // NaNs values will be skipped instead of rendered as missing data + ImPlotLineFlags_NoClip = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped +}; + +// Flags for PlotScatter +enum ImPlotScatterFlags_ { + ImPlotScatterFlags_None = 0, // default + ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped +}; + +// Flags for PlotStairs +enum ImPlotStairsFlags_ { + ImPlotStairsFlags_None = 0, // default + ImPlotStairsFlags_PreStep = 1 << 10 // the y value is continued constantly to the left from every x position, i.e. the interval (x[i-1], x[i]] has the value y[i] +}; + +// Flags for PlotShaded (placeholder) +enum ImPlotShadedFlags_ { + ImPlotShadedFlags_None = 0 // default +}; + +// Flags for PlotBars +enum ImPlotBarsFlags_ { + ImPlotBarsFlags_None = 0, // default + ImPlotBarsFlags_Horizontal = 1 << 10, // bars will be rendered horizontally on the current y-axis +}; + +// Flags for PlotBarGroups enum ImPlotBarGroupsFlags_ { - ImPlotBarGroupsFlags_None = 0, // default - ImPlotBarGroupsFlags_Stacked = 1 << 0, // items in a group will be stacked on top of each other + ImPlotBarGroupsFlags_None = 0, // default + ImPlotBarGroupsFlags_Horizontal = 1 << 10, // bar groups will be rendered horizontally on the current y-axis + ImPlotBarGroupsFlags_Stacked = 1 << 11, // items in a group will be stacked on top of each other +}; + +// Flags for PlotErrorBars +enum ImPlotErrorBarsFlags_ { + ImPlotErrorBarsFlags_None = 0, // default + ImPlotErrorBarsFlags_Horizontal = 1 << 10, // error bars will be rendered horizontally on the current y-axis +}; + +// Flags for PlotStems +enum ImPlotStemsFlags_ { + ImPlotStemsFlags_None = 0, // default + ImPlotStemsFlags_Horizontal = 1 << 10, // stems will be rendered horizontally on the current y-axis +}; + +// Flags for PlotInfLines +enum ImPlotInfLinesFlags_ { + ImPlotInfLinesFlags_None = 0, // default + ImPlotInfLinesFlags_Horizontal = 1 << 10 // lines will be rendered horizontally on the current y-axis +}; + +// Flags for PlotPieChart +enum ImPlotPieChartFlags_ { + ImPlotPieChartFlags_None = 0, // default + ImPlotPieChartFlags_Normalize = 1 << 10 // force normalization of pie chart values (i.e. always make a full circle if sum < 0) +}; + +// Flags for PlotHeatmap +enum ImPlotHeatmapFlags_ { + ImPlotHeatmapFlags_None = 0, // default + ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order +}; + +// Flags for PlotHistogram and PlotHistogram2D +enum ImPlotHistogramFlags_ { + ImPlotHistogramFlags_None = 0, // default + ImPlotHistogramFlags_Horizontal = 1 << 10, // histogram bars will be rendered horizontally (not supported by PlotHistogram2D) + ImPlotHistogramFlags_Cumulative = 1 << 11, // each bin will contain its count plus the counts of all previous bins (not supported by PlotHistogram2D) + ImPlotHistogramFlags_Density = 1 << 12, // counts will be normalized, i.e. the PDF will be visualized, or the CDF will be visualized if Cumulative is also set + ImPlotHistogramFlags_NoOutliers = 1 << 13, // exclude values outside the specifed histogram range from the count toward normalizing and cumulative counts + ImPlotHistogramFlags_ColMajor = 1 << 14 // data will be read in column major order (not supported by PlotHistogram) +}; + +// Flags for PlotDigital (placeholder) +enum ImPlotDigitalFlags_ { + ImPlotDigitalFlags_None = 0 // default +}; + +// Flags for PlotImage (placeholder) +enum ImPlotImageFlags_ { + ImPlotImageFlags_None = 0 // default +}; + +// Flags for PlotText +enum ImPlotTextFlags_ { + ImPlotTextFlags_None = 0, // default + ImPlotTextFlags_Vertical = 1 << 10 // text will be rendered vertically +}; + +// Flags for PlotDummy (placeholder) +enum ImPlotDummyFlags_ { + ImPlotDummyFlags_None = 0 // default }; // Represents a condition for SetupAxisLimits etc. (same as ImGuiCond, but we only support a subset of those enums) @@ -267,10 +384,18 @@ enum ImPlotStyleVar_ { ImPlotStyleVar_COUNT }; +// Axis scale +enum ImPlotScale_ { + ImPlotScale_Linear = 0, // default linear scale + ImPlotScale_Time, // date/time scale + ImPlotScale_Log10, // base 10 logartithmic scale + ImPlotScale_SymLog, // symmetric log scale +}; + // Marker specifications. enum ImPlotMarker_ { ImPlotMarker_None = -1, // no marker - ImPlotMarker_Circle, // a circle marker + ImPlotMarker_Circle, // a circle marker (default) ImPlotMarker_Square, // a square maker ImPlotMarker_Diamond, // a diamond marker ImPlotMarker_Up, // an upward-pointing triangle marker @@ -398,7 +523,6 @@ struct ImPlotStyle { // colormap ImPlotColormap Colormap; // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap. // settings/flags - bool AntiAliasedLines; // = false, enable global anti-aliasing on plot lines (overrides ImPlotFlags_AntiAliased) bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.) bool Use24HourClock; // = false, times will be formatted using a 24 hour clock @@ -436,10 +560,13 @@ struct ImPlotInputMap { //----------------------------------------------------------------------------- // Callback signature for axis tick label formatter. -typedef void (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); +typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_data); // Callback signature for data getter. -typedef ImPlotPoint (*ImPlotGetter)(void* user_data, int idx); +typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); + +// Callback signature for axis transform. +typedef double (*ImPlotTransform)(double value, void* user_data); namespace ImPlot { @@ -482,7 +609,7 @@ IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); // (e.g. "MyPlot##HiddenIdText" or "##NoTitle"). // - #size is the **frame** size of the plot widget, not the plot area. The default // size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle. -IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size = ImVec2(-1,0), ImPlotFlags flags = ImPlotFlags_None); +IMPLOT_API bool BeginPlot(const char* title_id, const ImVec2& size=ImVec2(-1,0), ImPlotFlags flags=0); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end // of an if statement conditioned on BeginPlot(). See example above. @@ -542,7 +669,7 @@ IMPLOT_API bool BeginSubplots(const char* title_id, int rows, int cols, const ImVec2& size, - ImPlotSubplotFlags flags = ImPlotSubplotFlags_None, + ImPlotSubplotFlags flags = 0, float* row_ratios = NULL, float* col_ratios = NULL); @@ -580,7 +707,7 @@ IMPLOT_API void EndSubplots(); // call it for you. // Enables an axis or sets the label and/or flags for an existing axis. Leave #label = NULL for no label. -IMPLOT_API void SetupAxis(ImAxis axis, const char* label = NULL, ImPlotAxisFlags flags = ImPlotAxisFlags_None); +IMPLOT_API void SetupAxis(ImAxis axis, const char* label=NULL, ImPlotAxisFlags flags=0); // Sets an axis range limits. If ImPlotCond_Always is used, the axes limits will be locked. IMPLOT_API void SetupAxisLimits(ImAxis axis, double v_min, double v_max, ImPlotCond cond = ImPlotCond_Once); // Links an axis range limits to external values. Set to NULL for no linkage. The pointer data must remain valid until EndPlot. @@ -588,21 +715,29 @@ IMPLOT_API void SetupAxisLinks(ImAxis axis, double* link_min, double* link_max); // Sets the format of numeric axis labels via formater specifier (default="%g"). Formated values will be double (i.e. use %f). IMPLOT_API void SetupAxisFormat(ImAxis axis, const char* fmt); // Sets the format of numeric axis labels via formatter callback. Given #value, write a label into #buff. Optionally pass user data. -IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data = NULL); +IMPLOT_API void SetupAxisFormat(ImAxis axis, ImPlotFormatter formatter, void* data=NULL); // Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); +IMPLOT_API void SetupAxisTicks(ImAxis axis, const double* values, int n_ticks, const char* const labels[]=NULL, bool keep_default=false); // Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true. -IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false); +IMPLOT_API void SetupAxisTicks(ImAxis axis, double v_min, double v_max, int n_ticks, const char* const labels[]=NULL, bool keep_default=false); +// Sets an axis' scale using built-in options. +IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotScale scale); +// Sets an axis' scale using user supplied forward and inverse transfroms. +IMPLOT_API void SetupAxisScale(ImAxis axis, ImPlotTransform forward, ImPlotTransform inverse, void* data=NULL); +// Sets an axis' limits constraints. +IMPLOT_API void SetupAxisLimitsConstraints(ImAxis axis, double v_min, double v_max); +// Sets an axis' zoom constraints. +IMPLOT_API void SetupAxisZoomConstraints(ImAxis axis, double z_min, double z_max); // Sets the label and/or flags for primary X and Y axes (shorthand for two calls to SetupAxis). -IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, ImPlotAxisFlags y_flags = ImPlotAxisFlags_None); +IMPLOT_API void SetupAxes(const char* x_label, const char* y_label, ImPlotAxisFlags x_flags=0, ImPlotAxisFlags y_flags=0); // Sets the primary X and Y axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits). IMPLOT_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, ImPlotCond cond = ImPlotCond_Once); // Sets up the plot legend. -IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags = ImPlotLegendFlags_None); +IMPLOT_API void SetupLegend(ImPlotLocation location, ImPlotLegendFlags flags=0); // Set the location of the current plot's mouse position text (default = South|East). -IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags = ImPlotMouseTextFlags_None); +IMPLOT_API void SetupMouseText(ImPlotLocation location, ImPlotMouseTextFlags flags=0); // Explicitly finalize plot setup. Once you call this, you cannot make anymore Setup calls for the current plot! // Note that calling this function is OPTIONAL; it will be called by the first subsequent setup-locking API call. @@ -666,7 +801,7 @@ IMPLOT_API void SetNextAxesToFit(); // struct Vector2f { float X, Y; }; // ... // Vector2f data[42]; -// ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, sizeof(Vector2f)); // or sizeof(float)*2 +// ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, 0, sizeof(Vector2f)); // // 2. Write a custom getter C function or C++ lambda and pass it and optionally your data to // an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a slight performance @@ -680,7 +815,7 @@ IMPLOT_API void SetNextAxesToFit(); // return p // } // ... -// auto my_lambda = [](void*, int idx) { +// auto my_lambda = [](int idx, void*) { // double t = idx / 999.0; // return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t)); // }; @@ -696,86 +831,71 @@ IMPLOT_API void SetNextAxesToFit(); // if you try plotting extremely large 64-bit integral types. Proceed with caution! // Plots a standard 2D line plot. -IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count); +IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0); // Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. -IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count); +IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); -// Plots a a stairstep graph. The y value is continued constantly from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i]. -IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count); +// Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] +IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotStairsG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotStairsFlags flags=0); // Plots a shaded (filled) region between two lines, or a line and a horizontal reference. Set yref to +/-INFINITY for infinite fill extents. -IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count); +IMPLOT_TMP void PlotShaded(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double xstart=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double yref=0, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotShaded(const char* label_id, const T* xs, const T* ys1, const T* ys2, int count, ImPlotShadedFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotShadedG(const char* label_id, ImPlotGetter getter1, void* data1, ImPlotGetter getter2, void* data2, int count, ImPlotShadedFlags flags=0); -// Plots a vertical bar graph. #bar_width and #x0 are in X units. -IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_width=0.67, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_width, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_width); +// Plots a bar graph. Vertical by default. #bar_size and #shift are in plot units. +IMPLOT_TMP void PlotBars(const char* label_id, const T* values, int count, double bar_size=0.67, double shift=0, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotBars(const char* label_id, const T* xs, const T* ys, int count, double bar_size, ImPlotBarsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotBarsG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_size, ImPlotBarsFlags flags=0); -// Plots a horizontal bar graph. #bar_height and #y0 are in Y units. -IMPLOT_TMP void PlotBarsH(const char* label_id, const T* values, int count, double bar_height=0.67, double y0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double bar_height, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotGetter getter, void* data, int count, double bar_height); - -// Plots a group of vertical bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. -IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_width=0.67, double x0=0, ImPlotBarGroupsFlags flags=ImPlotBarGroupsFlags_None); - -// Plots a group of horizontal bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. -IMPLOT_TMP void PlotBarGroupsH(const char* const label_ids[], const T* values, int item_count, int group_count, double group_height=0.67, double y0=0, ImPlotBarGroupsFlags flags=ImPlotBarGroupsFlags_None); +// Plots a group of bars. #values is a row-major matrix with #item_count rows and #group_count cols. #label_ids should have #item_count elements. +IMPLOT_TMP void PlotBarGroups(const char* const label_ids[], const T* values, int item_count, int group_count, double group_size=0.67, double shift=0, ImPlotBarGroupsFlags flags=0); // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. -IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, ImPlotErrorBarsFlags flags=0, int offset=0, int stride=sizeof(T)); -// Plots horizontal error bars. The label_id should be the same as the label_id of the associated line or bar plot. -IMPLOT_TMP void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotErrorBarsH(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); +// Plots stems. Vertical by default. +IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double ref=0, double scale=1, double start=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double ref=0, ImPlotStemsFlags flags=0, int offset=0, int stride=sizeof(T)); -/// Plots vertical stems. -IMPLOT_TMP void PlotStems(const char* label_id, const T* values, int count, double yref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double yref=0, int offset=0, int stride=sizeof(T)); +// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). +IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, ImPlotInfLinesFlags flags=0, int offset=0, int stride=sizeof(T)); -/// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). -IMPLOT_TMP void PlotVLines(const char* label_id, const T* xs, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_TMP void PlotHLines(const char* label_id, const T* ys, int count, int offset=0, int stride=sizeof(T)); +// Plots a pie chart. Center and radius are in plot units. #label_fmt can be set to NULL for no labels. +IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); -// Plots a pie chart. If the sum of values > 1 or normalize is true, each value will be normalized. Center and radius are in plot units. #label_fmt can be set to NULL for no labels. -IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, bool normalize=false, const char* label_fmt="%.1f", double angle0=90); +// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to NULL for no labels. +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); -// Plots a 2D heatmap chart. Values are expected to be in row-major order. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to NULL for no labels. -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); +// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. +// Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. +IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0); -// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #cumulative is true, each bin contains its count plus the counts of all previous bins. -// If #density is true, the PDF is visualized. If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. -// If #range is specified, outlier values outside of the range are not binned. However, outliers still count toward normalizing and cumulative counts unless #outliers is false. The largest bin count or density is returned. -IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), bool outliers=true, double bar_scale=1.0); - -// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #density is true, the PDF is visualized. -// If #bounds is left unspecified, the min/max of #xs an #ys will be used as the ranges. If #bounds is specified, outlier values outside of range are not binned. -// However, outliers still count toward the normalizing count for density plots unless #outliers is false. The largest bin count or density is returned. -IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, bool density=false, ImPlotRect range=ImPlotRect(), bool outliers=true); +// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #range is left unspecified, the min/max of +// #xs an #ys will be used as the ranges. Otherwise, outlier values outside of range are not binned. The largest bin count or density is returned. +IMPLOT_TMP double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, ImPlotRect range=ImPlotRect(), ImPlotHistogramFlags flags=0); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. -IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); -IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count); +IMPLOT_TMP void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, ImPlotDigitalFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotDigitalFlags flags=0); // Plots an axis-aligned image. #bounds_min/bounds_max are in plot coordinates (y-up) and #uv0/uv1 are in texture coordinates (y-down). -IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1)); +IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1), ImPlotImageFlags flags=0); // Plots a centered text label at point x,y with an optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). -IMPLOT_API void PlotText(const char* text, double x, double y, bool vertical=false, const ImVec2& pix_offset=ImVec2(0,0)); +IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix_offset=ImVec2(0,0), ImPlotTextFlags flags=0); // Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) -IMPLOT_API void PlotDummy(const char* label_id); +IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0); //----------------------------------------------------------------------------- // [SECTION] Plot Tools @@ -786,28 +906,28 @@ IMPLOT_API void PlotDummy(const char* label_id); // axes, which can be changed with `SetAxis/SetAxes`. // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +IMPLOT_API bool DragPoint(int id, double* x, double* y, const ImVec4& col, float size = 4, ImPlotDragToolFlags flags=0); // Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +IMPLOT_API bool DragLineX(int id, double* x, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); // Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. -IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +IMPLOT_API bool DragLineY(int id, double* y, const ImVec4& col, float thickness = 1, ImPlotDragToolFlags flags=0); // Shows a draggable and resizeable rectangle. -IMPLOT_API bool DragRect(int id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags = ImPlotDragToolFlags_None); +IMPLOT_API bool DragRect(int id, double* x_min, double* y_min, double* x_max, double* y_max, const ImVec4& col, ImPlotDragToolFlags flags=0); // Shows an annotation callout at a chosen point. Clamping keeps annotations in the plot area. Annotations are always rendered on top. -IMPLOT_API void Annotation(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, bool round = false); -IMPLOT_API void Annotation(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); -IMPLOT_API void AnnotationV(double x, double y, const ImVec4& color, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); +IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, bool round = false); +IMPLOT_API void Annotation(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, ...) IM_FMTARGS(6); +IMPLOT_API void AnnotationV(double x, double y, const ImVec4& col, const ImVec2& pix_offset, bool clamp, const char* fmt, va_list args) IM_FMTLIST(6); // Shows a x-axis tag at the specified coordinate value. -IMPLOT_API void TagX(double x, const ImVec4& color, bool round = false); -IMPLOT_API void TagX(double x, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(3); -IMPLOT_API void TagXV(double x, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(3); +IMPLOT_API void TagX(double x, const ImVec4& col, bool round = false); +IMPLOT_API void TagX(double x, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void TagXV(double x, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); // Shows a y-axis tag at the specified coordinate value. -IMPLOT_API void TagY(double y, const ImVec4& color, bool round = false); -IMPLOT_API void TagY(double y, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(3); -IMPLOT_API void TagYV(double y, const ImVec4& color, const char* fmt, va_list args) IM_FMTLIST(3); +IMPLOT_API void TagY(double y, const ImVec4& col, bool round = false); +IMPLOT_API void TagY(double y, const ImVec4& col, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void TagYV(double y, const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(3); //----------------------------------------------------------------------------- // [SECTION] Plot Utils @@ -869,7 +989,7 @@ IMPLOT_API void EndAlignedPlots(); //----------------------------------------------------------------------------- // Begin a popup for a legend entry. -IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button = 1); +IMPLOT_API bool BeginLegendPopup(const char* label_id, ImGuiMouseButton mouse_button=1); // End a popup for a legend entry. IMPLOT_API void EndLegendPopup(); // Returns true if a plot item legend entry is hovered. @@ -892,11 +1012,11 @@ IMPLOT_API void EndDragDropTarget(); // You can change the modifier if desired. If ImGuiModFlags_None is provided, the axes will be locked from panning. // Turns the current plot's plotting area into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags = 0); +IMPLOT_API bool BeginDragDropSourcePlot(ImGuiDragDropFlags flags=0); // Turns the current plot's X-axis into a drag and drop source. You must hold Ctrl. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags = 0); +IMPLOT_API bool BeginDragDropSourceAxis(ImAxis axis, ImGuiDragDropFlags flags=0); // Turns an item in the current plot's legend into drag and drop source. Don't forget to call EndDragDropSource! -IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags = 0); +IMPLOT_API bool BeginDragDropSourceItem(const char* label_id, ImGuiDragDropFlags flags=0); // Ends a drag and drop source (currently just an alias for ImGui::EndDragDropSource). IMPLOT_API void EndDragDropSource(); @@ -1140,8 +1260,8 @@ IMPLOT_DEPRECATED( IMPLOT_API bool BeginPlot(const char* title_id, const char* y_label, // = NULL, const ImVec2& size = ImVec2(-1,0), ImPlotFlags flags = ImPlotFlags_None, - ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, - ImPlotAxisFlags y_flags = ImPlotAxisFlags_None, + ImPlotAxisFlags x_flags = 0, + ImPlotAxisFlags y_flags = 0, ImPlotAxisFlags y2_flags = ImPlotAxisFlags_AuxDefault, ImPlotAxisFlags y3_flags = ImPlotAxisFlags_AuxDefault, const char* y2_label = NULL, diff --git a/implot_demo.cpp b/implot_demo.cpp index ac49157..f4bbe00 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2021 Evan Pezent +// Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.13 WIP +// ImPlot v0.14 // We define this so that the demo does not accidentally use deprecated API #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS @@ -41,6 +41,8 @@ #define PI 3.14159265358979323846 #endif +#define CHECKBOX_FLAG(flags, flag) ImGui::CheckboxFlags(#flag, (unsigned int*)&flags, flag) + // Encapsulates examples for customizing ImPlot. namespace MyImPlot { @@ -55,9 +57,9 @@ struct WaveData { double X, Amp, Freq, Offset; WaveData(double x, double amp, double freq, double offset) { X = x; Amp = amp; Freq = freq; Offset = offset; } }; -ImPlotPoint SineWave(void* wave_data, int idx); -ImPlotPoint SawWave(void* wave_data, int idx); -ImPlotPoint Spiral(void*, int idx); +ImPlotPoint SineWave(int idx, void* wave_data); +ImPlotPoint SawWave(int idx, void* wave_data); +ImPlotPoint Spiral(int idx, void* wave_data); // Example for Tables section. void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size); @@ -72,8 +74,6 @@ void StyleSeaborn(); namespace ImPlot { -void ShowBenchmarkTool(); - template inline T RandomRange(T min, T max) { T scale = rand() / (T) RAND_MAX; @@ -183,7 +183,7 @@ struct HugeTimeData { // [SECTION] Demo Functions //----------------------------------------------------------------------------- -void ShowDemo_Help() { +void Demo_Help() { ImGui::Text("ABOUT THIS DEMO:"); ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Tools\" menu above gives access to: Style Editors (ImPlot/ImGui)\n" @@ -191,13 +191,6 @@ void ShowDemo_Help() { ImGui::Separator(); ImGui::Text("PROGRAMMER GUIDE:"); ImGui::BulletText("See the ShowDemoWindow() code in implot_demo.cpp. <- you are here!"); - ImGui::BulletText("By default, anti-aliased lines are turned OFF."); - ImGui::Indent(); - ImGui::BulletText("Software AA can be enabled globally with ImPlotStyle.AntiAliasedLines."); - ImGui::BulletText("Software AA can be enabled per plot with ImPlotFlags_AntiAliased."); - ImGui::BulletText("AA for plots can be toggled from the plot's context menu."); - ImGui::BulletText("If permitable, you are better off using hardware AA (e.g. MSAA)."); - ImGui::Unindent(); ImGui::BulletText("If you see visual artifacts, do one of the following:"); ImGui::Indent(); ImGui::BulletText("Handle ImGuiBackendFlags_RendererHasVtxOffset for 16-bit indices in your backend."); @@ -263,21 +256,20 @@ void ShowInputMapping() { ImGui::SliderFloat("ZoomRate",&map.ZoomRate,-1,1); } -void ShowDemo_Config() { +void Demo_Config() { ImGui::ShowFontSelector("Font"); ImGui::ShowStyleSelector("ImGui Style"); ImPlot::ShowStyleSelector("ImPlot Style"); ImPlot::ShowColormapSelector("ImPlot Colormap"); ImPlot::ShowInputMapSelector("Input Map"); ImGui::Separator(); - ImGui::Checkbox("Anti-Aliased Lines", &ImPlot::GetStyle().AntiAliasedLines); ImGui::Checkbox("Use Local Time", &ImPlot::GetStyle().UseLocalTime); ImGui::Checkbox("Use ISO 8601", &ImPlot::GetStyle().UseISO8601); ImGui::Checkbox("Use 24 Hour Clock", &ImPlot::GetStyle().Use24HourClock); ImGui::Separator(); if (ImPlot::BeginPlot("Preview")) { static double now = (double)time(0); - ImPlot::SetupAxis(ImAxis_X1,NULL,ImPlotAxisFlags_Time); + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxisLimits(ImAxis_X1, now, now + 24*3600); for (int i = 0; i < 10; ++i) { double x[2] = {now, now + 24*3600}; @@ -292,30 +284,29 @@ void ShowDemo_Config() { //----------------------------------------------------------------------------- -void ShowDemo_LinePlots() { +void Demo_LinePlots() { static float xs1[1001], ys1[1001]; for (int i = 0; i < 1001; ++i) { xs1[i] = i * 0.001f; ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); } - static double xs2[11], ys2[11]; - for (int i = 0; i < 11; ++i) { - xs2[i] = i * 0.1f; + static double xs2[20], ys2[20]; + for (int i = 0; i < 20; ++i) { + xs2[i] = i * 1/19.0f; ys2[i] = xs2[i] * xs2[i]; } - ImGui::BulletText("Anti-aliasing can be enabled from the plot's context menu (see Help)."); if (ImPlot::BeginPlot("Line Plot")) { - ImPlot::SetupAxes("x","f(x)"); - ImPlot::PlotLine("sin(x)", xs1, ys1, 1001); + ImPlot::SetupAxes("x","y"); + ImPlot::PlotLine("f(x)", xs1, ys1, 1001); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotLine("x^2", xs2, ys2, 11); + ImPlot::PlotLine("g(x)", xs2, ys2, 20,ImPlotLineFlags_Segments); ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- -void ShowDemo_FilledLinePlots() { +void Demo_FilledLinePlots() { static double xs1[101], ys1[101], ys2[101], ys3[101]; srand(0); for (int i = 0; i < 101; ++i) { @@ -328,6 +319,7 @@ void ShowDemo_FilledLinePlots() { static bool show_fills = true; static float fill_ref = 0; static int shade_mode = 0; + static ImPlotShadedFlags flags = 0; ImGui::Checkbox("Lines",&show_lines); ImGui::SameLine(); ImGui::Checkbox("Fills",&show_fills); if (show_fills) { @@ -352,9 +344,9 @@ void ShowDemo_FilledLinePlots() { ImPlot::SetupAxesLimits(0,100,0,500); if (show_fills) { ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); - ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); - ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref); + ImPlot::PlotShaded("Stock 1", xs1, ys1, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); + ImPlot::PlotShaded("Stock 2", xs1, ys2, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); + ImPlot::PlotShaded("Stock 3", xs1, ys3, 101, shade_mode == 0 ? -INFINITY : shade_mode == 1 ? INFINITY : fill_ref, flags); ImPlot::PopStyleVar(); } if (show_lines) { @@ -368,7 +360,7 @@ void ShowDemo_FilledLinePlots() { //----------------------------------------------------------------------------- -void ShowDemo_ShadedPlots() { +void Demo_ShadedPlots() { static float xs[1001], ys[1001], ys1[1001], ys2[1001], ys3[1001], ys4[1001]; srand(0); for (int i = 0; i < 1001; ++i) { @@ -396,7 +388,7 @@ void ShowDemo_ShadedPlots() { //----------------------------------------------------------------------------- -void ShowDemo_ScatterPlots() { +void Demo_ScatterPlots() { srand(0); static float xs1[100], ys1[100]; for (int i = 0; i < 100; ++i) { @@ -421,33 +413,44 @@ void ShowDemo_ScatterPlots() { //----------------------------------------------------------------------------- -void ShowDemo_StairstepPlots() { - static float ys1[101], ys2[101]; - for (int i = 0; i < 101; ++i) { - ys1[i] = 0.5f + 0.4f * sinf(50 * i * 0.01f); - ys2[i] = 0.5f + 0.2f * sinf(25 * i * 0.01f); +void Demo_StairstepPlots() { + static float ys1[21], ys2[21]; + for (int i = 0; i < 21; ++i) { + ys1[i] = 0.75f + 0.2f * sinf(10 * i * 0.05f); + ys2[i] = 0.25f + 0.2f * sinf(10 * i * 0.05f); } if (ImPlot::BeginPlot("Stairstep Plot")) { ImPlot::SetupAxes("x","f(x)"); - ImPlot::PlotStairs("Signal 1", ys1, 101, 0.01f); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 2.0f); - ImPlot::PlotStairs("Signal 2", ys2, 101, 0.01f); + ImPlot::SetupAxesLimits(0,1,0,1); + + ImPlot::PushStyleColor(ImPlotCol_Line, ImVec4(0.5f,0.5f,0.5f,1.0f)); + ImPlot::PlotLine("##1",ys1,21,0.05f); + ImPlot::PlotLine("##2",ys2,21,0.05f); + ImPlot::PopStyleColor(); + + ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::PlotStairs("Post Step (default)", ys1, 21, 0.05f); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::PlotStairs("Pre Step", ys2, 21, 0.05f, 0, ImPlotStairsFlags_PreStep); + ImPlot::EndPlot(); } } //----------------------------------------------------------------------------- -void ShowDemo_BarPlots() { +void Demo_BarPlots() { static ImS8 data[10] = {1,2,3,4,5,6,7,8,9,10}; if (ImPlot::BeginPlot("Bar Plot")) { - ImPlot::PlotBars("Bars",data,10,0.7,1); - ImPlot::PlotBarsH("BarsH",data,10,0.4,1); + ImPlot::PlotBars("Vertical",data,10,0.7,1); + ImPlot::PlotBars("Horizontal",data,10,0.4,1,ImPlotBarsFlags_Horizontal); ImPlot::EndPlot(); } } -void ShowDemo_BarGroups() { +//----------------------------------------------------------------------------- + +void Demo_BarGroups() { static ImS8 data[30] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90, // midterm 80, 62, 56, 99, 55, 78, 88, 78, 90, 100, // final 80, 69, 52, 92, 72, 78, 75, 76, 89, 95}; // course @@ -475,7 +478,7 @@ void ShowDemo_BarGroups() { if (horz) { ImPlot::SetupAxes("Score","Student",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); ImPlot::SetupAxisTicks(ImAxis_Y1,positions, groups, glabels); - ImPlot::PlotBarGroupsH(ilabels,data,items,groups,size,0,flags); + ImPlot::PlotBarGroups(ilabels,data,items,groups,size,0,flags|ImPlotBarGroupsFlags_Horizontal); } else { ImPlot::SetupAxes("Student","Score",ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); @@ -486,7 +489,9 @@ void ShowDemo_BarGroups() { } } -void ShowDemo_BarStacks() { +//----------------------------------------------------------------------------- + +void Demo_BarStacks() { static ImPlotColormap Liars = -1; if (Liars == -1) { @@ -524,9 +529,9 @@ void ShowDemo_BarStacks() { ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_Invert); ImPlot::SetupAxisTicks(ImAxis_Y1,0,19,20,politicians,false); if (diverging) - ImPlot::PlotBarGroupsH(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked); + ImPlot::PlotBarGroups(labels_div,data_div,9,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); else - ImPlot::PlotBarGroupsH(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked); + ImPlot::PlotBarGroups(labels_reg,data_reg,6,20,0.75,0,ImPlotBarGroupsFlags_Stacked|ImPlotBarGroupsFlags_Horizontal); ImPlot::EndPlot(); } ImPlot::PopColormap(); @@ -534,7 +539,7 @@ void ShowDemo_BarStacks() { //----------------------------------------------------------------------------- -void ShowDemo_ErrorBars() { +void Demo_ErrorBars() { static float xs[5] = {1,2,3,4,5}; static float bar[5] = {1,2,5,3,4}; static float lin1[5] = {8,8,9,7,8}; @@ -551,18 +556,20 @@ void ShowDemo_ErrorBars() { ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); ImPlot::SetNextErrorBarStyle(ImPlot::GetColormapColor(1), 0); ImPlot::PlotErrorBars("Line", xs, lin1, err1, err2, 5); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::PlotLine("Line", xs, lin1, 5); ImPlot::PushStyleColor(ImPlotCol_ErrorBar, ImPlot::GetColormapColor(2)); ImPlot::PlotErrorBars("Scatter", xs, lin2, err2, 5); - ImPlot::PlotErrorBarsH("Scatter", xs, lin2, err3, err4, 5); + ImPlot::PlotErrorBars("Scatter", xs, lin2, err3, err4, 5, ImPlotErrorBarsFlags_Horizontal); ImPlot::PopStyleColor(); ImPlot::PlotScatter("Scatter", xs, lin2, 5); ImPlot::EndPlot(); } } -void ShowDemo_StemPlots() { +//----------------------------------------------------------------------------- + +void Demo_StemPlots() { static double xs[51], ys1[51], ys2[51]; for (int i = 0; i < 51; ++i) { xs[i] = i * 0.02; @@ -573,37 +580,41 @@ void ShowDemo_StemPlots() { ImPlot::SetupAxisLimits(ImAxis_X1,0,1.0); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1.6); ImPlot::PlotStems("Stems 1",xs,ys1,51); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square,5); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotStems("Stems 2", xs, ys2,51); ImPlot::EndPlot(); } } -void ShowDemo_InfiniteLines() { +//----------------------------------------------------------------------------- + +void Demo_InfiniteLines() { static double vals[] = {0.25, 0.5, 0.75}; if (ImPlot::BeginPlot("##Infinite")) { ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoInitialFit,ImPlotAxisFlags_NoInitialFit); - ImPlot::PlotVLines("VLines",vals,3); - ImPlot::PlotHLines("HLines",vals,3); + ImPlot::PlotInfLines("Vertical",vals,3); + ImPlot::PlotInfLines("Horizontal",vals,3,ImPlotInfLinesFlags_Horizontal); ImPlot::EndPlot(); } } -void ShowDemo_PieCharts() { - static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; - static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; - static bool normalize = false; +//----------------------------------------------------------------------------- + +void Demo_PieCharts() { + static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; + static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; + static ImPlotPieChartFlags flags = 0; ImGui::SetNextItemWidth(250); ImGui::DragFloat4("Values", data1, 0.01f, 0, 1); if ((data1[0] + data1[1] + data1[2] + data1[3]) < 1) { ImGui::SameLine(); - ImGui::Checkbox("Normalize", &normalize); + CHECKBOX_FLAG(flags,ImPlotPieChartFlags_Normalize); } if (ImPlot::BeginPlot("##Pie1", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, normalize, "%.2f"); + ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, "%.2f", 90, flags); ImPlot::EndPlot(); } @@ -616,13 +627,15 @@ void ShowDemo_PieCharts() { if (ImPlot::BeginPlot("##Pie2", ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(NULL, NULL, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, 1, 0, 1); - ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, true, "%.0f", 180); + ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, "%.0f", 180, flags); ImPlot::EndPlot(); } ImPlot::PopColormap(); } -void ShowDemo_Heatmaps() { +//----------------------------------------------------------------------------- + +void Demo_Heatmaps() { static float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, @@ -649,6 +662,11 @@ void ShowDemo_Heatmaps() { ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); + + static ImPlotHeatmapFlags hm_flags = 0; + + ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::PushColormap(map); @@ -657,7 +675,7 @@ void ShowDemo_Heatmaps() { ImPlot::SetupAxes(NULL, NULL, axes_flags, axes_flags); ImPlot::SetupAxisTicks(ImAxis_X1,0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); ImPlot::SetupAxisTicks(ImAxis_Y1,1 - 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); - ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); + ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max,"%g",ImPlotPoint(0,0),ImPlotPoint(1,1),hm_flags); ImPlot::EndPlot(); } ImGui::SameLine(); @@ -682,37 +700,30 @@ void ShowDemo_Heatmaps() { } -void ShowDemo_Histogram() { +//----------------------------------------------------------------------------- + +void Demo_Histogram() { + static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; static int bins = 50; - static bool cumulative = false; - static bool density = true; - static bool outliers = true; static double mu = 5; static double sigma = 2; - ImGui::SetNextItemWidth(200); if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); - if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; + if (ImGui::RadioButton("N Bins",bins>=0)) { bins = 50; } if (bins>=0) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::SliderInt("##Bins", &bins, 1, 100); } - if (ImGui::Checkbox("Density", &density)) - { - ImPlot::SetNextAxisToFit(ImAxis_X1); - ImPlot::SetNextAxisToFit(ImAxis_Y1); - } + ImGui::CheckboxFlags("Horizontal", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Horizontal); ImGui::SameLine(); - if (ImGui::Checkbox("Cumulative", &cumulative)) - { - ImPlot::SetNextAxisToFit(ImAxis_X1); - ImPlot::SetNextAxisToFit(ImAxis_Y1); - } + ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); ImGui::SameLine(); + ImGui::CheckboxFlags("Cumulative", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Cumulative); + static bool range = false; ImGui::Checkbox("Range", &range); static float rmin = -3; @@ -722,18 +733,17 @@ void ShowDemo_Histogram() { ImGui::SetNextItemWidth(200); ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); ImGui::SameLine(); - ImGui::Checkbox("Outliers",&outliers); + ImGui::CheckboxFlags("Exclude Outliers", (unsigned int*)&hist_flags, ImPlotHistogramFlags_NoOutliers); } - static NormalDistribution<10000> dist(mu, sigma); static double x[100]; static double y[100]; - if (density) { + if (hist_flags & ImPlotHistogramFlags_Density) { for (int i = 0; i < 100; ++i) { x[i] = -3 + 16 * (double)i/99.0; y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); } - if (cumulative) { + if (hist_flags & ImPlotHistogramFlags_Cumulative) { for (int i = 1; i < 100; ++i) y[i] += y[i-1]; for (int i = 0; i < 100; ++i) @@ -742,22 +752,32 @@ void ShowDemo_Histogram() { } if (ImPlot::BeginPlot("##Histograms")) { - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, cumulative, density, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), outliers); - if (density && outliers) - ImPlot::PlotLine("Theoretical",x,y,100); + ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit); + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); + ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, 1.0, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), hist_flags); + if ((hist_flags & ImPlotHistogramFlags_Density) && !(hist_flags & ImPlotHistogramFlags_NoOutliers)) { + if (hist_flags & ImPlotHistogramFlags_Horizontal) + ImPlot::PlotLine("Theoretical",y,x,100); + else + ImPlot::PlotLine("Theoretical",x,y,100); + } ImPlot::EndPlot(); } } -void ShowDemo_Histogram2D() { +//----------------------------------------------------------------------------- + +void Demo_Histogram2D() { static int count = 50000; static int xybins[2] = {100,100}; - static bool density2 = false; + + static ImPlotHistogramFlags hist_flags = 0; + ImGui::SliderInt("Count",&count,100,100000); ImGui::SliderInt2("Bins",xybins,1,500); ImGui::SameLine(); - ImGui::Checkbox("Density##2",&density2); + ImGui::CheckboxFlags("Density", (unsigned int*)&hist_flags, ImPlotHistogramFlags_Density); + static NormalDistribution<100000> dist1(1, 2); static NormalDistribution<100000> dist2(1, 1); double max_count = 0; @@ -766,15 +786,17 @@ void ShowDemo_Histogram2D() { if (ImPlot::BeginPlot("##Hist2D",ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0))) { ImPlot::SetupAxes(NULL, NULL, flags, flags); ImPlot::SetupAxesLimits(-6,6,-6,6); - max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotRect(-6,6,-6,6)); + max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],ImPlotRect(-6,6,-6,6), hist_flags); ImPlot::EndPlot(); } ImGui::SameLine(); - ImPlot::ColormapScale(density2 ? "Density" : "Count",0,max_count,ImVec2(100,0)); + ImPlot::ColormapScale(hist_flags & ImPlotHistogramFlags_Density ? "Density" : "Count",0,max_count,ImVec2(100,0)); ImPlot::PopColormap(); } -void ShowDemo_DigitalPlots() { +//----------------------------------------------------------------------------- + +void Demo_DigitalPlots() { ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); ImGui::Indent(); ImGui::Text("you can drag analog plots over the rising/falling digital edge."); @@ -812,21 +834,23 @@ void ShowDemo_DigitalPlots() { for (int i = 0; i < 2; ++i) { if (showDigital[i] && dataDigital[i].Data.size() > 0) { sprintf(label, "digital_%d", i); - ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); + ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), 0, dataDigital[i].Offset, 2 * sizeof(float)); } } for (int i = 0; i < 2; ++i) { if (showAnalog[i]) { sprintf(label, "analog_%d", i); if (dataAnalog[i].Data.size() > 0) - ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); + ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), 0, dataAnalog[i].Offset, 2 * sizeof(float)); } } ImPlot::EndPlot(); } } -void ShowDemo_Images() { +//----------------------------------------------------------------------------- + +void Demo_Images() { ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); ImGui::BulletText("See ImGui Wiki page 'Image Loading and Displaying Examples'."); @@ -846,7 +870,9 @@ void ShowDemo_Images() { } } -void ShowDemo_RealtimePlots() { +//----------------------------------------------------------------------------- + +void Demo_RealtimePlots() { ImGui::BulletText("Move your mouse to change the data!"); ImGui::BulletText("This example assumes 60 FPS. Higher FPS requires larger buffer size."); static ScrollingBuffer sdata1, sdata2; @@ -871,21 +897,23 @@ void ShowDemo_RealtimePlots() { ImPlot::SetupAxisLimits(ImAxis_X1,t - history, t, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL,0.5f); - ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, sdata1.Offset, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), sdata2.Offset, 2*sizeof(float)); + ImPlot::PlotShaded("Mouse X", &sdata1.Data[0].x, &sdata1.Data[0].y, sdata1.Data.size(), -INFINITY, 0, sdata1.Offset, 2 * sizeof(float)); + ImPlot::PlotLine("Mouse Y", &sdata2.Data[0].x, &sdata2.Data[0].y, sdata2.Data.size(), 0, sdata2.Offset, 2*sizeof(float)); ImPlot::EndPlot(); } if (ImPlot::BeginPlot("##Rolling", ImVec2(-1,150))) { ImPlot::SetupAxes(NULL, NULL, flags, flags); ImPlot::SetupAxisLimits(ImAxis_X1,0,history, ImGuiCond_Always); ImPlot::SetupAxisLimits(ImAxis_Y1,0,1); - ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 2 * sizeof(float)); - ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 2 * sizeof(float)); + ImPlot::PlotLine("Mouse X", &rdata1.Data[0].x, &rdata1.Data[0].y, rdata1.Data.size(), 0, 0, 2 * sizeof(float)); + ImPlot::PlotLine("Mouse Y", &rdata2.Data[0].x, &rdata2.Data[0].y, rdata2.Data.size(), 0, 0, 2 * sizeof(float)); ImPlot::EndPlot(); } } -void ShowDemo_MarkersAndText() { +//----------------------------------------------------------------------------- + +void Demo_MarkersAndText() { static float mk_size = ImPlot::GetStyle().MarkerSize; static float mk_weight = ImPlot::GetStyle().MarkerWeight; ImGui::DragFloat("Marker Size",&mk_size,0.1f,2.0f,10.0f,"%.2f px"); @@ -921,14 +949,41 @@ void ShowDemo_MarkersAndText() { ImPlot::PlotText("Open Markers", 7.5f, 6.0f); ImPlot::PushStyleColor(ImPlotCol_InlayText, ImVec4(1,0,1,1)); - ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, true); + ImPlot::PlotText("Vertical Text", 5.0f, 6.0f, ImVec2(0,0), ImPlotTextFlags_Vertical); ImPlot::PopStyleColor(); ImPlot::EndPlot(); } } -void ShowDemo_LogAxes() { +//----------------------------------------------------------------------------- + +void Demo_NaNValues() { + + static bool include_nan = true; + static ImPlotLineFlags flags = 0; + + float data1[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; + float data2[5] = {0.0f,0.25f,0.5f,0.75f,1.0f}; + + if (include_nan) + data1[2] = NAN; + + ImGui::Checkbox("Include NaN",&include_nan); + ImGui::SameLine(); + ImGui::CheckboxFlags("Skip NaN", (unsigned int*)&flags, ImPlotLineFlags_SkipNaN); + + if (ImPlot::BeginPlot("##NaNValues")) { + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); + ImPlot::PlotLine("line", data1, data2, 5, flags); + ImPlot::PlotBars("bars", data1, 5); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void Demo_LogScale() { static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = i*0.1f; @@ -939,7 +994,7 @@ void ShowDemo_LogAxes() { ImGui::BulletText("Open the plot context menu (right click) to change scales."); if (ImPlot::BeginPlot("Log Plot", ImVec2(-1,0))) { - ImPlot::SetupAxis(ImAxis_X1, NULL, ImPlotAxisFlags_LogScale); + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); ImPlot::SetupAxesLimits(0.1, 100, 0, 10); ImPlot::PlotLine("f(x) = x", xs, xs, 1001); ImPlot::PlotLine("f(x) = sin(x)+1", xs, ys1, 1001); @@ -949,7 +1004,26 @@ void ShowDemo_LogAxes() { } } -void ShowDemo_TimeAxes() { +//----------------------------------------------------------------------------- + +void Demo_SymmetricLogScale() { + static double xs[1001], ys1[1001], ys2[1001]; + for (int i = 0; i < 1001; ++i) { + xs[i] = i*0.1f-50; + ys1[i] = sin(xs[i]); + ys2[i] = i*0.002 - 1; + } + if (ImPlot::BeginPlot("SymLog Plot", ImVec2(-1,0))) { + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_SymLog); + ImPlot::PlotLine("f(x) = a*x+b",xs,ys2,1001); + ImPlot::PlotLine("f(x) = sin(x)",xs,ys1,1001); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void Demo_TimeScale() { static double t_min = 1609459200; // 01/01/2021 @ 12:00:00am (UTC) static double t_max = 1640995200; // 01/01/2022 @ 12:00:00am (UTC) @@ -974,7 +1048,7 @@ void ShowDemo_TimeAxes() { } if (ImPlot::BeginPlot("##Time", ImVec2(-1,0))) { - ImPlot::SetupAxis(ImAxis_X1, NULL, ImPlotAxisFlags_Time); + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); ImPlot::SetupAxesLimits(t_min,t_max,0,1); if (data != NULL) { // downsample our data @@ -985,7 +1059,7 @@ void ShowDemo_TimeAxes() { end = end < 0 ? 0 : end > HugeTimeData::Size - 1 ? HugeTimeData::Size - 1 : end; int size = (end - start)/downsample; // plot it - ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, sizeof(double)*downsample); + ImPlot::PlotLine("Time Series", &data->Ts[start], &data->Ys[start], size, 0, 0, sizeof(double)*downsample); } // plot time now double t_now = (double)time(0); @@ -996,7 +1070,34 @@ void ShowDemo_TimeAxes() { } } -void ShowDemo_MultipleAxes() { +//----------------------------------------------------------------------------- + +static inline double TransformForward_Sqrt(double v, void*) { + return sqrt(v); +} + +static inline double TransformInverse_Sqrt(double v, void*) { + return v*v; +} + +void Demo_CustomScale() { + static float v[100]; + for (int i = 0; i < 100; ++i) { + v[i] = i*0.01f; + } + if (ImPlot::BeginPlot("Sqrt")) { + ImPlot::SetupAxis(ImAxis_X1, "Linear"); + ImPlot::SetupAxis(ImAxis_Y1, "Sqrt"); + ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Sqrt, TransformInverse_Sqrt); + ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, 0, INFINITY); + ImPlot::PlotLine("##data",v,v,100); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void Demo_MultipleAxes() { static float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { xs[i] = (i*0.1f); @@ -1052,7 +1153,9 @@ void ShowDemo_MultipleAxes() { } } -void ShowDemo_LinkedAxes() { +//----------------------------------------------------------------------------- + +void Demo_LinkedAxes() { static ImPlotRect lims(0,1,0,1); static bool linkx = true, linky = true; int data[2] = {0,1}; @@ -1079,7 +1182,28 @@ void ShowDemo_LinkedAxes() { } } -void ShowDemo_EqualAxes() { +//----------------------------------------------------------------------------- + +void Demo_AxisConstraints() { + static float constraints[4] = {-10,10,1,20}; + static ImPlotAxisFlags flags; + ImGui::DragFloat2("Limits Constraints", &constraints[0], 0.01); + ImGui::DragFloat2("Zoom Constraints", &constraints[2], 0.01); + CHECKBOX_FLAG(flags, ImPlotAxisFlags_PanStretch); + if (ImPlot::BeginPlot("##AxisConstraints",ImVec2(-1,0))) { + ImPlot::SetupAxes("X","Y",flags,flags); + ImPlot::SetupAxesLimits(-1,1,-1,1); + ImPlot::SetupAxisLimitsConstraints(ImAxis_X1,constraints[0], constraints[1]); + ImPlot::SetupAxisZoomConstraints(ImAxis_X1,constraints[2], constraints[3]); + ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1,constraints[0], constraints[1]); + ImPlot::SetupAxisZoomConstraints(ImAxis_Y1,constraints[2], constraints[3]); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + +void Demo_EqualAxes() { ImGui::BulletText("Equal constraint applies to axis pairs (e.g ImAxis_X1/Y1, ImAxis_X2/Y2"); static double xs1[360], ys1[360]; for (int i = 0; i < 360; ++i) { @@ -1098,7 +1222,9 @@ void ShowDemo_EqualAxes() { } } -void ShowDemo_AutoFittingData() { +//----------------------------------------------------------------------------- + +void Demo_AutoFittingData() { ImGui::BulletText("The Y-axis has been configured to auto-fit to only the data visible in X-axis range."); ImGui::BulletText("Zoom and pan the X-axis. Disable Stems to see a difference in fit."); ImGui::BulletText("If ImPlotAxisFlags_RangeFit is disabled, the axis will fit ALL data."); @@ -1127,12 +1253,14 @@ void ShowDemo_AutoFittingData() { }; } -ImPlotPoint SinewaveGetter(void* data, int i) { +//----------------------------------------------------------------------------- + +ImPlotPoint SinewaveGetter(int i, void* data) { float f = *(float*)data; return ImPlotPoint(i,sinf(f*i)); } -void ShowDemo_SubplotsSizing() { +void Demo_SubplotsSizing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_None; ImGui::CheckboxFlags("ImPlotSubplotFlags_NoResize", (unsigned int*)&flags, ImPlotSubplotFlags_NoResize); @@ -1160,7 +1288,9 @@ void ShowDemo_SubplotsSizing() { } } -void ShowDemo_SubplotItemSharing() { +//----------------------------------------------------------------------------- + +void Demo_SubplotItemSharing() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_ShareItems; ImGui::CheckboxFlags("ImPlotSubplotFlags_ShareItems", (unsigned int*)&flags, ImPlotSubplotFlags_ShareItems); ImGui::CheckboxFlags("ImPlotSubplotFlags_ColMajor", (unsigned int*)&flags, ImPlotSubplotFlags_ColMajor); @@ -1201,7 +1331,9 @@ void ShowDemo_SubplotItemSharing() { } } -void ShowDemo_SubplotAxisLinking() { +//----------------------------------------------------------------------------- + +void Demo_SubplotAxisLinking() { static ImPlotSubplotFlags flags = ImPlotSubplotFlags_LinkRows | ImPlotSubplotFlags_LinkCols; ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkRows", (unsigned int*)&flags, ImPlotSubplotFlags_LinkRows); ImGui::CheckboxFlags("ImPlotSubplotFlags_LinkCols", (unsigned int*)&flags, ImPlotSubplotFlags_LinkCols); @@ -1223,8 +1355,9 @@ void ShowDemo_SubplotAxisLinking() { } } +//----------------------------------------------------------------------------- -void ShowDemo_LegendOptions() { +void Demo_LegendOptions() { static ImPlotLocation loc = ImPlotLocation_East; static bool h = false; static bool o = true; ImGui::CheckboxFlags("North", (unsigned int*)&loc, ImPlotLocation_North); ImGui::SameLine(); @@ -1255,7 +1388,9 @@ void ShowDemo_LegendOptions() { } } -void ShowDemo_DragPoints() { +//----------------------------------------------------------------------------- + +void Demo_DragPoints() { ImGui::BulletText("Click and drag each point."); static ImPlotDragToolFlags flags = ImPlotDragToolFlags_None; ImGui::CheckboxFlags("NoCursors", (unsigned int*)&flags, ImPlotDragToolFlags_NoCursors); ImGui::SameLine(); @@ -1285,17 +1420,19 @@ void ShowDemo_DragPoints() { ImPlot::SetNextLineStyle(ImVec4(1,0.5f,1,1)); - ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::PlotLine("##h1",&P[0].x, &P[0].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.5f,1,1)); - ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, sizeof(ImPlotPoint)); + ImPlot::PlotLine("##h2",&P[2].x, &P[2].y, 2, 0, 0, sizeof(ImPlotPoint)); ImPlot::SetNextLineStyle(ImVec4(0,0.9f,0,1), 2); - ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, sizeof(ImPlotPoint)); + ImPlot::PlotLine("##bez",&B[0].x, &B[0].y, 100, 0, 0, sizeof(ImPlotPoint)); ImPlot::EndPlot(); } } -void ShowDemo_DragLines() { +//----------------------------------------------------------------------------- + +void Demo_DragLines() { ImGui::BulletText("Click and drag the horizontal and vertical lines."); static double x1 = 0.2; static double x2 = 0.8; @@ -1323,7 +1460,9 @@ void ShowDemo_DragLines() { } } -void ShowDemo_DragRects() { +//----------------------------------------------------------------------------- + +void Demo_DragRects() { static float x_data[512]; static float y_data1[512]; @@ -1365,6 +1504,8 @@ void ShowDemo_DragRects() { } } +//----------------------------------------------------------------------------- + ImPlotPoint FindCentroid(const ImVector& data, ImPlotRect& bounds, int& cnt) { cnt = 0; ImPlotPoint avg; @@ -1382,7 +1523,9 @@ ImPlotPoint FindCentroid(const ImVector& data, ImPlotRect& bounds, return avg; } -void ShowDemo_Querying() { +//----------------------------------------------------------------------------- + +void Demo_Querying() { static ImVector data; static ImVector rects; static ImPlotRect limits, select; @@ -1409,7 +1552,7 @@ void ShowDemo_Querying() { ImPlotPoint pt = ImPlot::GetPlotMousePos(); data.push_back(pt); } - ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 2 * sizeof(double)); + ImPlot::PlotScatter("Points", &data[0].x, &data[0].y, data.size(), 0, 0, 2 * sizeof(double)); if (ImPlot::IsPlotSelected()) { select = ImPlot::GetPlotSelection(); int cnt; @@ -1437,7 +1580,9 @@ void ShowDemo_Querying() { } } -void ShowDemo_Annotations() { +//----------------------------------------------------------------------------- + +void Demo_Annotations() { static bool clamp = false; ImGui::Checkbox("Clamp",&clamp); if (ImPlot::BeginPlot("##Annotations")) { @@ -1462,7 +1607,9 @@ void ShowDemo_Annotations() { } } -void ShowDemo_Tags() { +//----------------------------------------------------------------------------- + +void Demo_Tags() { static bool show = true; ImGui::Checkbox("Show Tags",&show); if (ImPlot::BeginPlot("##Tags")) { @@ -1482,7 +1629,9 @@ void ShowDemo_Tags() { } } -void ShowDemo_DragAndDrop() { +//----------------------------------------------------------------------------- + +void Demo_DragAndDrop() { ImGui::BulletText("Drag/drop items from the left column."); ImGui::BulletText("Drag/drop items between plots."); ImGui::Indent(); @@ -1562,7 +1711,7 @@ void ShowDemo_DragAndDrop() { if (dnd[k].Plt == 1 && dnd[k].Data.size() > 0) { ImPlot::SetAxis(dnd[k].Yax); ImPlot::SetNextLineStyle(dnd[k].Color); - ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 2 * sizeof(float)); + ImPlot::PlotLine(dnd[k].Label, &dnd[k].Data[0].x, &dnd[k].Data[0].y, dnd[k].Data.size(), 0, 0, 2 * sizeof(float)); // allow legend item labels to be DND sources if (ImPlot::BeginDragDropSourceItem(dnd[k].Label)) { ImGui::SetDragDropPayload("MY_DND", &k, sizeof(int)); @@ -1607,7 +1756,7 @@ void ShowDemo_DragAndDrop() { if (dndx != NULL && dndy != NULL) { ImVec4 mixed((dndx->Color.x + dndy->Color.x)/2,(dndx->Color.y + dndy->Color.y)/2,(dndx->Color.z + dndy->Color.z)/2,(dndx->Color.w + dndy->Color.w)/2); ImPlot::SetNextLineStyle(mixed); - ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 2 * sizeof(float)); + ImPlot::PlotLine("##dndxy", &dndx->Data[0].y, &dndy->Data[0].y, dndx->Data.size(), 0, 0, 2 * sizeof(float)); } // allow the x-axis to be a DND target if (ImPlot::BeginDragDropTargetAxis(ImAxis_X1)) { @@ -1653,7 +1802,9 @@ void ShowDemo_DragAndDrop() { ImGui::EndChild(); } -void ShowDemo_Tables() { +//----------------------------------------------------------------------------- + +void Demo_Tables() { #ifdef IMGUI_HAS_TABLE static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable; @@ -1692,7 +1843,9 @@ void ShowDemo_Tables() { #endif } -void ShowDemo_OffsetAndStride() { +//----------------------------------------------------------------------------- + +void Demo_OffsetAndStride() { static const int k_circles = 11; static const int k_points_per = 50; static const int k_size = 2 * k_points_per * k_circles; @@ -1717,7 +1870,7 @@ void ShowDemo_OffsetAndStride() { char buff[16]; for (int c = 0; c < k_circles; ++c) { sprintf(buff, "Circle %d", c); - ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, offset, 2*k_circles*sizeof(double)); + ImPlot::PlotLine(buff, &interleaved_data[c*2 + 0], &interleaved_data[c*2 + 1], k_points_per, 0, offset, 2*k_circles*sizeof(double)); } ImPlot::EndPlot(); ImPlot::PopColormap(); @@ -1725,7 +1878,9 @@ void ShowDemo_OffsetAndStride() { // offset++; uncomment for animation! } -void ShowDemo_CustomDataAndGetters() { +//----------------------------------------------------------------------------- + +void Demo_CustomDataAndGetters() { ImGui::BulletText("You can plot custom structs using the stride feature."); ImGui::BulletText("Most plotters can also be passed a function pointer for getting data."); ImGui::Indent(); @@ -1738,7 +1893,7 @@ void ShowDemo_CustomDataAndGetters() { if (ImPlot::BeginPlot("##Custom Data")) { // custom structs using stride example: - ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); + ImPlot::PlotLine("Vector2f", &vec2_data[0].x, &vec2_data[0].y, 2, 0, 0, sizeof(MyImPlot::Vector2f) /* or sizeof(float) * 2 */); // custom getter example 1: ImPlot::PlotLineG("Spiral", MyImPlot::Spiral, NULL, 1000); @@ -1760,24 +1915,24 @@ void ShowDemo_CustomDataAndGetters() { } } -void MetricFormatter(double value, char* buff, int size, void* data) { +//----------------------------------------------------------------------------- + +int MetricFormatter(double value, char* buff, int size, void* data) { const char* unit = (const char*)data; static double v[] = {1000000000,1000000,1000,1,0.001,0.000001,0.000000001}; static const char* p[] = {"G","M","k","","m","u","n"}; if (value == 0) { - snprintf(buff,size,"0 %s", unit); - return; + return snprintf(buff,size,"0 %s", unit); } for (int i = 0; i < 7; ++i) { if (fabs(value) >= v[i]) { - snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); - return; + return snprintf(buff,size,"%g %s%s",value/v[i],p[i],unit); } } - snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); + return snprintf(buff,size,"%g %s%s",value/v[6],p[6],unit); } -void ShowDemo_TickLabels() { +void Demo_TickLabels() { static bool custom_fmt = true; static bool custom_ticks = false; static bool custom_labels = true; @@ -1794,7 +1949,6 @@ void ShowDemo_TickLabels() { static const char* ylabels[] = {"One","Three","Seven","Nine"}; static double yticks_aux[] = {0.2,0.4,0.6}; static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; - if (ImPlot::BeginPlot("##Ticks")) { ImPlot::SetupAxesLimits(2.5,5,0,1000); ImPlot::SetupAxis(ImAxis_Y2, NULL, ImPlotAxisFlags_AuxDefault); @@ -1815,7 +1969,9 @@ void ShowDemo_TickLabels() { } } -void ShowDemo_CustomStyles() { +//----------------------------------------------------------------------------- + +void Demo_CustomStyles() { ImPlot::PushColormap(ImPlotColormap_Deep); // normally you wouldn't change the entire style each frame ImPlotStyle backup = ImPlot::GetStyle(); @@ -1836,7 +1992,9 @@ void ShowDemo_CustomStyles() { ImPlot::PopColormap(); } -void ShowDemo_CustomRendering() { +//----------------------------------------------------------------------------- + +void Demo_CustomRendering() { if (ImPlot::BeginPlot("##CustomRend")) { ImVec2 cntr = ImPlot::PlotToPixels(ImPlotPoint(0.5f, 0.5f)); ImVec2 rmin = ImPlot::PlotToPixels(ImPlotPoint(0.25f, 0.75f)); @@ -1849,7 +2007,9 @@ void ShowDemo_CustomRendering() { } } -void ShowDemo_LegendPopups() { +//----------------------------------------------------------------------------- + +void Demo_LegendPopups() { ImGui::BulletText("You can implement legend context menus to inject per-item controls and widgets."); ImGui::BulletText("Right click the legend label/icon to edit custom item attributes."); @@ -1875,7 +2035,7 @@ void ShowDemo_LegendPopups() { ImPlot::PlotBars("Right Click Me", vals, 101); } else { - if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); + if (markers) ImPlot::SetNextMarkerStyle(ImPlotMarker_Square); ImPlot::SetNextLineStyle(color, thickness); ImPlot::PlotLine("Right Click Me", vals, 101); if (shaded) ImPlot::PlotShaded("Right Click Me",vals,101); @@ -1900,7 +2060,9 @@ void ShowDemo_LegendPopups() { } } -void ShowDemo_ColormapTools() { +//----------------------------------------------------------------------------- + +void Demo_ColormapTools() { static int cmap = 0; if (ImPlot::ColormapButton("Colormap Button",ImVec2(0,0),cmap)) { cmap = (cmap + 1) % ImPlot::GetColormapCount(); @@ -1909,7 +2071,9 @@ void ShowDemo_ColormapTools() { ImPlot::ColormapScale("Colormap Scale",0,1,ImVec2(0,0),cmap); } -void ShowDemo_CustomPlottersAndTooltips() { +//----------------------------------------------------------------------------- + +void Demo_CustomPlottersAndTooltips() { ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h."); double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600}; double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4}; @@ -1926,8 +2090,11 @@ void ShowDemo_CustomPlottersAndTooltips() { ImPlot::GetStyle().UseLocalTime = false; if (ImPlot::BeginPlot("Candlestick Chart",ImVec2(-1,0))) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_Time,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); + ImPlot::SetupAxes(NULL,NULL,0,ImPlotAxisFlags_AutoFit|ImPlotAxisFlags_RangeFit); ImPlot::SetupAxesLimits(1546300800, 1571961600, 1250, 1600); + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time); + ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, 1546300800, 1571961600); + ImPlot::SetupAxisZoomConstraints(ImAxis_X1, 60*60*24*14, 1571961600-1546300800); ImPlot::SetupAxisFormat(ImAxis_Y1, "$%.0f"); MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); ImPlot::EndPlot(); @@ -1938,46 +2105,51 @@ void ShowDemo_CustomPlottersAndTooltips() { // DEMO WINDOW //----------------------------------------------------------------------------- -void ShowDemoWindow(bool* p_open) { - static bool show_imgui_metrics = false; - static bool show_implot_metrics = false; - static bool show_imgui_style_editor = false; - static bool show_implot_style_editor = false; - static bool show_implot_benchmark = false; - if (show_imgui_metrics) { - ImGui::ShowMetricsWindow(&show_imgui_metrics); +void DemoHeader(const char* label, void(*demo)()) { + if (ImGui::TreeNodeEx(label)) { + demo(); + ImGui::TreePop(); } +} + +void ShowDemoWindow(bool* p_open) { + static bool show_implot_metrics = false; + static bool show_implot_style_editor = false; + static bool show_imgui_metrics = false; + static bool show_imgui_style_editor = false; + static bool show_imgui_demo = false; + if (show_implot_metrics) { ImPlot::ShowMetricsWindow(&show_implot_metrics); } - if (show_imgui_style_editor) { - ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); - ImGui::ShowStyleEditor(); - ImGui::End(); - } if (show_implot_style_editor) { ImGui::SetNextWindowSize(ImVec2(415,762), ImGuiCond_Appearing); ImGui::Begin("Style Editor (ImPlot)", &show_implot_style_editor); ImPlot::ShowStyleEditor(); ImGui::End(); } - if (show_implot_benchmark) { - ImGui::SetNextWindowSize(ImVec2(530,740), ImGuiCond_Appearing); - ImGui::Begin("ImPlot Benchmark Tool", &show_implot_benchmark); - ImPlot::ShowBenchmarkTool(); + if (show_imgui_style_editor) { + ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor); + ImGui::ShowStyleEditor(); ImGui::End(); - return; + } + if (show_imgui_metrics) { + ImGui::ShowMetricsWindow(&show_imgui_metrics); + } + if (show_imgui_demo) { + ImGui::ShowDemoWindow(&show_imgui_demo); } ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Tools")) { - ImGui::MenuItem("Metrics (ImGui)", NULL, &show_imgui_metrics); - ImGui::MenuItem("Metrics (ImPlot)", NULL, &show_implot_metrics); - ImGui::MenuItem("Style Editor (ImGui)", NULL, &show_imgui_style_editor); - ImGui::MenuItem("Style Editor (ImPlot)", NULL, &show_implot_style_editor); - ImGui::MenuItem("Benchmark", NULL, &show_implot_benchmark); + ImGui::MenuItem("Metrics", NULL, &show_implot_metrics); + ImGui::MenuItem("Style Editor", NULL, &show_implot_style_editor); + ImGui::Separator(); + ImGui::MenuItem("ImGui Metrics", NULL, &show_imgui_metrics); + ImGui::MenuItem("ImGui Style Editor", NULL, &show_imgui_style_editor); + ImGui::MenuItem("ImGui Demo", NULL, &show_imgui_demo); ImGui::EndMenu(); } ImGui::EndMenuBar(); @@ -1996,116 +2168,75 @@ void ShowDemoWindow(bool* p_open) { if (ImGui::BeginTabBar("ImPlotDemoTabs")) { if (ImGui::BeginTabItem("Plots")) { - if (ImGui::CollapsingHeader("Line Plots")) - ShowDemo_LinePlots(); - if (ImGui::CollapsingHeader("Filled Line Plots")) - ShowDemo_FilledLinePlots(); - if (ImGui::CollapsingHeader("Shaded Plots##")) - ShowDemo_ShadedPlots(); - if (ImGui::CollapsingHeader("Scatter Plots")) - ShowDemo_ScatterPlots(); - if (ImGui::CollapsingHeader("Realtime Plots")) - ShowDemo_RealtimePlots(); - if (ImGui::CollapsingHeader("Stairstep Plots")) - ShowDemo_StairstepPlots(); - if (ImGui::CollapsingHeader("Bar Plots")) - ShowDemo_BarPlots(); - if (ImGui::CollapsingHeader("Bar Groups")) - ShowDemo_BarGroups(); - if (ImGui::CollapsingHeader("Bar Stacks")) - ShowDemo_BarStacks(); - if (ImGui::CollapsingHeader("Error Bars")) - ShowDemo_ErrorBars(); - if (ImGui::CollapsingHeader("Stem Plots##")) - ShowDemo_StemPlots(); - if (ImGui::CollapsingHeader("Infinite Lines")) - ShowDemo_InfiniteLines(); - if (ImGui::CollapsingHeader("Pie Charts")) - ShowDemo_PieCharts(); - if (ImGui::CollapsingHeader("Heatmaps")) - ShowDemo_Heatmaps(); - if (ImGui::CollapsingHeader("Histogram")) - ShowDemo_Histogram(); - if (ImGui::CollapsingHeader("Histogram 2D")) - ShowDemo_Histogram2D(); - if (ImGui::CollapsingHeader("Digital Plots")) - ShowDemo_DigitalPlots(); - if (ImGui::CollapsingHeader("Images")) - ShowDemo_Images(); - if (ImGui::CollapsingHeader("Markers and Text")) - ShowDemo_MarkersAndText(); + DemoHeader("Line Plots", Demo_LinePlots); + DemoHeader("Filled Line Plots", Demo_FilledLinePlots); + DemoHeader("Shaded Plots##", Demo_ShadedPlots); + DemoHeader("Scatter Plots", Demo_ScatterPlots); + DemoHeader("Realtime Plots", Demo_RealtimePlots); + DemoHeader("Stairstep Plots", Demo_StairstepPlots); + DemoHeader("Bar Plots", Demo_BarPlots); + DemoHeader("Bar Groups", Demo_BarGroups); + DemoHeader("Bar Stacks", Demo_BarStacks); + DemoHeader("Error Bars", Demo_ErrorBars); + DemoHeader("Stem Plots##", Demo_StemPlots); + DemoHeader("Infinite Lines", Demo_InfiniteLines); + DemoHeader("Pie Charts", Demo_PieCharts); + DemoHeader("Heatmaps", Demo_Heatmaps); + DemoHeader("Histogram", Demo_Histogram); + DemoHeader("Histogram 2D", Demo_Histogram2D); + DemoHeader("Digital Plots", Demo_DigitalPlots); + DemoHeader("Images", Demo_Images); + DemoHeader("Markers and Text", Demo_MarkersAndText); + DemoHeader("NaN Values", Demo_NaNValues); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { - if (ImGui::CollapsingHeader("Sizing")) - ShowDemo_SubplotsSizing(); - if (ImGui::CollapsingHeader("Item Sharing")) - ShowDemo_SubplotItemSharing(); - if (ImGui::CollapsingHeader("Axis Linking")) - ShowDemo_SubplotAxisLinking(); - if (ImGui::CollapsingHeader("Tables")) - ShowDemo_Tables(); + DemoHeader("Sizing", Demo_SubplotsSizing); + DemoHeader("Item Sharing", Demo_SubplotItemSharing); + DemoHeader("Axis Linking", Demo_SubplotAxisLinking); + DemoHeader("Tables", Demo_Tables); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Axes")) { - if (ImGui::CollapsingHeader("Log Axes")) - ShowDemo_LogAxes(); - if (ImGui::CollapsingHeader("Time Axes")) - ShowDemo_TimeAxes(); - if (ImGui::CollapsingHeader("Multiple Axes")) - ShowDemo_MultipleAxes(); - if (ImGui::CollapsingHeader("Tick Labels")) - ShowDemo_TickLabels(); - if (ImGui::CollapsingHeader("Linked Axes")) - ShowDemo_LinkedAxes(); - if (ImGui::CollapsingHeader("Equal Axes")) - ShowDemo_EqualAxes(); - if (ImGui::CollapsingHeader("Auto-Fitting Data")) - ShowDemo_AutoFittingData(); + DemoHeader("Log Scale", Demo_LogScale); + DemoHeader("Symmetric Log Scale", Demo_SymmetricLogScale); + DemoHeader("Time Scale", Demo_TimeScale); + DemoHeader("Custom Scale", Demo_CustomScale); + DemoHeader("Multiple Axes", Demo_MultipleAxes); + DemoHeader("Tick Labels", Demo_TickLabels); + DemoHeader("Linked Axes", Demo_LinkedAxes); + DemoHeader("Axis Constraints", Demo_AxisConstraints); + DemoHeader("Equal Axes", Demo_EqualAxes); + DemoHeader("Auto-Fitting Data", Demo_AutoFittingData); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tools")) { - if (ImGui::CollapsingHeader("Offset and Stride")) - ShowDemo_OffsetAndStride(); - if (ImGui::CollapsingHeader("Drag Points")) - ShowDemo_DragPoints(); - if (ImGui::CollapsingHeader("Drag Lines")) - ShowDemo_DragLines(); - if (ImGui::CollapsingHeader("Drag Rects")) - ShowDemo_DragRects(); - if (ImGui::CollapsingHeader("Querying")) - ShowDemo_Querying(); - if (ImGui::CollapsingHeader("Annotations")) - ShowDemo_Annotations(); - if (ImGui::CollapsingHeader("Tags")) - ShowDemo_Tags(); - if (ImGui::CollapsingHeader("Drag and Drop")) - ShowDemo_DragAndDrop(); - if (ImGui::CollapsingHeader("Legend Options")) - ShowDemo_LegendOptions(); - if (ImGui::CollapsingHeader("Legend Popups")) - ShowDemo_LegendPopups(); - if (ImGui::CollapsingHeader("Colormap Tools")) - ShowDemo_ColormapTools(); + DemoHeader("Offset and Stride", Demo_OffsetAndStride); + DemoHeader("Drag Points", Demo_DragPoints); + DemoHeader("Drag Lines", Demo_DragLines); + DemoHeader("Drag Rects", Demo_DragRects); + DemoHeader("Querying", Demo_Querying); + DemoHeader("Annotations", Demo_Annotations); + DemoHeader("Tags", Demo_Tags); + DemoHeader("Drag and Drop", Demo_DragAndDrop); + DemoHeader("Legend Options", Demo_LegendOptions); + DemoHeader("Legend Popups", Demo_LegendPopups); + DemoHeader("Colormap Tools", Demo_ColormapTools); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Custom")) { - if (ImGui::CollapsingHeader("Custom Styles")) - ShowDemo_CustomStyles(); - if (ImGui::CollapsingHeader("Custom Data and Getters")) - ShowDemo_CustomDataAndGetters(); - if (ImGui::CollapsingHeader("Custom Rendering")) - ShowDemo_CustomRendering(); - if (ImGui::CollapsingHeader("Custom Plotters and Tooltips")) - ShowDemo_CustomPlottersAndTooltips(); + DemoHeader("Custom Styles", Demo_CustomStyles); + DemoHeader("Custom Data and Getters", Demo_CustomDataAndGetters); + DemoHeader("Custom Rendering", Demo_CustomRendering); + DemoHeader("Custom Plotters and Tooltips", Demo_CustomPlottersAndTooltips); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Config")) { - ShowDemo_Config(); + Demo_Config(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Help")) { - ShowDemo_Help(); + Demo_Help(); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -2117,19 +2248,19 @@ void ShowDemoWindow(bool* p_open) { namespace MyImPlot { -ImPlotPoint SineWave(void* data , int idx) { +ImPlotPoint SineWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * sin(2 * 3.14 * wd->Freq * x)); } -ImPlotPoint SawWave(void* data, int idx) { +ImPlotPoint SawWave(int idx, void* data) { WaveData* wd = (WaveData*)data; double x = idx * wd->X; return ImPlotPoint(x, wd->Offset + wd->Amp * (-2 / 3.14 * atan(cos(3.14 * wd->Freq * x) / sin(3.14 * wd->Freq * x)))); } -ImPlotPoint Spiral(void*, int idx) { +ImPlotPoint Spiral(int idx, void*) { float r = 0.9f; // outer radius float a = 0; // inner radius float b = 0.05f; // increment per rev @@ -2146,9 +2277,9 @@ void Sparkline(const char* id, const float* values, int count, float min_v, floa ImPlot::SetupAxes(0,0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always); ImPlot::PushStyleColor(ImPlotCol_Line, col); - ImPlot::PlotLine(id, values, count, 1, 0, offset); + ImPlot::PlotLine(id, values, count, 1, 0, 0, offset); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::PlotShaded(id, values, count, 0, 1, 0, offset); + ImPlot::PlotShaded(id, values, count, 0, 1, 0, 0, offset); ImPlot::PopStyleVar(); ImPlot::PopStyleColor(); ImPlot::EndPlot(); @@ -2293,165 +2424,4 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens } } -} // namespace MyImplot - -namespace ImPlot { - -//----------------------------------------------------------------------------- -// BENCHMARK -//----------------------------------------------------------------------------- - -struct BenchData { - BenchData() { - float y = RandomRange(0.0f,1.0f); - Data = new float[1000]; - for (int i = 0; i < 1000; ++i) { - Data[i] = y + RandomRange(-0.01f,0.01f); - } - Col = ImVec4(RandomRange(0.0f,1.0f),RandomRange(0.0f,1.0f),RandomRange(0.0f,1.0f),0.5f); - } - ~BenchData() { delete[] Data; } - float* Data; - ImVec4 Col; -}; - -enum BenchMode { - Line = 0, - LineG = 1, - Shaded = 2, - Scatter = 3, - Bars = 4 -}; - -struct BenchRecord { - int Mode; - bool AA; - ImVector Data; -}; - -ImPlotPoint BenchmarkGetter(void* data, int idx) { - float* values = (float*)data; - return ImPlotPoint(idx, values[idx]); -} - -void ShowBenchmarkTool() { - static const int max_items = 500; - static BenchData items[max_items]; - static bool running = false; - static int frames = 60; - static int L = 0; - static int F = 0; - static double t1, t2; - static int mode = BenchMode::Line; - const char* names[] = {"Line","LineG","Shaded","Scatter","Bars"}; - - static ImVector records; - - if (running) { - F++; - if (F == frames) { - t2 = ImGui::GetTime(); - records.back().Data.push_back(ImPlotPoint(L, frames / (t2 - t1))); - L += 5; - F = 0; - t1 = ImGui::GetTime(); - } - if (L > max_items) { - running = false; - L = max_items; - } - } - - ImGui::Text("ImDrawIdx: %d-bit", (int)(sizeof(ImDrawIdx) * 8)); - ImGui::Text("ImGuiBackendFlags_RendererHasVtxOffset: %s", (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ? "True" : "False"); - ImGui::Text("%.2f FPS", ImGui::GetIO().Framerate); - - ImGui::Separator(); - - bool was_running = running; - if (was_running) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); - } - if (ImGui::Button("Benchmark")) { - running = true; - L = F = 0; - records.push_back(BenchRecord()); - records.back().Data.reserve(max_items+1); - records.back().Mode = mode; - records.back().AA = ImPlot::GetStyle().AntiAliasedLines; - t1 = ImGui::GetTime(); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(200); - ImGui::Combo("##Mode",&mode,names,4); - ImGui::SameLine(); - - ImGui::Checkbox("Anti-Aliased Lines", &ImPlot::GetStyle().AntiAliasedLines); - if (was_running) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); } - - ImGui::ProgressBar((float)L / (float)(max_items - 1)); - - if (ImPlot::BeginPlot("##Bench",ImVec2(-1,0),ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) { - ImPlot::SetupAxes(NULL,NULL,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations); - ImPlot::SetupAxesLimits(0,1000,0,1,ImGuiCond_Always); - if (running) { - if (mode == BenchMode::Line) { - for (int i = 0; i < L; ++i) { - ImGui::PushID(i); - ImPlot::SetNextLineStyle(items[i].Col); - ImPlot::PlotLine("##item", items[i].Data, 1000); - ImGui::PopID(); - } - } - else if (mode == BenchMode::LineG) { - for (int i = 0; i < L; ++i) { - ImGui::PushID(i); - ImPlot::SetNextLineStyle(items[i].Col); - ImPlot::PlotLineG("##item",BenchmarkGetter,items[i].Data,1000); - ImGui::PopID(); - } - } - else if (mode == BenchMode::Shaded) { - for (int i = 0; i < L; ++i) { - ImGui::PushID(i); - ImPlot::SetNextFillStyle(items[i].Col,0.5f); - ImPlot::PlotShaded("##item", items[i].Data, 1000); - ImGui::PopID(); - } - } - else if (mode == BenchMode::Scatter) { - for (int i = 0; i < L; ++i) { - ImGui::PushID(i); - ImPlot::SetNextLineStyle(items[i].Col); - ImPlot::PlotScatter("##item", items[i].Data, 1000); - ImGui::PopID(); - } - } - else if (mode == BenchMode::Bars) { - for (int i = 0; i < L; ++i) { - ImGui::PushID(i); - ImPlot::SetNextFillStyle(items[i].Col,0.5f); - ImPlot::PlotBars("##item", items[i].Data, 1000); - ImGui::PopID(); - } - } - } - ImPlot::EndPlot(); - } - static char buffer[64]; - if (ImPlot::BeginPlot("##Stats", ImVec2(-1,0), ImPlotFlags_NoChild)) { - ImPlot::SetupAxes("Items (1,000 pts each)", "Framerate (Hz)"); - ImPlot::SetupAxesLimits(0,500,0,500,ImGuiCond_Always); - for (int run = 0; run < records.size(); ++run) { - if (records[run].Data.Size > 1) { - sprintf(buffer, "B%d-%s%s", run + 1, names[records[run].Mode], records[run].AA ? "-AA" : ""); - ImVector& d = records[run].Data; - ImPlot::PlotLine(buffer, &d[0].x, &d[0].y, d.Size, 0, 2*sizeof(double)); - } - } - ImPlot::EndPlot(); - } -} - -} +} // namespace MyImplot \ No newline at end of file diff --git a/implot_internal.h b/implot_internal.h index e1f1acf..8c73d4a 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2021 Evan Pezent +// Copyright (c) 2022 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.13 WIP +// ImPlot v0.14 // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! @@ -63,8 +63,6 @@ #define IMPLOT_LABEL_FORMAT "%g" // Max character size for tick labels #define IMPLOT_LABEL_MAX_SIZE 32 -// Plot values less than or equal to 0 will be replaced with this on log scale axes -#define IMPLOT_LOG_ZERO DBL_MIN //----------------------------------------------------------------------------- // [SECTION] Macros @@ -90,6 +88,7 @@ struct ImPlotItem; struct ImPlotLegend; struct ImPlotPlot; struct ImPlotNextPlotData; +struct ImPlotTicker; //----------------------------------------------------------------------------- // [SECTION] Context Pointer @@ -106,6 +105,10 @@ extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer // Computes the common (base-10) logarithm static inline float ImLog10(float x) { return log10f(x); } static inline double ImLog10(double x) { return log10(x); } +static inline float ImSinh(float x) { return sinhf(x); } +static inline double ImSinh(double x) { return sinh(x); } +static inline float ImAsinh(float x) { return asinhf(x); } +static inline double ImAsinh(double x) { return asinh(x); } // Returns true if a flag is set template static inline bool ImHasFlag(TSet set, TFlag flag) { return (set & flag) == flag; } @@ -120,10 +123,12 @@ template static inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } // Returns always positive modulo (assumes r != 0) static inline int ImPosMod(int l, int r) { return (l % r + r) % r; } +// Returns true if val is NAN +static inline bool ImNan(double val) { return isnan(val); } // Returns true if val is NAN or INFINITY -static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || isnan(val); } +static inline bool ImNanOrInf(double val) { return !(val >= -DBL_MAX && val <= DBL_MAX) || ImNan(val); } // Turns NANs to 0s -static inline double ImConstrainNan(double val) { return isnan(val) ? 0 : val; } +static inline double ImConstrainNan(double val) { return ImNan(val) ? 0 : val; } // Turns infinity to floating point maximums static inline double ImConstrainInf(double val) { return val >= DBL_MAX ? DBL_MAX : val <= -DBL_MAX ? - DBL_MAX : val; } // Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) @@ -215,23 +220,20 @@ static inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)< +static inline bool ImOverlaps(T min_a, T max_a, T min_b, T max_b) { + return min_a <= max_b && min_b <= max_a; +} + //----------------------------------------------------------------------------- // [SECTION] ImPlot Enums //----------------------------------------------------------------------------- -typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_ typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_ -// XY axes scaling combinations -enum ImPlotScale_ { - ImPlotScale_LinLin, // linear x, linear y - ImPlotScale_LogLin, // log x, linear y - ImPlotScale_LinLog, // linear x, log y - ImPlotScale_LogLog // log x, log y -}; - enum ImPlotTimeUnit_ { ImPlotTimeUnit_Us, // microsecond ImPlotTimeUnit_Ms, // millisecond @@ -266,12 +268,19 @@ enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] }; //----------------------------------------------------------------------------- -// [SECTION] ImPlot Structs +// [SECTION] Callbacks +//----------------------------------------------------------------------------- + +typedef void (*ImPlotLocator)(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); + +//----------------------------------------------------------------------------- +// [SECTION] Structs //----------------------------------------------------------------------------- // Combined date/time format spec -struct ImPlotDateTimeFmt { - ImPlotDateTimeFmt(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { +struct ImPlotDateTimeSpec { + ImPlotDateTimeSpec() {} + ImPlotDateTimeSpec(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { Date = date_fmt; Time = time_fmt; UseISO8601 = use_iso_8601; @@ -409,7 +418,6 @@ struct ImPlotColormapData { int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); return Tables[off + idx]; } - }; // ImPlotPoint with positive/negative error values @@ -528,38 +536,41 @@ struct ImPlotTick bool Major; bool ShowLabel; int Level; + int Idx; - ImPlotTick(double value, bool major, bool show_label) { + ImPlotTick(double value, bool major, int level, bool show_label) { PlotPos = value; Major = major; ShowLabel = show_label; + Level = level; TextOffset = -1; - Level = 0; } }; // Collection of ticks -struct ImPlotTickCollection { +struct ImPlotTicker { ImVector Ticks; ImGuiTextBuffer TextBuffer; ImVec2 MaxSize; ImVec2 LateSize; - int Size; + int Levels; - ImPlotTickCollection() { Reset(); } - - const ImPlotTick& Append(const ImPlotTick& tick) { - if (tick.ShowLabel) { - MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; - MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; - } - Ticks.push_back(tick); - Size++; - return Ticks.back(); + ImPlotTicker() { + Reset(); } - const ImPlotTick& Append(double value, bool major, bool show_label, ImPlotFormatter formatter, void* data) { - ImPlotTick tick(value, major, show_label); + ImPlotTick& AddTick(double value, bool major, int level, bool show_label, const char* label) { + ImPlotTick tick(value, major, level, show_label); + if (show_label && label != NULL) { + tick.TextOffset = TextBuffer.size(); + TextBuffer.append(label, label + strlen(label) + 1); + tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); + } + return AddTick(tick); + } + + ImPlotTick& AddTick(double value, bool major, int level, bool show_label, ImPlotFormatter formatter, void* data) { + ImPlotTick tick(value, major, level, show_label); if (show_label && formatter != NULL) { char buff[IMPLOT_LABEL_MAX_SIZE]; tick.TextOffset = TextBuffer.size(); @@ -567,16 +578,25 @@ struct ImPlotTickCollection { TextBuffer.append(buff, buff + strlen(buff) + 1); tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset); } - return Append(tick); + return AddTick(tick); + } + + inline ImPlotTick& AddTick(ImPlotTick tick) { + if (tick.ShowLabel) { + MaxSize.x = tick.LabelSize.x > MaxSize.x ? tick.LabelSize.x : MaxSize.x; + MaxSize.y = tick.LabelSize.y > MaxSize.y ? tick.LabelSize.y : MaxSize.y; + } + tick.Idx = Ticks.size(); + Ticks.push_back(tick); + return Ticks.back(); } const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } - void OverrideSize(const ImVec2& size) { - MaxSize.x = size.x > MaxSize.x ? size.x : MaxSize.x; - MaxSize.y = size.y > MaxSize.y ? size.y : MaxSize.y; + const char* GetText(const ImPlotTick& tick) { + return GetText(tick.Idx); } void OverrideSizeLate(const ImVec2& size) { @@ -589,7 +609,11 @@ struct ImPlotTickCollection { TextBuffer.Buf.shrink(0); MaxSize = LateSize; LateSize = ImVec2(0,0); - Size = 0; + Levels = 1; + } + + int TickCount() const { + return Ticks.Size; } }; @@ -599,24 +623,38 @@ struct ImPlotAxis ImGuiID ID; ImPlotAxisFlags Flags; ImPlotAxisFlags PreviousFlags; - ImPlotCond RangeCond; - ImPlotTickCollection Ticks; ImPlotRange Range; + ImPlotCond RangeCond; + ImPlotScale Scale; ImPlotRange FitExtents; ImPlotAxis* OrthoAxis; + ImPlotRange ConstraintRange; + ImPlotRange ConstraintZoom; + + ImPlotTicker Ticker; + ImPlotFormatter Formatter; + void* FormatterData; + char FormatSpec[16]; + ImPlotLocator Locator; + double* LinkedMin; double* LinkedMax; + int PickerLevel; ImPlotTime PickerTimeMin, PickerTimeMax; - float Datum1, Datum2; + + ImPlotTransform TransformForward; + ImPlotTransform TransformInverse; + void* TransformData; float PixelMin, PixelMax; - double LinM, LogD; + double ScaleMin, ScaleMax; + double ScaleToPixel; + float Datum1, Datum2; + ImRect HoverRect; int LabelOffset; ImU32 ColorMaj, ColorMin, ColorTick, ColorTxt, ColorBg, ColorHov, ColorAct, ColorHiLi; - char FormatSpec[16]; - ImPlotFormatter Formatter; - void* FormatterData; + bool Enabled; bool Vertical; bool FitThisFrame; @@ -630,9 +668,14 @@ struct ImPlotAxis Flags = PreviousFlags = ImPlotAxisFlags_None; Range.Min = 0; Range.Max = 1; + Scale = ImPlotScale_Linear; + TransformForward = TransformInverse = NULL; + TransformData = NULL; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = NULL; + ConstraintRange = ImPlotRange(-INFINITY,INFINITY); + ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); LinkedMin = LinkedMax = NULL; PickerLevel = 0; Datum1 = Datum2 = 0; @@ -642,32 +685,42 @@ struct ImPlotAxis ColorHiLi = IM_COL32_BLACK_TRANS; Formatter = NULL; FormatterData = NULL; + Locator = NULL; Enabled = Hovered = Held = FitThisFrame = HasRange = HasFormatSpec = false; ShowDefaultTicks = true; } inline void Reset() { Enabled = false; + Scale = ImPlotScale_Linear; + TransformForward = TransformInverse = NULL; + TransformData = NULL; LabelOffset = -1; HasFormatSpec = false; Formatter = NULL; FormatterData = NULL; + Locator = NULL; ShowDefaultTicks = true; FitThisFrame = false; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; OrthoAxis = NULL; - Ticks.Reset(); + ConstraintRange = ImPlotRange(-INFINITY,INFINITY); + ConstraintZoom = ImPlotRange(DBL_MIN,INFINITY); + Ticker.Reset(); } inline bool SetMin(double _min, bool force=false) { if (!force && IsLockedMin()) return false; _min = ImConstrainNan(ImConstrainInf(_min)); - if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale)) - _min = ImConstrainLog(_min); - if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) - _min = ImConstrainTime(_min); + if (_min < ConstraintRange.Min) + _min = ConstraintRange.Min; + double z = Range.Max - _min; + if (z < ConstraintZoom.Min) + _min = Range.Max - ConstraintZoom.Min; + if (z > ConstraintZoom.Max) + _min = Range.Max - ConstraintZoom.Max; if (_min >= Range.Max) return false; Range.Min = _min; @@ -680,10 +733,13 @@ struct ImPlotAxis if (!force && IsLockedMax()) return false; _max = ImConstrainNan(ImConstrainInf(_max)); - if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale)) - _max = ImConstrainLog(_max); - if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) - _max = ImConstrainTime(_max); + if (_max > ConstraintRange.Max) + _max = ConstraintRange.Max; + double z = _max - Range.Min; + if (z < ConstraintZoom.Min) + _max = Range.Min + ConstraintZoom.Min; + if (z > ConstraintZoom.Max) + _max = Range.Min + ConstraintZoom.Max; if (_max <= Range.Min) return false; Range.Max = _max; @@ -725,43 +781,59 @@ struct ImPlotAxis inline void Constrain() { Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); - if (IsLog()) { - Range.Min = ImConstrainLog(Range.Min); - Range.Max = ImConstrainLog(Range.Max); + if (Range.Min < ConstraintRange.Min) + Range.Min = ConstraintRange.Min; + if (Range.Max > ConstraintRange.Max) + Range.Max = ConstraintRange.Max; + double z = Range.Size(); + if (z < ConstraintZoom.Min) { + double delta = (ConstraintZoom.Min - z) * 0.5; + Range.Min -= delta; + Range.Max += delta; } - if (IsTime()) { - Range.Min = ImConstrainTime(Range.Min); - Range.Max = ImConstrainTime(Range.Max); + if (z > ConstraintZoom.Max) { + double delta = (z - ConstraintZoom.Max) * 0.5f; + Range.Min += delta; + Range.Max -= delta; } if (Range.Max <= Range.Min) Range.Max = Range.Min + DBL_EPSILON; } inline void UpdateTransformCache() { - LinM = (PixelMax - PixelMin) / Range.Size(); - LogD = IsLog() ? ImLog10(Range.Max / Range.Min) : 0; + ScaleToPixel = (PixelMax - PixelMin) / Range.Size(); + if (TransformForward != NULL) { + ScaleMin = TransformForward(Range.Min, TransformData); + ScaleMax = TransformForward(Range.Max, TransformData); + } + else { + ScaleMin = Range.Min; + ScaleMax = Range.Max; + } } + inline float PlotToPixels(double plt) const { + if (TransformForward != NULL) { + double s = TransformForward(plt, TransformData); + double t = (s - ScaleMin) / (ScaleMax - ScaleMin); + plt = Range.Min + Range.Size() * t; + } + return (float)(PixelMin + ScaleToPixel * (plt - Range.Min)); + } + + inline double PixelsToPlot(float pix) const { - double plt = (pix - PixelMin) / LinM + Range.Min; - if (IsLog()) { + double plt = (pix - PixelMin) / ScaleToPixel + Range.Min; + if (TransformInverse != NULL) { double t = (plt - Range.Min) / Range.Size(); - plt = ImPow(10, t * LogD) * Range.Min; + double s = t * (ScaleMax - ScaleMin) + ScaleMin; + plt = TransformInverse(s, TransformData); } return plt; } - inline float PlotToPixels(double plt) const { - if (IsLog()) { - plt = plt <= 0.0 ? IMPLOT_LOG_ZERO : plt; - double t = ImLog10(plt / Range.Min) / LogD; - plt = ImLerp(Range.Min, Range.Max, (float)t); - } - return (float)(PixelMin + LinM * (plt - Range.Min)); - } - inline void ExtendFit(double v) { - if (!ImNanOrInf(v) && !(IsLog() && v <= 0)) { + if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } @@ -770,7 +842,7 @@ struct ImPlotAxis inline void ExtendFitWith(ImPlotAxis& alt, double v, double v_alt) { if (ImHasFlag(Flags, ImPlotAxisFlags_RangeFit) && !alt.Range.Contains(v_alt)) return; - if (!ImNanOrInf(v) && !(IsLog() && v <= 0)) { + if (!ImNanOrInf(v) && v >= ConstraintRange.Min && v <= ConstraintRange.Max) { FitExtents.Min = v < FitExtents.Min ? v : FitExtents.Min; FitExtents.Max = v > FitExtents.Max ? v : FitExtents.Max; } @@ -802,17 +874,29 @@ struct ImPlotAxis inline bool IsForeground() const { return ImHasFlag(Flags, ImPlotAxisFlags_Foreground); } inline bool IsAutoFitting() const { return ImHasFlag(Flags, ImPlotAxisFlags_AutoFit); } inline bool CanInitFit() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoInitialFit) && !HasRange && !LinkedMin && !LinkedMax; } - inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } + inline bool IsRangeLocked() const { return HasRange && RangeCond == ImPlotCond_Always; } inline bool IsLockedMin() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMin); } inline bool IsLockedMax() const { return !Enabled || IsRangeLocked() || ImHasFlag(Flags, ImPlotAxisFlags_LockMax); } inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } - inline bool IsTime() const { return ImHasFlag(Flags, ImPlotAxisFlags_Time); } - inline bool IsLog() const { return ImHasFlag(Flags, ImPlotAxisFlags_LogScale); } inline bool HasMenus() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoMenus); } + inline bool IsPanLocked(bool increasing) { + if (ImHasFlag(Flags, ImPlotAxisFlags_PanStretch)) { + return IsInputLocked(); + } + else { + if (IsLockedMin() || IsLockedMax() || IsAutoFitting()) + return false; + if (increasing) + return Range.Max == ConstraintRange.Max; + else + return Range.Min == ConstraintRange.Min; + } + } + void PushLinks() { if (LinkedMin) { *LinkedMin = Range.Min; } if (LinkedMax) { *LinkedMax = Range.Max; } @@ -1021,7 +1105,7 @@ struct ImPlotPlot inline const char* GetAxisLabel(const ImPlotAxis& axis) const { return TextBuffer.Buf.Data + axis.LabelOffset; } }; -// Holds subplot data that must persist afer EndSubplot +// Holds subplot data that must persist after EndSubplot struct ImPlotSubplot { ImGuiID ID; ImPlotSubplotFlags Flags; @@ -1077,23 +1161,23 @@ struct ImPlotNextPlotData // Temporary data storage for upcoming item struct ImPlotNextItemData { - ImVec4 Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar - float LineWeight; - ImPlotMarker Marker; - float MarkerSize; - float MarkerWeight; - float FillAlpha; - float ErrorBarSize; - float ErrorBarWeight; - float DigitalBitHeight; - float DigitalBitGap; - bool RenderLine; - bool RenderFill; - bool RenderMarkerLine; - bool RenderMarkerFill; - bool HasHidden; - bool Hidden; - ImPlotCond HiddenCond; + ImVec4 Colors[5]; // ImPlotCol_Line, ImPlotCol_Fill, ImPlotCol_MarkerOutline, ImPlotCol_MarkerFill, ImPlotCol_ErrorBar + float LineWeight; + ImPlotMarker Marker; + float MarkerSize; + float MarkerWeight; + float FillAlpha; + float ErrorBarSize; + float ErrorBarWeight; + float DigitalBitHeight; + float DigitalBitGap; + bool RenderLine; + bool RenderFill; + bool RenderMarkerLine; + bool RenderMarkerFill; + bool HasHidden; + bool Hidden; + ImPlotCond HiddenCond; ImPlotNextItemData() { Reset(); } void Reset() { for (int i = 0; i < 5; ++i) @@ -1116,7 +1200,7 @@ struct ImPlotContext { ImPlotItem* PreviousItem; // Tick Marks and Labels - ImPlotTickCollection CTicks; + ImPlotTicker CTicker; // Annotation and Tabs ImPlotAnnotationCollection Annotations; @@ -1214,12 +1298,25 @@ IMPLOT_API void ShowSubplotsContextMenu(ImPlotSubplot& subplot); //----------------------------------------------------------------------------- // Begins a new item. Returns false if the item should not be plotted. Pushes PlotClipRect. -IMPLOT_API bool BeginItem(const char* label_id, ImPlotCol recolor_from = -1); +IMPLOT_API bool BeginItem(const char* label_id, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO); + +// Same as above but with fitting functionality. +template +bool BeginItemEx(const char* label_id, const _Fitter& fitter, ImPlotItemFlags flags=0, ImPlotCol recolor_from=IMPLOT_AUTO) { + if (BeginItem(label_id, flags, recolor_from)) { + ImPlotPlot& plot = *GetCurrentPlot(); + if (plot.FitThisFrame && !ImHasFlag(flags, ImPlotItemFlags_NoFit)) + fitter.Fit(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]); + return true; + } + return false; +} + // Ends an item (call only if BeginItem returns true). Pops PlotClipRect. IMPLOT_API void EndItem(); // Register or get an existing item from the current plot. -IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created = NULL); +IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created = NULL); // Get a plot item from the current plot. IMPLOT_API ImPlotItem* GetItem(const char* label_id); // Gets the current item. @@ -1265,21 +1362,6 @@ static inline bool AnyAxesHovered(ImPlotAxis* axes, int count) { return false; } -// Gets the XY scale for the current plot and y-axis (TODO) -static inline ImPlotScale GetCurrentScale() { - ImPlotPlot& plot = *GetCurrentPlot(); - ImPlotAxis& x = plot.Axes[plot.CurrentX]; - ImPlotAxis& y = plot.Axes[plot.CurrentY]; - if (!x.IsLog() && !y.IsLog()) - return ImPlotScale_LinLin; - else if (x.IsLog() && !y.IsLog()) - return ImPlotScale_LogLin; - else if (!x.IsLog() && y.IsLog()) - return ImPlotScale_LinLog; - else - return ImPlotScale_LogLog; -} - // Returns true if the user has requested data to be fit. static inline bool FitThisFrame() { return GImPlot->CurrentPlot->FitThisFrame; @@ -1331,21 +1413,9 @@ IMPLOT_API void ShowAltLegend(const char* title_id, bool vertical = true, const IMPLOT_API bool ShowLegendContextMenu(ImPlotLegend& legend, bool visible); //----------------------------------------------------------------------------- -// [SECTION] Tick Utils +// [SECTION] Label Utils //----------------------------------------------------------------------------- -// Label a tick with time formatting. -IMPLOT_API void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotDateTimeFmt fmt); - -// Populates a list of ImPlotTicks with normal spaced and formatted ticks -IMPLOT_API void AddTicksDefault(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); -// Populates a list of ImPlotTicks with logarithmic space and formatted ticks -IMPLOT_API void AddTicksLogarithmic(const ImPlotRange& range, float pix, bool vertical, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); -// Populates a list of ImPlotTicks with custom spaced and labeled ticks -IMPLOT_API void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, ImPlotFormatter formatter, void* data); -// Populates a list of ImPlotTicks with time formatted ticks. -IMPLOT_API void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks); - // Create a a string label for a an axis value IMPLOT_API void LabelAxisValue(const ImPlotAxis& axis, double value, char* buff, int size, bool round = false); @@ -1503,7 +1573,7 @@ IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTim // Formats the date part of timestamp t into a buffer according to #fmt IMPLOT_API int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, bool use_iso_8601); // Formats the time and/or date parts of a timestamp t into a buffer according to #fmt -IMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeFmt fmt); +IMPLOT_API int FormatDateTime(const ImPlotTime& t, char* buffer, int size, ImPlotDateTimeSpec fmt); // Shows a date picker widget block (year/month/day). // #level = 0 for day, 1 for month, 2 for year. Modified by user interaction. @@ -1514,4 +1584,73 @@ IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const // #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true. IMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t); +//----------------------------------------------------------------------------- +// [SECTION] Transforms +//----------------------------------------------------------------------------- + +static inline double TransformForward_Log10(double v, void*) { + v = v <= 0.0 ? DBL_MIN : v; + return ImLog10(v); +} + +static inline double TransformInverse_Log10(double v, void*) { + return ImPow(10, v); +} + +static inline double TransformForward_SymLog(double v, void*) { + return 2.0 * ImAsinh(v / 2.0); +} + +static inline double TransformInverse_SymLog(double v, void*) { + return 2.0 * ImSinh(v / 2.0); +} + +static inline double TransformForward_Logit(double v, void*) { + v = ImClamp(v, DBL_MIN, 1.0 - DBL_EPSILON); + return ImLog10(v / (1 - v)); +} + +static inline double TransformInverse_Logit(double v, void*) { + return 1.0 / (1.0 + ImPow(10,-v)); +} + +//----------------------------------------------------------------------------- +// [SECTION] Formatters +//----------------------------------------------------------------------------- + +static inline int Formatter_Default(double value, char* buff, int size, void* data) { + char* fmt = (char*)data; + return ImFormatString(buff, size, fmt, value); +} + +static inline int Formatter_Logit(double value, char* buff, int size, void*) { + if (value == 0.5) + return ImFormatString(buff,size,"1/2"); + else if (value < 0.5) + return ImFormatString(buff,size,"%g", value); + else + return ImFormatString(buff,size,"1 - %g", 1 - value); +} + +struct Formatter_Time_Data { + ImPlotTime Time; + ImPlotDateTimeSpec Spec; + ImPlotFormatter UserFormatter; + void* UserFormatterData; +}; + +static inline int Formatter_Time(double, char* buff, int size, void* data) { + Formatter_Time_Data* ftd = (Formatter_Time_Data*)data; + return FormatDateTime(ftd->Time, buff, size, ftd->Spec); +} + +//------------------------------------------------------------------------------ +// [SECTION] Locator +//------------------------------------------------------------------------------ + +void Locator_Default(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); +void Locator_Time(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); +void Locator_Log10(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); +void Locator_SymLog(ImPlotTicker& ticker, const ImPlotRange& range, float pixels, bool vertical, ImPlotFormatter formatter, void* formatter_data); + } // namespace ImPlot diff --git a/implot_items.cpp b/implot_items.cpp index ae750c9..b6247fc 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -20,11 +20,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.13 WIP +// ImPlot v0.14 #include "implot.h" #include "implot_internal.h" +//----------------------------------------------------------------------------- +// [SECTION] Macros and Defines +//----------------------------------------------------------------------------- + #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f @@ -65,7 +69,7 @@ static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } namespace ImPlot { //----------------------------------------------------------------------------- -// Utils +// [SECTION] Utils //----------------------------------------------------------------------------- // Calc maximum index size of ImDrawIdx @@ -74,11 +78,172 @@ struct MaxIdx { static const unsigned int Value; }; template <> const unsigned int MaxIdx::Value = 65535; template <> const unsigned int MaxIdx::Value = 4294967295; +IMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { + const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) && + ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex); + if (aa) { + ImVec4 tex_uvs = draw_list._Data->TexUvLines[(int)(half_weight*2)]; + tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y); + tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w); + half_weight += 1; + } + else { + tex_uv0 = tex_uv1 = draw_list._Data->TexUvWhitePixel; + } +} + +IMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) { + float dx = P2.x - P1.x; + float dy = P2.y - P1.y; + IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); + dx *= half_weight; + dy *= half_weight; + draw_list._VtxWritePtr[0].pos.x = P1.x + dy; + draw_list._VtxWritePtr[0].pos.y = P1.y - dx; + draw_list._VtxWritePtr[0].uv = tex_uv0; + draw_list._VtxWritePtr[0].col = col; + draw_list._VtxWritePtr[1].pos.x = P2.x + dy; + draw_list._VtxWritePtr[1].pos.y = P2.y - dx; + draw_list._VtxWritePtr[1].uv = tex_uv0; + draw_list._VtxWritePtr[1].col = col; + draw_list._VtxWritePtr[2].pos.x = P2.x - dy; + draw_list._VtxWritePtr[2].pos.y = P2.y + dx; + draw_list._VtxWritePtr[2].uv = tex_uv1; + draw_list._VtxWritePtr[2].col = col; + draw_list._VtxWritePtr[3].pos.x = P1.x - dy; + draw_list._VtxWritePtr[3].pos.y = P1.y + dx; + draw_list._VtxWritePtr[3].uv = tex_uv1; + draw_list._VtxWritePtr[3].col = col; + draw_list._VtxWritePtr += 4; + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr += 6; + draw_list._VtxCurrentIdx += 4; +} + +IMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) { + draw_list._VtxWritePtr[0].pos = Pmin; + draw_list._VtxWritePtr[0].uv = uv; + draw_list._VtxWritePtr[0].col = col; + draw_list._VtxWritePtr[1].pos = Pmax; + draw_list._VtxWritePtr[1].uv = uv; + draw_list._VtxWritePtr[1].col = col; + draw_list._VtxWritePtr[2].pos.x = Pmin.x; + draw_list._VtxWritePtr[2].pos.y = Pmax.y; + draw_list._VtxWritePtr[2].uv = uv; + draw_list._VtxWritePtr[2].col = col; + draw_list._VtxWritePtr[3].pos.x = Pmax.x; + draw_list._VtxWritePtr[3].pos.y = Pmin.y; + draw_list._VtxWritePtr[3].uv = uv; + draw_list._VtxWritePtr[3].col = col; + draw_list._VtxWritePtr += 4; + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr += 6; + draw_list._VtxCurrentIdx += 4; +} + +IMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) { + + draw_list._VtxWritePtr[0].pos.x = Pmin.x; + draw_list._VtxWritePtr[0].pos.y = Pmin.y; + draw_list._VtxWritePtr[0].uv = uv; + draw_list._VtxWritePtr[0].col = col; + + draw_list._VtxWritePtr[1].pos.x = Pmin.x; + draw_list._VtxWritePtr[1].pos.y = Pmax.y; + draw_list._VtxWritePtr[1].uv = uv; + draw_list._VtxWritePtr[1].col = col; + + draw_list._VtxWritePtr[2].pos.x = Pmax.x; + draw_list._VtxWritePtr[2].pos.y = Pmax.y; + draw_list._VtxWritePtr[2].uv = uv; + draw_list._VtxWritePtr[2].col = col; + + draw_list._VtxWritePtr[3].pos.x = Pmax.x; + draw_list._VtxWritePtr[3].pos.y = Pmin.y; + draw_list._VtxWritePtr[3].uv = uv; + draw_list._VtxWritePtr[3].col = col; + + draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight; + draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight; + draw_list._VtxWritePtr[4].uv = uv; + draw_list._VtxWritePtr[4].col = col; + + draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight; + draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight; + draw_list._VtxWritePtr[5].uv = uv; + draw_list._VtxWritePtr[5].col = col; + + draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight; + draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight; + draw_list._VtxWritePtr[6].uv = uv; + draw_list._VtxWritePtr[6].col = col; + + draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight; + draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight; + draw_list._VtxWritePtr[7].uv = uv; + draw_list._VtxWritePtr[7].col = col; + + draw_list._VtxWritePtr += 8; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); + draw_list._IdxWritePtr += 3; + + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); + draw_list._IdxWritePtr += 3; + + draw_list._VtxCurrentIdx += 8; +} + + //----------------------------------------------------------------------------- -// Item Utils +// [SECTION] Item Utils //----------------------------------------------------------------------------- -ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created) { +ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) { ImPlotContext& gp = *GImPlot; ImPlotItemGroup& Items = *gp.CurrentItems; ImGuiID id = Items.GetItemID(label_id); @@ -90,7 +255,7 @@ ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created) { item->SeenThisFrame = true; int idx = Items.GetItemIndex(item); item->ID = id; - if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) { + if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) { Items.Legend.Indices.push_back(idx); item->NameOffset = Items.Legend.Labels.size(); Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); @@ -182,20 +347,19 @@ void BustColorCache(const char* plot_title_id) { } //----------------------------------------------------------------------------- -// Begin/EndItem +// [SECTION] BeginItem / EndItem //----------------------------------------------------------------------------- static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; - // Begins a new item. Returns false if the item should not be plotted. -bool BeginItem(const char* label_id, ImPlotCol recolor_from) { +bool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); bool just_created; - ImPlotItem* item = RegisterOrGetItem(label_id, &just_created); + ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created); // set current item gp.CurrentItem = item; ImPlotNextItemData& s = gp.NextItemData; @@ -262,8 +426,8 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { // set render flags s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; s.RenderFill = s.Colors[ImPlotCol_Fill].w > 0; - s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w > 0; + s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; // push rendering clip rect PushPlotClipRect(); return true; @@ -283,10 +447,9 @@ void EndItem() { } //----------------------------------------------------------------------------- -// INDEXERS +// [SECTION] Indexers //----------------------------------------------------------------------------- -// Offsets and strides a data buffer template IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); @@ -299,16 +462,9 @@ IMPLOT_INLINE T IndexData(const T* data, int idx, int count, int offset, int str } } -//----------------------------------------------------------------------------- -// GETTERS -//----------------------------------------------------------------------------- - -// Getters can be thought of as iterators that convert user data (e.g. raw arrays) -// to ImPlotPoints - template -struct GetterIdx { - GetterIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : +struct IndexerIdx { + IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(count ? ImPosMod(offset, count) : 0), @@ -323,8 +479,27 @@ struct GetterIdx { int Stride; }; -struct GetterLin { - GetterLin(double m, double b) : M(m), B(b) { } +template +struct IndexerAdd { + IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) + : Indexer1(indexer1), + Indexer2(indexer2), + Scale1(scale1), + Scale2(scale2), + Count(ImMin(Indexer1.Count, Indexer2.Count)) + { } + template IMPLOT_INLINE double operator()(I idx) const { + return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx); + } + const _Indexer1& Indexer1; + const _Indexer2& Indexer2; + double Scale1; + double Scale2; + int Count; +}; + +struct IndexerLin { + IndexerLin(double m, double b) : M(m), B(b) { } template IMPLOT_INLINE double operator()(I idx) const { return M * idx + B; } @@ -332,86 +507,79 @@ struct GetterLin { const double B; }; -struct GetterRef { - GetterRef(double ref) : Ref(ref) { } +struct IndexerConst { + IndexerConst(double ref) : Ref(ref) { } template IMPLOT_INLINE double operator()(I) const { return Ref; } const double Ref; }; -template -struct GetterXY { - GetterXY(TGetterX x, TGetterY y, int count) : GetterX(x), GetterY(y), Count(count) { } - template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - return ImPlotPoint(GetterX(idx),GetterY(idx)); - } - const TGetterX GetterX; - const TGetterY GetterY; - const int Count; -}; +//----------------------------------------------------------------------------- +// [SECTION] Getters +//----------------------------------------------------------------------------- -// Interprets an array of Y points as ImPlotPoints where the X value is the index -template -struct GetterXs { - GetterXs(const T* xs, int count, double yscale, double y0, int offset, int stride) : - Xs(xs), - Count(count), - YScale(yscale), - Y0(y0), - Offset(count ? ImPosMod(offset, count) : 0), - Stride(stride) - { } +template +struct GetterXY { + GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - return ImPlotPoint((double)IndexData(Xs, idx, Count, Offset, Stride), Y0 + YScale * idx); + return ImPlotPoint(IndxerX(idx),IndxerY(idx)); } - const T* const Xs; + const _IndexerX IndxerX; + const _IndexerY IndxerY; const int Count; - const double YScale; - const double Y0; - const int Offset; - const int Stride; }; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { - GetterFuncPtr(ImPlotPoint (*getter)(void* data, int idx), void* data, int count) : + GetterFuncPtr(ImPlotGetter getter, void* data, int count) : Getter(getter), Data(data), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { - return Getter(Data, idx); + return Getter(idx, Data); } - ImPlotPoint (* const Getter)(void* data, int idx); + ImPlotGetter Getter; void* const Data; const int Count; }; -template +template struct GetterOverrideX { - GetterOverrideX(const TGetter& getter, double x) : Getter(getter), X(x), Count(getter.Count) { } + GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.x = X; return p; } - const TGetter& Getter; + const _Getter Getter; const double X; const int Count; }; -template +template struct GetterOverrideY { - GetterOverrideY(const TGetter& getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } + GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.y = Y; return p; } - const TGetter& Getter; + const _Getter Getter; const double Y; const int Count; }; +template +struct GetterLoop { + GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { } + template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + idx = idx % (Count - 1); + return Getter(idx); + } + const _Getter Getter; + const int Count; +}; + template struct GetterError { GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) : @@ -439,49 +607,185 @@ struct GetterError { }; //----------------------------------------------------------------------------- -// TRANSFORMERS +// [SECTION] Fitters //----------------------------------------------------------------------------- -// Transforms convert points in plot space (i.e. ImPlotPoint) to pixel space (i.e. ImVec2) - -struct TransformerLin { - TransformerLin(double pixMin, double pltMin, double, double m, double ) : PixMin(pixMin), PltMin(pltMin), M(m) { } - template IMPLOT_INLINE float operator()(T p) const { return (float)(PixMin + M * (p - PltMin)); } - double PixMin, PltMin, M; +template +struct Fitter1 { + Fitter1(const _Getter1& getter) : Getter(getter) { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + for (int i = 0; i < Getter.Count; ++i) { + ImPlotPoint p = Getter(i); + x_axis.ExtendFitWith(y_axis, p.x, p.y); + y_axis.ExtendFitWith(x_axis, p.y, p.x); + } + } + const _Getter1& Getter; }; -struct TransformerLog { - TransformerLog(double pixMin, double pltMin, double pltMax, double m, double den) : Den(den), PltMin(pltMin), PltMax(pltMax), PixMin(pixMin), M(m) { } +template +struct FitterX { + FitterX(const _Getter1& getter) : Getter(getter) { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const { + for (int i = 0; i < Getter.Count; ++i) { + ImPlotPoint p = Getter(i); + x_axis.ExtendFit(p.x); + } + } + const _Getter1& Getter; +}; + +template +struct FitterY { + FitterY(const _Getter1& getter) : Getter(getter) { } + void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const { + for (int i = 0; i < Getter.Count; ++i) { + ImPlotPoint p = Getter(i); + y_axis.ExtendFit(p.y); + } + } + const _Getter1& Getter; +}; + +template +struct Fitter2 { + Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + for (int i = 0; i < Getter1.Count; ++i) { + ImPlotPoint p = Getter1(i); + x_axis.ExtendFitWith(y_axis, p.x, p.y); + y_axis.ExtendFitWith(x_axis, p.y, p.x); + } + for (int i = 0; i < Getter2.Count; ++i) { + ImPlotPoint p = Getter2(i); + x_axis.ExtendFitWith(y_axis, p.x, p.y); + y_axis.ExtendFitWith(x_axis, p.y, p.x); + } + } + const _Getter1& Getter1; + const _Getter2& Getter2; +}; + +template +struct FitterBarV { + FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : + Getter1(getter1), + Getter2(getter2), + HalfWidth(width*0.5) + { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + int count = ImMin(Getter1.Count, Getter2.Count); + for (int i = 0; i < count; ++i) { + ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth; + ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth; + x_axis.ExtendFitWith(y_axis, p1.x, p1.y); + y_axis.ExtendFitWith(x_axis, p1.y, p1.x); + x_axis.ExtendFitWith(y_axis, p2.x, p2.y); + y_axis.ExtendFitWith(x_axis, p2.y, p2.x); + } + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const double HalfWidth; +}; + +template +struct FitterBarH { + FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) : + Getter1(getter1), + Getter2(getter2), + HalfHeight(height*0.5) + { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + int count = ImMin(Getter1.Count, Getter2.Count); + for (int i = 0; i < count; ++i) { + ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight; + ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight; + x_axis.ExtendFitWith(y_axis, p1.x, p1.y); + y_axis.ExtendFitWith(x_axis, p1.y, p1.x); + x_axis.ExtendFitWith(y_axis, p2.x, p2.y); + y_axis.ExtendFitWith(x_axis, p2.y, p2.x); + } + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const double HalfHeight; +}; + +struct FitterRect { + FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) : + Pmin(pmin), + Pmax(pmax) + { } + FitterRect(const ImPlotRect& rect) : + FitterRect(rect.Min(), rect.Max()) + { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y); + y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x); + x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y); + y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x); + } + const ImPlotPoint Pmin; + const ImPlotPoint Pmax; +}; + +//----------------------------------------------------------------------------- +// [SECTION] Transformers +//----------------------------------------------------------------------------- + +struct Transformer1 { + Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) : + ScaMin(scaMin), + ScaMax(scaMax), + PltMin(pltMin), + PltMax(pltMax), + PixMin(pixMin), + M(m), + TransformFwd(fwd), + TransformData(data) + { } + template IMPLOT_INLINE float operator()(T p) const { - p = p <= 0.0 ? IMPLOT_LOG_ZERO : p; - double t = ImLog10(p / PltMin) / Den; - p = ImLerp(PltMin, PltMax, (float)t); + if (TransformFwd != NULL) { + double s = TransformFwd(p, TransformData); + double t = (s - ScaMin) / (ScaMax - ScaMin); + p = PltMin + (PltMax - PltMin) * t; + } return (float)(PixMin + M * (p - PltMin)); } - double Den, PltMin, PltMax, PixMin, M; + + double ScaMin, ScaMax, PltMin, PltMax, PixMin, M; + ImPlotTransform TransformFwd; + void* TransformData; }; -template -struct TransformerXY { - TransformerXY(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : +struct Transformer2 { + Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : Tx(x_axis.PixelMin, x_axis.Range.Min, x_axis.Range.Max, - x_axis.LinM, - x_axis.LogD), + x_axis.ScaleToPixel, + x_axis.ScaleMin, + x_axis.ScaleMax, + x_axis.TransformForward, + x_axis.TransformData), Ty(y_axis.PixelMin, y_axis.Range.Min, y_axis.Range.Max, - y_axis.LinM, - y_axis.LogD) + y_axis.ScaleToPixel, + y_axis.ScaleMin, + y_axis.ScaleMax, + y_axis.TransformForward, + y_axis.TransformData) { } - TransformerXY(const ImPlotPlot& plot) : - TransformerXY(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) + Transformer2(const ImPlotPlot& plot) : + Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) { } - TransformerXY() : - TransformerXY(*GImPlot->CurrentPlot) + Transformer2() : + Transformer2(*GImPlot->CurrentPlot) { } template IMPLOT_INLINE ImVec2 operator()(const P& plt) const { @@ -490,187 +794,388 @@ struct TransformerXY { out.y = Ty(plt.y); return out; } - TransformerX Tx; - TransformerY Ty; + + template IMPLOT_INLINE ImVec2 operator()(T x, T y) const { + ImVec2 out; + out.x = Tx(x); + out.y = Ty(y); + return out; + } + + Transformer1 Tx; + Transformer1 Ty; }; -typedef TransformerXY TransformerLinLin; -typedef TransformerXY TransformerLinLog; -typedef TransformerXY TransformerLogLin; -typedef TransformerXY TransformerLogLog; - //----------------------------------------------------------------------------- -// PRIMITIVE RENDERERS +// [SECTION] Renderers //----------------------------------------------------------------------------- -IMPLOT_INLINE void PrimLine(const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { - float dx = P2.x - P1.x; - float dy = P2.y - P1.y; - IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); - dx *= half_weight; - dy *= half_weight; - DrawList._VtxWritePtr[0].pos.x = P1.x + dy; - DrawList._VtxWritePtr[0].pos.y = P1.y - dx; - DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = col; - DrawList._VtxWritePtr[1].pos.x = P2.x + dy; - DrawList._VtxWritePtr[1].pos.y = P2.y - dx; - DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = col; - DrawList._VtxWritePtr[2].pos.x = P2.x - dy; - DrawList._VtxWritePtr[2].pos.y = P2.y + dx; - DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = col; - DrawList._VtxWritePtr[3].pos.x = P1.x - dy; - DrawList._VtxWritePtr[3].pos.y = P1.y + dx; - DrawList._VtxWritePtr[3].uv = uv; - DrawList._VtxWritePtr[3].col = col; - DrawList._VtxWritePtr += 4; - DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); - DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); - DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2); - DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx); - DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2); - DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); - DrawList._IdxWritePtr += 6; - DrawList._VtxCurrentIdx += 4; -} +struct RendererBase { + RendererBase(int prims, int idx_consumed, int vtx_consumed) : + Prims(prims), + IdxConsumed(idx_consumed), + VtxConsumed(vtx_consumed) + { } + const int Prims; + Transformer2 Transformer; + const int IdxConsumed; + const int VtxConsumed; +}; -IMPLOT_INLINE void PrimRectFilled(const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, ImDrawList& DrawList, ImVec2 uv) { - DrawList._VtxWritePtr[0].pos = Pmin; - DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = col; - DrawList._VtxWritePtr[1].pos = Pmax; - DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = col; - DrawList._VtxWritePtr[2].pos.x = Pmin.x; - DrawList._VtxWritePtr[2].pos.y = Pmax.y; - DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = col; - DrawList._VtxWritePtr[3].pos.x = Pmax.x; - DrawList._VtxWritePtr[3].pos.y = Pmin.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 + 2); - DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx); - DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); - DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); - DrawList._IdxWritePtr += 6; - DrawList._VtxCurrentIdx += 4; -} - -template -struct LineStripRenderer { - IMPLOT_INLINE LineStripRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : +template +struct RendererLineStrip : RendererBase { + RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : + RendererBase(getter.Count - 1, 6, 4), Getter(getter), - Transformer(transformer), - Prims(Getter.Count - 1), Col(col), - HalfWeight(weight/2) + HalfWeight(ImMax(1.0f,weight)*0.5f) { - P1 = Transformer(Getter(0)); + P1 = this->Transformer(Getter(0)); } - IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { - ImVec2 P2 = Transformer(Getter(prim + 1)); + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } - PrimLine(P1,P2,HalfWeight,Col,DrawList,uv); + PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); P1 = P2; return true; } - const TGetter& Getter; - const TTransformer& Transformer; - const int Prims; + const _Getter& Getter; const ImU32 Col; - const float HalfWeight; + mutable float HalfWeight; mutable ImVec2 P1; - static const int IdxConsumed = 6; - static const int VtxConsumed = 4; + mutable ImVec2 UV0; + mutable ImVec2 UV1; }; -template -struct LineSegmentsRenderer { - IMPLOT_INLINE LineSegmentsRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col, float weight) : - Getter1(getter1), - Getter2(getter2), - Transformer(transformer), - Prims(ImMin(Getter1.Count, Getter2.Count)), +template +struct RendererLineStripSkip : RendererBase { + RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : + RendererBase(getter.Count - 1, 6, 4), + Getter(getter), Col(col), - HalfWeight(weight/2) - {} - IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { - ImVec2 P1 = Transformer(Getter1(prim)); - ImVec2 P2 = Transformer(Getter2(prim)); + HalfWeight(ImMax(1.0f,weight)*0.5f) + { + P1 = this->Transformer(Getter(0)); + } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { + if (!ImNan(P2.x) && !ImNan(P2.y)) + P1 = P2; + return false; + } + PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + if (!ImNan(P2.x) && !ImNan(P2.y)) + P1 = P2; + return true; + } + const _Getter& Getter; + const ImU32 Col; + mutable float HalfWeight; + mutable ImVec2 P1; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + +template +struct RendererLineSegments1 : RendererBase { + RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : + RendererBase(getter.Count / 2, 6, 4), + Getter(getter), + Col(col), + HalfWeight(ImMax(1.0f,weight)*0.5f) + { } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P1 = this->Transformer(Getter(prim*2+0)); + ImVec2 P2 = this->Transformer(Getter(prim*2+1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; - PrimLine(P1,P2,HalfWeight,Col,DrawList,uv); + PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } - const TGetter1& Getter1; - const TGetter2& Getter2; - const TTransformer& Transformer; - const int Prims; + const _Getter& Getter; const ImU32 Col; - const float HalfWeight; - static const int IdxConsumed = 6; - static const int VtxConsumed = 4; + mutable float HalfWeight; + mutable ImVec2 UV0; + mutable ImVec2 UV1; }; -template -struct StairsRenderer { - IMPLOT_INLINE StairsRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col, float weight) : - Getter(getter), - Transformer(transformer), - Prims(Getter.Count - 1), +template +struct RendererLineSegments2 : RendererBase { + RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : + RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), + Getter1(getter1), + Getter2(getter2), Col(col), - HalfWeight(weight * 0.5f) - { - P1 = Transformer(Getter(0)); + HalfWeight(ImMax(1.0f,weight)*0.5f) + {} + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } - IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { - ImVec2 P2 = Transformer(Getter(prim + 1)); + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P1 = this->Transformer(Getter1(prim)); + ImVec2 P2 = this->Transformer(Getter2(prim)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const ImU32 Col; + mutable float HalfWeight; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + +template +struct RendererBarsFillV : RendererBase { + RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : + RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), + Getter1(getter1), + Getter2(getter2), + Col(col), + HalfWidth(width/2) + {} + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint p1 = Getter1(prim); + ImPlotPoint p2 = Getter2(prim); + p1.x += HalfWidth; + p2.x -= HalfWidth; + ImVec2 P1 = this->Transformer(p1); + ImVec2 P2 = this->Transformer(p2); + float width_px = ImAbs(P1.x-P2.x); + if (width_px < 1.0f) { + P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; + P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; + } + ImVec2 PMin = ImMin(P1, P2); + ImVec2 PMax = ImMax(P1, P2); + if (!cull_rect.Overlaps(ImRect(PMin, PMax))) + return false; + PrimRectFill(draw_list,PMin,PMax,Col,UV); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const ImU32 Col; + const double HalfWidth; + mutable ImVec2 UV; +}; + +template +struct RendererBarsFillH : RendererBase { + RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : + RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), + Getter1(getter1), + Getter2(getter2), + Col(col), + HalfHeight(height/2) + {} + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint p1 = Getter1(prim); + ImPlotPoint p2 = Getter2(prim); + p1.y += HalfHeight; + p2.y -= HalfHeight; + ImVec2 P1 = this->Transformer(p1); + ImVec2 P2 = this->Transformer(p2); + float height_px = ImAbs(P1.y-P2.y); + if (height_px < 1.0f) { + P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; + P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; + } + ImVec2 PMin = ImMin(P1, P2); + ImVec2 PMax = ImMax(P1, P2); + if (!cull_rect.Overlaps(ImRect(PMin, PMax))) + return false; + PrimRectFill(draw_list,PMin,PMax,Col,UV); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const ImU32 Col; + const double HalfHeight; + mutable ImVec2 UV; +}; + +template +struct RendererBarsLineV : RendererBase { + RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : + RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), + Getter1(getter1), + Getter2(getter2), + Col(col), + HalfWidth(width/2), + Weight(weight) + {} + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint p1 = Getter1(prim); + ImPlotPoint p2 = Getter2(prim); + p1.x += HalfWidth; + p2.x -= HalfWidth; + ImVec2 P1 = this->Transformer(p1); + ImVec2 P2 = this->Transformer(p2); + float width_px = ImAbs(P1.x-P2.x); + if (width_px < 1.0f) { + P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; + P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; + } + ImVec2 PMin = ImMin(P1, P2); + ImVec2 PMax = ImMax(P1, P2); + if (!cull_rect.Overlaps(ImRect(PMin, PMax))) + return false; + PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const ImU32 Col; + const double HalfWidth; + const float Weight; + mutable ImVec2 UV; +}; + +template +struct RendererBarsLineH : RendererBase { + RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : + RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), + Getter1(getter1), + Getter2(getter2), + Col(col), + HalfHeight(height/2), + Weight(weight) + {} + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint p1 = Getter1(prim); + ImPlotPoint p2 = Getter2(prim); + p1.y += HalfHeight; + p2.y -= HalfHeight; + ImVec2 P1 = this->Transformer(p1); + ImVec2 P2 = this->Transformer(p2); + float height_px = ImAbs(P1.y-P2.y); + if (height_px < 1.0f) { + P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; + P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; + } + ImVec2 PMin = ImMin(P1, P2); + ImVec2 PMax = ImMax(P1, P2); + if (!cull_rect.Overlaps(ImRect(PMin, PMax))) + return false; + PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + const ImU32 Col; + const double HalfHeight; + const float Weight; + mutable ImVec2 UV; +}; + + +template +struct RendererStairsPre : RendererBase { + RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : + RendererBase(getter.Count - 1, 12, 8), + Getter(getter), + Col(col), + HalfWeight(ImMax(1.0f,weight)*0.5f) + { + P1 = this->Transformer(Getter(0)); + } + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } - PrimRectFilled(ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, DrawList, uv); - PrimRectFilled(ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, DrawList, uv); + PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); + PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); P1 = P2; return true; } - const TGetter& Getter; - const TTransformer& Transformer; - const int Prims; + const _Getter& Getter; const ImU32 Col; - const float HalfWeight; + mutable float HalfWeight; mutable ImVec2 P1; - static const int IdxConsumed = 12; - static const int VtxConsumed = 8; + mutable ImVec2 UV; }; +template +struct RendererStairsPost : RendererBase { + RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : + RendererBase(getter.Count - 1, 12, 8), + Getter(getter), + Col(col), + HalfWeight(ImMax(1.0f,weight) * 0.5f) + { + P1 = this->Transformer(Getter(0)); + } + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { + P1 = P2; + return false; + } + PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); + PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); + P1 = P2; + return true; + } + const _Getter& Getter; + const ImU32 Col; + mutable float HalfWeight; + mutable ImVec2 P1; + mutable ImVec2 UV; +}; - -template -struct ShadedRenderer { - IMPLOT_INLINE ShadedRenderer(const TGetter1& getter1, const TGetter2& getter2, const TTransformer& transformer, ImU32 col) : +template +struct RendererShaded : RendererBase { + RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : + RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), - Transformer(transformer), - Prims(ImMin(Getter1.Count, Getter2.Count) - 1), Col(col) { - P11 = Transformer(Getter1(0)); - P12 = Transformer(Getter2(0)); + P11 = this->Transformer(Getter1(0)); + P12 = this->Transformer(Getter2(0)); } - - IMPLOT_INLINE bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { - ImVec2 P21 = Transformer(Getter1(prim+1)); - ImVec2 P22 = Transformer(Getter2(prim+1)); + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P21 = this->Transformer(Getter1(prim+1)); + ImVec2 P22 = this->Transformer(Getter2(prim+1)); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); if (!cull_rect.Overlaps(rect)) { P11 = P21; @@ -679,427 +1184,436 @@ struct ShadedRenderer { } 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 + 4); - DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3 - intersect); - DrawList._IdxWritePtr += 6; - DrawList._VtxCurrentIdx += 5; + draw_list._VtxWritePtr[0].pos = P11; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = Col; + draw_list._VtxWritePtr[1].pos = P21; + draw_list._VtxWritePtr[1].uv = UV; + draw_list._VtxWritePtr[1].col = Col; + draw_list._VtxWritePtr[2].pos = intersection; + draw_list._VtxWritePtr[2].uv = UV; + draw_list._VtxWritePtr[2].col = Col; + draw_list._VtxWritePtr[3].pos = P12; + draw_list._VtxWritePtr[3].uv = UV; + draw_list._VtxWritePtr[3].col = Col; + draw_list._VtxWritePtr[4].pos = P22; + draw_list._VtxWritePtr[4].uv = UV; + draw_list._VtxWritePtr[4].col = Col; + draw_list._VtxWritePtr += 5; + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); + draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect); + draw_list._IdxWritePtr += 6; + draw_list._VtxCurrentIdx += 5; P11 = P21; P12 = P22; return true; } - const TGetter1& Getter1; - const TGetter2& Getter2; - const TTransformer& Transformer; - const int Prims; + const _Getter1& Getter1; + const _Getter2& Getter2; const ImU32 Col; mutable ImVec2 P11; mutable ImVec2 P12; - static const int IdxConsumed = 6; - static const int VtxConsumed = 5; + mutable ImVec2 UV; }; +struct RectC { + ImPlotPoint Pos; + ImPlotPoint HalfSize; + ImU32 Color; +}; + +template +struct RendererRectC : RendererBase { + RendererRectC(const _Getter& getter) : + RendererBase(getter.Count, 6, 4), + Getter(getter) + {} + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + RectC rect = Getter(prim); + ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y); + ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y); + if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + PrimRectFill(draw_list,P1,P2,rect.Color,UV); + return true; + } + const _Getter& Getter; + mutable ImVec2 UV; +}; + +//----------------------------------------------------------------------------- +// [SECTION] RenderPrimitives +//----------------------------------------------------------------------------- + /// Renders primitive shapes in bulk as efficiently as possible. -template -IMPLOT_INLINE void RenderPrimitives(const Renderer& renderer, ImDrawList& DrawList, const ImRect& cull_rect) { +template +void RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) { unsigned int prims = renderer.Prims; unsigned int prims_culled = 0; unsigned int idx = 0; - const ImVec2 uv = DrawList._Data->TexUvWhitePixel; + renderer.Init(draw_list); while (prims) { // find how many can be reserved up to end of current draw command's limit - unsigned int cnt = ImMin(prims, (MaxIdx::Value - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed); + unsigned int cnt = ImMin(prims, (MaxIdx::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed); // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time if (cnt >= ImMin(64u, prims)) { if (prims_culled >= cnt) prims_culled -= cnt; // reuse previous reservation else { - DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation + // add more elements to previous reservation + draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed); prims_culled = 0; } } else { if (prims_culled > 0) { - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); + draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); prims_culled = 0; } - cnt = ImMin(prims, (MaxIdx::Value - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed); - DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command + cnt = ImMin(prims, (MaxIdx::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed); + // reserve new draw command + draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed); } prims -= cnt; for (unsigned int ie = idx + cnt; idx != ie; ++idx) { - if (!renderer(DrawList, cull_rect, uv, idx)) + if (!renderer.Render(draw_list, cull_rect, idx)) prims_culled++; } } if (prims_culled > 0) - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); + draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); } -template -IMPLOT_INLINE void RenderLineStrip(const Getter& getter, const Transformer& transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { - ImPlotContext& gp = *GImPlot; - if (ImHasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased) || gp.Style.AntiAliasedLines) { - ImVec2 p1 = transformer(getter(0)); - for (int i = 1; i < getter.Count; ++i) { - ImVec2 p2 = transformer(getter(i)); - if (gp.CurrentPlot->PlotRect.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) - DrawList.AddLine(p1, p2, col, line_weight); - p1 = p2; - } - } - else { - RenderPrimitives(LineStripRenderer(getter, transformer, col, line_weight), DrawList, gp.CurrentPlot->PlotRect); - } +template