mirror of
https://github.com/gwm17/implot.git
synced 2024-11-22 18:28:53 -05:00
first pass at time formatted axes
This commit is contained in:
parent
90693cca1b
commit
5f77a9bb58
33
implot.cpp
33
implot.cpp
|
@ -501,7 +501,7 @@ void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer) {
|
|||
char temp[32];
|
||||
if (tick.ShowLabel) {
|
||||
tick.BufferOffset = buffer.size();
|
||||
sprintf(temp, "%.10g", tick.PlotPos);
|
||||
snprintf(temp, 32, "%.10g", tick.PlotPos);
|
||||
buffer.append(temp, temp + strlen(temp) + 1);
|
||||
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
|
||||
}
|
||||
|
@ -511,7 +511,17 @@ void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) {
|
|||
char temp[32];
|
||||
if (tick.ShowLabel) {
|
||||
tick.BufferOffset = buffer.size();
|
||||
sprintf(temp, "%.0E", tick.PlotPos);
|
||||
snprintf(temp, 32, "%.0E", tick.PlotPos);
|
||||
buffer.append(temp, temp + strlen(temp) + 1);
|
||||
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt) {
|
||||
char temp[16];
|
||||
if (tick.ShowLabel) {
|
||||
tick.BufferOffset = buffer.size();
|
||||
FormatTime(tick.PlotPos, temp, 16, fmt);
|
||||
buffer.append(temp, temp + strlen(temp) + 1);
|
||||
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
|
||||
}
|
||||
|
@ -565,6 +575,17 @@ 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
|
@ -755,6 +776,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
|
|||
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);
|
||||
}
|
||||
|
@ -768,10 +791,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
|
|||
}
|
||||
|
||||
// plot bb
|
||||
|
||||
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);
|
||||
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_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)
|
||||
|
|
1
implot.h
1
implot.h
|
@ -76,6 +76,7 @@ enum ImPlotAxisFlags_ {
|
|||
ImPlotAxisFlags_LockMin = 1 << 4, // the axis minimum value will be locked when panning/zooming
|
||||
ImPlotAxisFlags_LockMax = 1 << 5, // the axis maximum value will be locked when panning/zooming
|
||||
ImPlotAxisFlags_LogScale = 1 << 6, // a logartithmic (base 10) axis scale will be used
|
||||
ImPlotAxisFlags_Time = 1 << 7, // axis will display data/time formatted labels
|
||||
ImPlotAxisFlags_Default = ImPlotAxisFlags_GridLines | ImPlotAxisFlags_TickMarks | ImPlotAxisFlags_TickLabels,
|
||||
ImPlotAxisFlags_Auxiliary = ImPlotAxisFlags_TickMarks | ImPlotAxisFlags_TickLabels,
|
||||
};
|
||||
|
|
|
@ -591,6 +591,13 @@ void ShowDemoWindow(bool* p_open) {
|
|||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Time Formatting")) {
|
||||
ImPlot::SetNextPlotLimits(1599106881,1599106881+1000000,0,1);
|
||||
if (ImPlot::BeginPlot("UTC Time", "Date-Time", "Y-Axis", ImVec2(-1,0), ImPlotFlags_Default, ImPlotAxisFlags_Default | ImPlotAxisFlags_Time)) {
|
||||
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
if (ImGui::CollapsingHeader("Multiple Y-Axes")) {
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#ifndef IMPLOT_VERSION
|
||||
|
@ -147,7 +148,9 @@ struct ImPlotPointArray {
|
|||
// [SECTION] ImPlot Enums
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef int ImPlotScale; // -> enum ImPlotScale_
|
||||
typedef int ImPlotScale; // -> enum ImPlotScale_
|
||||
typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_
|
||||
typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_
|
||||
|
||||
// XY axes scaling combinations
|
||||
enum ImPlotScale_ {
|
||||
|
@ -157,6 +160,29 @@ enum ImPlotScale_ {
|
|||
ImPlotScale_LogLog // log x, log y
|
||||
};
|
||||
|
||||
enum ImPlotTimeUnit_ {
|
||||
ImPlotTimeUnit_Us, // microsecond
|
||||
ImPlotTimeUnit_Ms, // millisecond
|
||||
ImPlotTimeUnit_S, // second
|
||||
ImPlotTimeUnit_Min, // minute
|
||||
ImPlotTimeUnit_Hr, // hour
|
||||
ImPlotTimeUnit_Day, // day
|
||||
ImPlotTimeUnit_Mo, // month
|
||||
ImPlotTimeUnit_Yr, // year
|
||||
ImPlotTimeUnit_COUNT
|
||||
};
|
||||
|
||||
enum ImPlotTimeFmt_ {
|
||||
ImPlotTimeFmt_SUs, // :29.428 552
|
||||
ImPlotTimeFmt_SMs, // :29.428
|
||||
ImPlotTimeFmt_S, // :29
|
||||
ImPlotTimeFmt_HrMin, // 7:21pm
|
||||
ImPlotTimeFmt_Hr, // 7pm
|
||||
ImPlotTimeFmt_MoDay, // 10/3
|
||||
ImPlotTimeFmt_Mo, // Oct
|
||||
ImPlotTimeFmt_Yr // 1991
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImPlot Structs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -272,6 +298,7 @@ struct ImPlotAxisState
|
|||
bool LockMin;
|
||||
bool LockMax;
|
||||
bool Lock;
|
||||
bool IsTime;
|
||||
|
||||
ImPlotAxisState(ImPlotAxis* axis, bool has_range, ImGuiCond range_cond, bool present) {
|
||||
Axis = axis;
|
||||
|
@ -283,6 +310,7 @@ struct ImPlotAxisState
|
|||
LockMin = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always);
|
||||
LockMax = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMax) || (HasRange && RangeCond == ImGuiCond_Always);
|
||||
Lock = !Present || ((LockMin && LockMax) || (HasRange && RangeCond == ImGuiCond_Always));
|
||||
IsTime = ImHasFlag(Axis->Flags, ImPlotAxisFlags_Time);
|
||||
}
|
||||
|
||||
ImPlotAxisState() { }
|
||||
|
@ -456,6 +484,9 @@ struct ImPlotContext {
|
|||
int ColormapSize;
|
||||
ImVector<ImPlotColormapMod> ColormapModifiers;
|
||||
|
||||
// Time
|
||||
tm Tm;
|
||||
|
||||
// Misc
|
||||
int VisibleItemCount;
|
||||
int DigitalPlotItemCnt;
|
||||
|
@ -560,15 +591,19 @@ const char* GetLegendLabel(int i);
|
|||
// [SECTION] Tick Utils
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Label a tick with default formatting
|
||||
// Label a tick with default formatting.
|
||||
void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer);
|
||||
// Label a tick with scientific formating
|
||||
// Label a tick with scientific formating.
|
||||
void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer);
|
||||
// Label a tick with time formatting.
|
||||
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);
|
||||
// Populates a list of ImPlotTicks with logarithmic space and formatted ticks
|
||||
void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks);
|
||||
// Populates a list of ImPlotTicks with time formatted ticks.
|
||||
void AddTicksTime(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks);
|
||||
// Populates a list of ImPlotTicks with custom spaced and labeled ticks
|
||||
void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks);
|
||||
|
||||
|
@ -649,6 +684,144 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri
|
|||
return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [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};
|
||||
|
||||
inline ImPlotTimeUnit GetUnitForRange(double smin, double smax) {
|
||||
double range = smax - smin;
|
||||
for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {
|
||||
if (range <= ImPlotTimeUnitSpans[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;
|
||||
if (year % 400 == 0) return true;
|
||||
if (year % 100 == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the number of days in a month, accounting for Feb. leap years.
|
||||
inline int GetDaysInMonth(int year, int month) {
|
||||
return DaysInMonth[month] + (int)(month == 1 && IsLeapYear(year));
|
||||
}
|
||||
|
||||
inline time_t MkGmTime(const struct tm *ptm) {
|
||||
time_t secs = 0;
|
||||
int year = ptm->tm_year + 1900;
|
||||
for (int y = 1970; y < year; ++y) {
|
||||
secs += (IsLeapYear(y)? 366: 365) * 86400;
|
||||
}
|
||||
for (int m = 0; m < ptm->tm_mon; ++m) {
|
||||
secs += DaysInMonth[m] * 86400;
|
||||
if (m == 1 && IsLeapYear(year)) secs += 86400;
|
||||
}
|
||||
secs += (ptm->tm_mday - 1) * 86400;
|
||||
secs += ptm->tm_hour * 3600;
|
||||
secs += ptm->tm_min * 60;
|
||||
secs += ptm->tm_sec;
|
||||
return secs;
|
||||
}
|
||||
|
||||
inline tm* GmTime(const time_t* time, tm* tm)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
if (gmtime_s(tm, time) == 0)
|
||||
return tm;
|
||||
else
|
||||
return NULL;
|
||||
#else
|
||||
return gmtime_r(time, tm);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double AddTime(double t, ImPlotTimeUnit unit, int count) {
|
||||
switch(unit) {
|
||||
case ImPlotTimeUnit_Us: return t + count * 0.000001;
|
||||
case ImPlotTimeUnit_Ms: return t + count * 0.001;
|
||||
case ImPlotTimeUnit_S: return t + count;
|
||||
case ImPlotTimeUnit_Min: return t + count * 60;
|
||||
case ImPlotTimeUnit_Hr: return t + count * 3600;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline double FloorTime(double t, ImPlotTimeUnit unit) {
|
||||
time_t s = (time_t)t;
|
||||
GmTime(&s, &GImPlot->Tm);
|
||||
GImPlot->Tm.tm_isdst = -1;
|
||||
switch (unit) {
|
||||
case ImPlotTimeUnit_S: return (double)s;
|
||||
case ImPlotTimeUnit_Ms: return floor(t * 1000) / 1000;
|
||||
case ImPlotTimeUnit_Us: return floor(t * 1000000) / 1000000;
|
||||
case ImPlotTimeUnit_Yr: GImPlot->Tm.tm_mon = 0; // fall-through
|
||||
case ImPlotTimeUnit_Mo: GImPlot->Tm.tm_mday = 1; // fall-through
|
||||
case ImPlotTimeUnit_Day: GImPlot->Tm.tm_hour = 0; // fall-through
|
||||
case ImPlotTimeUnit_Hr: GImPlot->Tm.tm_min = 0; // fall-through
|
||||
case ImPlotTimeUnit_Min: GImPlot->Tm.tm_sec = 0; break;
|
||||
default: return t;
|
||||
}
|
||||
s = MkGmTime(&GImPlot->Tm);
|
||||
return (double)s;
|
||||
}
|
||||
|
||||
inline double CeilTime(double t, ImPlotTimeUnit unit) {
|
||||
return AddTime(FloorTime(t, unit), unit, 1);
|
||||
}
|
||||
|
||||
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(fmt) {
|
||||
case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break;
|
||||
case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break;
|
||||
case ImPlotTimeFmt_MoDay: strftime(buffer, size, "%m/%d", &Tm); 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);
|
||||
break;
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case ImPlotTimeFmt_S: strftime(buffer, size, ":%S", &Tm);
|
||||
case ImPlotTimeFmt_SMs: snprintf(buffer, size, ":%02d.%d", Tm.tm_sec, ms);
|
||||
case ImPlotTimeFmt_SUs: snprintf(buffer, size, ":%02d.%d", Tm.tm_sec, us);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Internal / Experimental Plotters
|
||||
// No guarantee of forward compatibility here!
|
||||
|
|
Loading…
Reference in New Issue
Block a user