diff --git a/implot.cpp b/implot.cpp index 8e2bd70..61850c8 100644 --- a/implot.cpp +++ b/implot.cpp @@ -517,11 +517,11 @@ void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) { } } -void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeUnit unit) { - char temp[16]; +void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt) { + char temp[32]; if (tick.ShowLabel) { tick.BufferOffset = buffer.size(); - FormatTime(tick.PlotPos, temp, 16, unit); + FormatTime(tick.PlotPos, temp, 32, fmt); buffer.append(temp, temp + strlen(temp) + 1); tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset); } @@ -575,17 +575,82 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollect } } -void AddTicksTime(const ImPlotRange& range, ImPlotTickCollection& ticks) { - ImPlotTimeUnit unit_range = GetUnitForRange(range.Min, range.Max); - ImPlotTimeUnit unit_ticks = unit_range == 0 ? ImPlotTimeUnit_Us : unit_range - 1; - printf("%d\n",(int)unit_ticks); - double t = FloorTime(range.Min, unit_range); - while (t < range.Max) { - t = AddTime(t, unit_ticks, 1); - ImPlotTick tick(t,false,true); - LabelTickTime(tick,ticks.Labels,unit_ticks); - ticks.AddTick(tick); +// splits +// mo: 6 3 2 +// day: +// hr: 12, 6, 3, 2, 1 +// min: 30: 15, 10, 5, 1 + +void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks) { + const ImPlotTimeUnit unit0 = GetUnitForRange(range.Size() / (plot_width / 100)); // level = 0 (top) + const ImPlotTimeUnit unit1 = unit0 + 1; // level = 1 (bottom) + + // maximum allowable density of labels + const float max_density = 0.5f; + // pixels per major (level 1) division + const float pix_per_major_div = plot_width / (float)(range.Size() / TimeUnitSpans[unit1]); + // nominal pixels taken up by minor (level 0) label + const float minor_label_width = GetTimeLabelWidth(TimeFormatLevel0[unit0]); + // the maximum number of minor (level 0) labels that can fit between major (level 1) divisions + const int minor_per_major = (int)(max_density * pix_per_major_div / minor_label_width); + + if (unit1 != ImPlotTimeUnit_COUNT) { + double t = FloorTime(range.Min, unit1); + while (t < range.Max) { + if (range.Contains(t)) { + ImPlotTick tick_maj(t,true,true); + tick_maj.Level = 1; + LabelTickTime(tick_maj,ticks.Labels,TimeFormatLevel1[unit1]); + ticks.AddTick(tick_maj); + ImPlotTick tick_min(t,true,true); + tick_min.Level = 0; + LabelTickTime(tick_min,ticks.Labels,TimeFormatLevel0[unit0]); + ticks.AddTick(tick_min); + } + // add minor ticks up until next major + if (minor_per_major > 1) { + double t2 = AddTime(t, unit1, 1); + double inc = (t2 - t) / minor_per_major; + for (int i = 1; i < minor_per_major; ++i) { + double t3 = t + i * inc; + if (range.Contains(t3)) { + ImPlotTick tick(t3,false,true); + tick.Level = 0; + LabelTickTime(tick,ticks.Labels,TimeFormatLevel0[unit0]); + ticks.AddTick(tick); + } + } + + if (unit0 == ImPlotTimeUnit_Us) { + + } + else if (unit0 == ImPlotTimeUnit_Ms) { + + } + else if (unit0 == ImPlotTimeUnit_S) { + + } + else if (unit0 == ImPlotTimeUnit_Min) { + + } + else if (unit0 == ImPlotTimeUnit_Hr) { + + } + else if (unit0 == ImPlotTimeUnit_Day) { + + } + else if (unit0 == ImPlotTimeUnit_Mo) { + + } + } + t = AddTime(t, unit1, 1); + } } + else { + + } + + // printf("%d\n",minor_per_major); } void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) { @@ -614,6 +679,12 @@ void ConstrainAxis(ImPlotAxis& axis) { axis.Range.Min = ConstrainLog(axis.Range.Min); axis.Range.Max = ConstrainLog(axis.Range.Max); } + if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) { + axis.Range.Min = ConstrainTime(axis.Range.Min); + axis.Range.Max = ConstrainTime(axis.Range.Max); + // if (axis.Range.Size() < 0.0001) + // axis.Range.Max = axis.Range.Min + 0.0001; // TBD + } if (axis.Range.Max <= axis.Range.Min) axis.Range.Max = axis.Range.Min + DBL_EPSILON; } @@ -743,7 +814,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons UpdateAxisColors(ImPlotCol_YAxis2, &gp.Col_Y[1]); UpdateAxisColors(ImPlotCol_YAxis3, &gp.Col_Y[2]); - // BB AND HOVER ----------------------------------------------------------- + // BB, PADDING, HOVER ----------------------------------------------------------- // frame ImVec2 frame_size = ImGui::CalcItemSize(size, IMPLOT_DEFAULT_W, IMPLOT_DEFAULT_H); @@ -767,48 +838,57 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks) || ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)); for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.RenderY[i] = - gp.Y[i].Present && - (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) || - ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) || - ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)); - } - // get ticks - if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX) { - if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) - AddTicksLogarithmic(plot.XAxis.Range, (int)(gp.BB_Canvas.GetWidth() * 0.01f), gp.XTicks); - else if (gp.X.IsTime) - AddTicksTime(plot.XAxis.Range, gp.XTicks); - else - AddTicksDefault(plot.XAxis.Range, ImMax(2, (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth())), IMPLOT_SUB_DIV, gp.XTicks); - } - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) { - if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - AddTicksLogarithmic(plot.YAxis[i].Range, (int)(gp.BB_Canvas.GetHeight() * 0.02f) ,gp.YTicks[i]); - else - AddTicksDefault(plot.YAxis[i].Range, ImMax(2, (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight())), IMPLOT_SUB_DIV, gp.YTicks[i]); - } + gp.RenderY[i] = gp.Y[i].Present && + (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines) || + ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickMarks) || + ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)); } // plot bb - + + // (1) calc top/bot padding and plot height const ImVec2 title_size = ImGui::CalcTextSize(title, NULL, true); const float txt_height = ImGui::GetTextLineHeight(); - const float pad_top = title_size.x > 0.0f ? txt_height + gp.Style.LabelPadding.y : 0; - const float pad_bot = (gp.X.HasLabels ? txt_height + gp.Style.LabelPadding.y : 0) - + (x_label ? txt_height + gp.Style.LabelPadding.y : 0) - + (gp.X.IsTime ? txt_height + gp.Style.LabelPadding.y : 0); + const float pad_top = title_size.x > 0.0f ? txt_height + gp.Style.LabelPadding.y : 0; + const float pad_bot = (gp.X.HasLabels ? txt_height + gp.Style.LabelPadding.y : 0) + + (x_label ? txt_height + gp.Style.LabelPadding.y : 0) + + (gp.X.IsTime ? txt_height + gp.Style.LabelPadding.y : 0); + + const float plot_height = gp.BB_Canvas.GetHeight() - pad_top - pad_bot; + + // (2) get y tick labels (needed for left/right pad) + for (int i = 0; i < IMPLOT_Y_AXES; i++) { + if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) { + if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) + AddTicksLogarithmic(plot.YAxis[i].Range, ImMax(2, (int)IM_ROUND(plot_height * 0.02f)) ,gp.YTicks[i]); + else + AddTicksDefault(plot.YAxis[i].Range, ImMax(2, (int)IM_ROUND(0.0025 * plot_height)), IMPLOT_SUB_DIV, gp.YTicks[i]); + } + } + + // (3) calc left/right pad const float pad_left = (y_label ? txt_height + gp.Style.LabelPadding.x : 0) + (gp.Y[0].HasLabels ? gp.YTicks[0].MaxWidth + gp.Style.LabelPadding.x : 0); const float pad_right = ((gp.Y[1].Present && gp.Y[1].HasLabels) ? gp.YTicks[1].MaxWidth + gp.Style.LabelPadding.x : 0) + ((gp.Y[1].Present && gp.Y[2].Present) ? gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y : 0) + ((gp.Y[2].Present && gp.Y[2].HasLabels) ? gp.YTicks[2].MaxWidth + gp.Style.LabelPadding.x : 0); + const float plot_width = gp.BB_Canvas.GetWidth() - pad_left - pad_right; - gp.BB_Plot = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot)); - gp.Hov_Plot = gp.BB_Plot.Contains(IO.MousePos); + // (4) get x ticks + if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX) { + if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) + AddTicksLogarithmic(plot.XAxis.Range, (int)IM_ROUND(plot_width * 0.01f), gp.XTicks); + else if (gp.X.IsTime) + AddTicksTime(plot.XAxis.Range, plot_width, gp.XTicks); + else + AddTicksDefault(plot.XAxis.Range, ImMax(2, (int)IM_ROUND(0.0025 * plot_width)), IMPLOT_SUB_DIV, gp.XTicks); + } + + // (5) calc plot bb + gp.BB_Plot = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot)); + gp.Hov_Plot = gp.BB_Plot.Contains(IO.MousePos); // x axis region bb and hover const ImRect xAxisRegion_bb(gp.BB_Plot.GetBL(), ImVec2(gp.BB_Plot.Max.x, gp.BB_Frame.Max.y)); @@ -1132,10 +1212,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min); for (int t = 0; t < gp.XTicks.Size; t++) { ImPlotTick& xt = gp.XTicks.Ticks[t]; - if (xt.Major) - DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), gp.Col_X.Major, gp.Style.MajorGridSize.x); - else if (density < 0.2f) - DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), col_min32, gp.Style.MinorGridSize.x); + if (xt.Level == 0) { + if (xt.Major) + DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), gp.Col_X.Major, gp.Style.MajorGridSize.x); + else if (density < 0.2f) + DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), col_min32, gp.Style.MinorGridSize.x); + } } } @@ -1182,7 +1264,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons for (int t = 0; t < gp.XTicks.Size; t++) { ImPlotTick *xt = &gp.XTicks.Ticks[t]; if (xt->ShowLabel && xt->PixelPos >= gp.BB_Plot.Min.x - 1 && xt->PixelPos <= gp.BB_Plot.Max.x + 1) - DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, gp.BB_Plot.Max.y + gp.Style.LabelPadding.y), xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetLabel(t)); + DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, gp.BB_Plot.Max.y + gp.Style.LabelPadding.y + xt->Level * (txt_height + gp.Style.LabelPadding.y)), + xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetLabel(t)); } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { @@ -1241,6 +1324,7 @@ inline void ShowAxisContextMenu(ImPlotAxisState& state) { ImGui::PushItemWidth(75); bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always; bool logscale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale); + bool timesale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_Time); bool grid = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines); bool ticks = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_TickMarks); bool labels = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_TickLabels); @@ -1272,6 +1356,8 @@ inline void ShowAxisContextMenu(ImPlotAxisState& state) { ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Invert); if (ImGui::Checkbox("Log Scale", &logscale)) ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale); + if (ImGui::Checkbox("Time", ×ale)) + ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Time); ImGui::Separator(); if (ImGui::Checkbox("Grid Lines", &grid)) ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines); diff --git a/implot_internal.h b/implot_internal.h index e6cdda5..6bd32da 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -77,6 +77,8 @@ extern ImPlotContext* GImPlot; // Current implicit context pointer #define IMPLOT_SUB_DIV 10 // Zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click) #define IMPLOT_ZOOM_RATE 0.1f +// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) +#define IMPLOT_MAX_TIME 32503680000 //----------------------------------------------------------------------------- // [SECTION] Generic Helpers @@ -150,7 +152,7 @@ struct ImPlotPointArray { typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ -typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ +typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_ // XY axes scaling combinations enum ImPlotScale_ { @@ -160,18 +162,6 @@ enum ImPlotScale_ { ImPlotScale_LogLog // log x, log y }; -enum ImPlotTimeUnit_ { - ImPlotTimeUnit_Us, // microsecond (:29.428 552) - ImPlotTimeUnit_Ms, // millisecond (:29.428) - ImPlotTimeUnit_S, // second (:29) - ImPlotTimeUnit_Min, // minute (7:21pm) - ImPlotTimeUnit_Hr, // hour (7pm) - ImPlotTimeUnit_Day, // day (10/3) - ImPlotTimeUnit_Mo, // month (Oct) - ImPlotTimeUnit_Yr, // year (1991) - ImPlotTimeUnit_COUNT -}; - //----------------------------------------------------------------------------- // [SECTION] ImPlot Structs //----------------------------------------------------------------------------- @@ -205,12 +195,14 @@ struct ImPlotTick int BufferOffset; bool Major; bool ShowLabel; + int Level; ImPlotTick(double value, bool major, bool show_label) { PlotPos = value; Major = major; ShowLabel = show_label; BufferOffset = -1; + Level = 0; } }; @@ -585,7 +577,7 @@ void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer); // Label a tick with scientific formating. void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer); // Label a tick with time formatting. -void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeUnit fmt); +void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt); // Populates a list of ImPlotTicks with normal spaced and formatted ticks void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks); @@ -642,6 +634,8 @@ inline double ConstrainNan(double val) { return isnan(val) ? 0 : val; } inline double ConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : val == -HUGE_VAL ? - DBL_MAX : val; } // Turns numbers less than or equal to 0 to 0.001 (sort of arbitrary, is there a better way?) inline double ConstrainLog(double val) { return val <= 0 ? 0.001f : val; } +// Turns numbers less than 0 to zero +inline double ConstrainTime(double val) { return val < 0 ? 0 : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } // Computes order of magnitude of double. inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(floor(log10(fabs(val)))); } // Returns the precision required for a order of magnitude. @@ -677,18 +671,67 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri // [SECTION] Time Utils //----------------------------------------------------------------------------- -static const int DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static const double ImPlotTimeUnitSpans[ImPlotTimeUnit_COUNT] = {0.000001, 0.001, 1, 60, 3600, 86400, 2629800, 31557600}; +enum ImPlotTimeUnit_ { // primary + ImPlotTimeUnit_Us, // microsecond :29.428552 + ImPlotTimeUnit_Ms, // millisecond :29.428 + ImPlotTimeUnit_S, // second :29 + ImPlotTimeUnit_Min, // minute 7:21pm + ImPlotTimeUnit_Hr, // hour 7pm + ImPlotTimeUnit_Day, // day 10/3 + ImPlotTimeUnit_Mo, // month Oct + ImPlotTimeUnit_Yr, // year 1991 + ImPlotTimeUnit_COUNT +}; -inline ImPlotTimeUnit GetUnitForRange(double smin, double smax) { - double range = smax - smin; +enum ImPlotTimeFmt_ { + ImPlotTimeFmt_SUs, // :29.428552 + ImPlotTimeFmt_SMs, // :29.428 + ImPlotTimeFmt_S, // :29 + ImPlotTimeFmt_HrMin, // 7:21pm + ImPlotTimeFmt_Hr, // 7pm + ImPlotTimeFmt_DayMo, // 10/3 + ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm + ImPlotTimeFmt_DayMoYrHrMin, // 10/3/1991 7:21pm + ImPlotTimeFmt_Mo, // Oct + ImPlotTimeFmt_Yr // 1991 +}; + +static const int DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {0.000001, 0.001, 1, 60, 3600, 86400, 2629800, 31557600}; +static const ImPlotTimeFmt TimeFormatLevel0[ImPlotTimeUnit_COUNT] = +{ + ImPlotTimeFmt_SUs, + ImPlotTimeFmt_SMs, + ImPlotTimeFmt_S, + ImPlotTimeFmt_HrMin, + ImPlotTimeFmt_Hr, + ImPlotTimeFmt_DayMo, + ImPlotTimeFmt_Mo, + ImPlotTimeFmt_Yr +}; +static const ImPlotTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] = +{ + ImPlotTimeFmt_DayMoHrMin, + ImPlotTimeFmt_DayMoHrMin, + ImPlotTimeFmt_DayMoHrMin, + ImPlotTimeFmt_DayMoHrMin, + ImPlotTimeFmt_DayMo, + ImPlotTimeFmt_DayMo, + ImPlotTimeFmt_Yr, + ImPlotTimeFmt_Yr +}; + + +inline ImPlotTimeUnit GetUnitForRange(double range) { + static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME}; for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) { - if (range <= ImPlotTimeUnitSpans[i]) + if (range <= cutoffs[i]) return (ImPlotTimeUnit)i; } return ImPlotTimeUnit_Yr; } + // Returns true if year is leap year (366 days long) inline bool IsLeapYear(int year) { if (year % 4 != 0) return false; @@ -741,13 +784,13 @@ inline double AddTime(double t, ImPlotTimeUnit unit, int count) { case ImPlotTimeUnit_Day: return t + count * 86400; case ImPlotTimeUnit_Yr: count *= 12; // fall-through case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) { - time_t s = (time_t)t; - GmTime(&s, &GImPlot->Tm); - int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon); - t = AddTime(t, ImPlotTimeUnit_Day, days); - } - return t; - default: return t; + time_t s = (time_t)t; + GmTime(&s, &GImPlot->Tm); + int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon); + t = AddTime(t, ImPlotTimeUnit_Day, days); + } + return t; + default: return t; } } @@ -774,43 +817,86 @@ inline double CeilTime(double t, ImPlotTimeUnit unit) { return AddTime(FloorTime(t, unit), unit, 1); } -inline void FormatTime(double t, char* buffer, int size, ImPlotTimeUnit unit) { +inline void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) { time_t s = (time_t)t; int ms = (int)(t * 1000 - floor(t) * 1000); int us = (int)(t * 1000000 - floor(t) * 1000000); tm& Tm = GImPlot->Tm; - GmTime(&s, &Tm); - switch(unit) { - case ImPlotTimeUnit_Yr: strftime(buffer, size, "%Y", &Tm); break; - case ImPlotTimeUnit_Mo: strftime(buffer, size, "%b", &Tm); break; - case ImPlotTimeUnit_Day: strftime(buffer, size, "%m/%d", &Tm); break; - case ImPlotTimeUnit_Hr: + IM_ASSERT(GmTime(&s, &Tm) != NULL); + switch(fmt) { + case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break; + case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break; + case ImPlotTimeFmt_DayMo: snprintf(buffer, size, "%d/%d", Tm.tm_mon + 1, Tm.tm_mday); break; + case ImPlotTimeFmt_DayMoHrMin: + if (Tm.tm_hour == 0) + snprintf(buffer, size, "%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min); + else if (Tm.tm_hour == 12) + snprintf(buffer, size, "%d/%d 12%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min); + else if (Tm.tm_hour < 12) + snprintf(buffer, size, "%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour, Tm.tm_min); + else if (Tm.tm_hour > 12) + snprintf(buffer, size, "%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour - 12, Tm.tm_min); + break; + case ImPlotTimeFmt_DayMoYrHrMin: + if (Tm.tm_hour == 0) + snprintf(buffer, size, "%d/%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min); + else if (Tm.tm_hour == 12) + snprintf(buffer, size, "%d/%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min); + else if (Tm.tm_hour < 12) + snprintf(buffer, size, "%d/%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour, Tm.tm_min); + else if (Tm.tm_hour > 12) + snprintf(buffer, size, "%d/%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour - 12, Tm.tm_min); + break; + case ImPlotTimeFmt_Hr: if (Tm.tm_hour == 0) snprintf(buffer, size, "12am"); else if (Tm.tm_hour == 12) snprintf(buffer, size, "12pm"); else if (Tm.tm_hour < 12) snprintf(buffer, size, "%uam", Tm.tm_hour); - else if (Tm.tm_hour > 12) - snprintf(buffer, size, "%upm", Tm.tm_hour - 12); + else if (Tm.tm_hour > 12) + snprintf(buffer, size, "%upm", Tm.tm_hour - 12); break; - case ImPlotTimeUnit_Min: + case ImPlotTimeFmt_HrMin: if (Tm.tm_hour == 0) snprintf(buffer, size, "12:%02dam", Tm.tm_min); else if (Tm.tm_hour == 12) - snprintf(buffer, size, "12%02dpm", Tm.tm_min); + snprintf(buffer, size, "12:%02dpm", Tm.tm_min); else if (Tm.tm_hour < 12) - snprintf(buffer, size, "%u:%02dam", Tm.tm_hour, Tm.tm_min); - else if (Tm.tm_hour > 12) - snprintf(buffer, size, "%u:%02dpm", Tm.tm_hour - 12, Tm.tm_min); + snprintf(buffer, size, "%d:%02dam", Tm.tm_hour, Tm.tm_min); + else if (Tm.tm_hour > 12) + snprintf(buffer, size, "%d:%02dpm", Tm.tm_hour - 12, Tm.tm_min); break; - case ImPlotTimeUnit_S: snprintf(buffer, size, ":%02d", Tm.tm_sec); break; - case ImPlotTimeUnit_Ms: snprintf(buffer, size, ":%02d.%03d", Tm.tm_sec, ms); break; - case ImPlotTimeUnit_Us: snprintf(buffer, size, ":%02d.%06d", Tm.tm_sec, us); break; + case ImPlotTimeFmt_S: snprintf(buffer, size, ":%02d", Tm.tm_sec); break; + case ImPlotTimeFmt_SMs: snprintf(buffer, size, ":%02d.%03d", Tm.tm_sec, ms); break; + case ImPlotTimeFmt_SUs: snprintf(buffer, size, ":%02d.%06d", Tm.tm_sec, us); break; default: break; } } +inline void PrintTime(double t, ImPlotTimeFmt fmt) { + char buff[32]; + FormatTime(t, buff, 32, fmt); + printf("%s\n",buff); +} + +// Returns the nominally largest possible width for a time format +inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) { + switch (fmt) { + case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888888").x; // :29.428552 + case ImPlotTimeFmt_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428 + case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29 + case ImPlotTimeFmt_HrMin: return ImGui::CalcTextSize("88:88pm").x; // 7:21pm + case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("8pm").x; // 7pm + case ImPlotTimeFmt_DayMo: return ImGui::CalcTextSize("88/88").x; // 10/3 + case ImPlotTimeFmt_DayMoHrMin: return ImGui::CalcTextSize("88/88 88:88pm").x; // 10/3 7:21pm + case ImPlotTimeFmt_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/8888 88:88pm").x; // 10/3/1991 7:21pm + case ImPlotTimeFmt_Mo: return ImGui::CalcTextSize("MMM").x; // Oct + case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991 + default: return 0; + } +} + //----------------------------------------------------------------------------- // [SECTION] Internal / Experimental Plotters // No guarantee of forward compatibility here!