1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-23 02:38:53 -05:00

time axes nearly finished except for a few bugs and oddities

This commit is contained in:
epezent 2020-09-04 13:13:45 -05:00
parent 0c76ffe81e
commit 8d74440765
3 changed files with 133 additions and 57 deletions

View File

@ -575,82 +575,121 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollect
} }
} }
// splits inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) {
// mo: 6 3 2 if (max_divs < divs[0])
// day: return 0;
// hr: 12, 6, 3, 2, 1 for (int i = 1; i < size; ++i) {
// min: 30: 15, 10, 5, 1 if (max_divs < divs[i])
return step[i-1];
}
return step[size-1];
}
inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) {
static const int step[] = {500,250,200,100,50,25,20,10,5};
static const int divs[] = {2,4,5,10,20,40,50,100,200};
return LowerBoundStep(max_divs, divs, step, 9);
}
if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) {
static const int step[] = {30,15,10,5,1};
static const int divs[] = {2,4,6,12,60};
return LowerBoundStep(max_divs, divs, step, 5);
}
else if (unit == ImPlotTimeUnit_Hr) {
static const int step[] = {12,6,3,2,1};
static const int divs[] = {2,4,8,12,24};
return LowerBoundStep(max_divs, divs, step, 5);
}
else if (unit == ImPlotTimeUnit_Day) {
static const int step[] = {14,7,2,1};
static const int divs[] = {2,4,14,28};
return LowerBoundStep(max_divs, divs, step, 4);
}
else if (unit == ImPlotTimeUnit_Mo) {
static const int step[] = {6,3,2,1};
static const int divs[] = {2,4,6,12};
return LowerBoundStep(max_divs, divs, step, 4);
}
return 0;
}
void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks) { void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks) {
// 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() / (plot_width / 100)); // level = 0 (top)
const ImPlotTimeUnit unit1 = unit0 + 1; // level = 1 (bottom) const ImPlotTimeUnit unit1 = unit0 + 1; // level = 1 (bottom)
// maximum allowable density of labels // maximum allowable density of labels
const float max_density = 0.5f; const float max_density = 0.5f;
if (unit0 != ImPlotTimeUnit_Yr) {
// pixels per major (level 1) division // 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 = plot_width / (float)(range.Size() / TimeUnitSpans[unit1]);
// nominal pixels taken up by minor (level 0) label // nominal pixels taken up by minor (level 0) label
const float minor_label_width = GetTimeLabelWidth(TimeFormatLevel0[unit0]); const float minor_label_width = GetTimeLabelWidth(TimeFormatLevel0[unit0]);
// the maximum number of minor (level 0) labels that can fit between major (level 1) divisions // 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); const int minor_per_major = (int)(max_density * pix_per_major_div / minor_label_width);
// the minor step size (level 0)
if (unit1 != ImPlotTimeUnit_COUNT) { const int step = GetTimeStep(minor_per_major, unit0);
double t = FloorTime(range.Min, unit1); // generate ticks
while (t < range.Max) { double t1 = FloorTime(range.Min, unit1);
if (range.Contains(t)) { while (t1 < range.Max) {
ImPlotTick tick_maj(t,true,true); if (range.Contains(t1)) {
ImPlotTick tick_maj(t1,true,true);
tick_maj.Level = 1; tick_maj.Level = 1;
LabelTickTime(tick_maj,ticks.Labels,TimeFormatLevel1[unit1]); LabelTickTime(tick_maj,ticks.Labels,TimeFormatLevel1[unit1]);
ticks.AddTick(tick_maj); ticks.AddTick(tick_maj);
ImPlotTick tick_min(t,true,true); ImPlotTick tick_min(t1,true,true);
tick_min.Level = 0; tick_min.Level = 0;
LabelTickTime(tick_min,ticks.Labels,TimeFormatLevel0[unit0]); LabelTickTime(tick_min,ticks.Labels,TimeFormatLevel0[unit0]);
ticks.AddTick(tick_min); ticks.AddTick(tick_min);
} }
// add minor ticks up until next major // add minor ticks up until next major
if (minor_per_major > 1) { if (minor_per_major > 1) {
double t2 = AddTime(t, unit1, 1); double t2 = AddTime(t1, unit1, 1);
double inc = (t2 - t) / minor_per_major; double t12 = AddTime(t1, unit0, step);
for (int i = 1; i < minor_per_major; ++i) { while (t12 < t2) {
double t3 = t + i * inc; float px_to_t2 = (float)((t2 - t12)/range.Size()) * plot_width;
if (range.Contains(t3)) { if (range.Contains(t12)) {
ImPlotTick tick(t3,false,true); ImPlotTick tick(t12,false,px_to_t2 >= minor_label_width);
tick.Level = 0; tick.Level = 0;
LabelTickTime(tick,ticks.Labels,TimeFormatLevel0[unit0]); LabelTickTime(tick,ticks.Labels,TimeFormatLevel0[unit0]);
ticks.AddTick(tick); ticks.AddTick(tick);
} }
} t12 = AddTime(t12, unit0, step);
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); t1 = AddTime(t1, unit1, 1);
} }
} }
else { else {
// nominal pixels taken up by year label
const float label_width = GetTimeLabelWidth(TimeFormatLevel0[ImPlotTimeUnit_Yr]);
// maximum number of labels we can display
const int max_labels = (int)(max_density * plot_width / label_width);
const int year_min = GetYear(range.Min);
const int year_max = GetYear(CeilTime(range.Max, ImPlotTimeUnit_Yr));
const double nice_range = NiceNum((year_max - year_min)*0.99,false);
const double interval = NiceNum(nice_range / (max_labels - 1), true);
const int graphmin = (int)(floor(year_min / interval) * interval);
double t1 = MakeYear(graphmin);
while (t1 < range.Max) {
if (range.Contains(t1)) {
ImPlotTick tick(t1, true, true);
tick.Level = 0;
LabelTickTime(tick, ticks.Labels, TimeFormatLevel0[ImPlotTimeUnit_Yr]);
ticks.AddTick(tick);
}
t1 = AddTime(t1, ImPlotTimeUnit_Yr, (int)interval);
}
} }
// printf("%d\n",minor_per_major); // printf("%d , %d\n",minor_per_major,step);
} }
void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) { void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) {

View File

@ -592,8 +592,23 @@ void ShowDemoWindow(bool* p_open) {
} }
} }
if (ImGui::CollapsingHeader("Time Formatting")) { if (ImGui::CollapsingHeader("Time Formatting")) {
ImPlot::SetNextPlotLimits(1599106881,1599106881+1000000,0,1); static double min = 1599242863*0.5;
if (ImPlot::BeginPlot("UTC Time", "Date-Time", "Y-Axis", ImVec2(-1,0), ImPlotFlags_Default, ImPlotAxisFlags_Default | ImPlotAxisFlags_Time)) { static double max = 1599242863*1.5;
static bool zooming = false;
ImGuiCond cond = ImGuiCond_Once;
if (ImGui::Button("Zoom")) {
zooming = true;
}
if (zooming) {
cond = ImGuiCond_Always;
double range = max - min;
min += range * 0.005;
max -= range * 0.005;
if (range < 0.005)
zooming = false;
}
ImPlot::SetNextPlotLimits(min,max,0,1,cond);
if (ImPlot::BeginPlot("##Time", "UTC Time", "Y-Axis", ImVec2(-1,0), ImPlotFlags_Default, ImPlotAxisFlags_Default | ImPlotAxisFlags_Time)) {
ImPlot::EndPlot(); ImPlot::EndPlot();
} }

View File

@ -745,7 +745,7 @@ inline int GetDaysInMonth(int year, int month) {
return DaysInMonth[month] + (int)(month == 1 && IsLeapYear(year)); return DaysInMonth[month] + (int)(month == 1 && IsLeapYear(year));
} }
inline time_t MkGmTime(const struct tm *ptm) { inline time_t MakeGmTime(const struct tm *ptm) {
time_t secs = 0; time_t secs = 0;
int year = ptm->tm_year + 1900; int year = ptm->tm_year + 1900;
for (int y = 1970; y < year; ++y) { for (int y = 1970; y < year; ++y) {
@ -762,7 +762,7 @@ inline time_t MkGmTime(const struct tm *ptm) {
return secs; return secs;
} }
inline tm* GmTime(const time_t* time, tm* tm) inline tm* GetGmTime(const time_t* time, tm* tm)
{ {
#ifdef _MSC_VER #ifdef _MSC_VER
if (gmtime_s(tm, time) == 0) if (gmtime_s(tm, time) == 0)
@ -785,7 +785,7 @@ inline double AddTime(double t, ImPlotTimeUnit unit, int count) {
case ImPlotTimeUnit_Yr: count *= 12; // fall-through case ImPlotTimeUnit_Yr: count *= 12; // fall-through
case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) { case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) {
time_t s = (time_t)t; time_t s = (time_t)t;
GmTime(&s, &GImPlot->Tm); GetGmTime(&s, &GImPlot->Tm);
int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon); int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon);
t = AddTime(t, ImPlotTimeUnit_Day, days); t = AddTime(t, ImPlotTimeUnit_Day, days);
} }
@ -794,9 +794,29 @@ inline double AddTime(double t, ImPlotTimeUnit unit, int count) {
} }
} }
inline int GetYear(double t) {
time_t s = (time_t)t;
tm& Tm = GImPlot->Tm;
IM_ASSERT(GetGmTime(&s, &Tm) != NULL);
return Tm.tm_year + 1900;
}
inline double MakeYear(int year) {
int yr = year - 1900;
if (yr < 0)
yr = 0;
GImPlot->Tm = tm();
GImPlot->Tm.tm_year = yr;
GImPlot->Tm.tm_sec = 1;
time_t s = MakeGmTime(&GImPlot->Tm);
if (s < 0)
s = 0;
return (double)s;
}
inline double FloorTime(double t, ImPlotTimeUnit unit) { inline double FloorTime(double t, ImPlotTimeUnit unit) {
time_t s = (time_t)t; time_t s = (time_t)t;
GmTime(&s, &GImPlot->Tm); GetGmTime(&s, &GImPlot->Tm);
GImPlot->Tm.tm_isdst = -1; GImPlot->Tm.tm_isdst = -1;
switch (unit) { switch (unit) {
case ImPlotTimeUnit_S: return (double)s; case ImPlotTimeUnit_S: return (double)s;
@ -809,7 +829,7 @@ inline double FloorTime(double t, ImPlotTimeUnit unit) {
case ImPlotTimeUnit_Min: GImPlot->Tm.tm_sec = 0; break; case ImPlotTimeUnit_Min: GImPlot->Tm.tm_sec = 0; break;
default: return t; default: return t;
} }
s = MkGmTime(&GImPlot->Tm); s = MakeGmTime(&GImPlot->Tm);
return (double)s; return (double)s;
} }
@ -820,9 +840,11 @@ inline double CeilTime(double t, ImPlotTimeUnit unit) {
inline void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) { inline void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) {
time_t s = (time_t)t; time_t s = (time_t)t;
int ms = (int)(t * 1000 - floor(t) * 1000); int ms = (int)(t * 1000 - floor(t) * 1000);
ms = ms % 10 == 9 ? ms + 1 : ms;
int us = (int)(t * 1000000 - floor(t) * 1000000); int us = (int)(t * 1000000 - floor(t) * 1000000);
us = us % 10 == 9 ? us + 1 : us;
tm& Tm = GImPlot->Tm; tm& Tm = GImPlot->Tm;
IM_ASSERT(GmTime(&s, &Tm) != NULL); IM_ASSERT(GetGmTime(&s, &Tm) != NULL);
switch(fmt) { switch(fmt) {
case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break; case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break;
case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break; case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break;
@ -831,7 +853,7 @@ inline void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) {
if (Tm.tm_hour == 0) if (Tm.tm_hour == 0)
snprintf(buffer, size, "%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min); snprintf(buffer, size, "%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour == 12) else if (Tm.tm_hour == 12)
snprintf(buffer, size, "%d/%d 12%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min); snprintf(buffer, size, "%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour < 12) 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); 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) else if (Tm.tm_hour > 12)