1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-26 20:28:50 -05:00

Merge branch 'date-picker'

This commit is contained in:
epezent 2020-09-09 19:17:39 -05:00
commit fbfd41047b
3 changed files with 573 additions and 169 deletions

View File

@ -659,17 +659,15 @@ inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
} }
ImPlotTime MkGmtTime(struct tm *ptm) { ImPlotTime MkGmtTime(struct tm *ptm) {
time_t secs = 0; ImPlotTime t;
int year = ptm->tm_year + 1900; #ifdef _WIN32
for (int y = 1970; y < year; ++y) t.S = _mkgmtime(ptm);
secs += (IsLeapYear(y)? 366: 365) * 86400; #else
for (int m = 0; m < ptm->tm_mon; ++m) t.S = timegm(ptm);
secs += GetDaysInMonth(year, m) * 86400; #endif
secs += (ptm->tm_mday - 1) * 86400; if (t.S < 0)
secs += ptm->tm_hour * 3600; t.S = 0;
secs += ptm->tm_min * 60; return t;
secs += ptm->tm_sec;
return ImPlotTime(secs,0);
} }
tm* GetGmtTime(const ImPlotTime& t, tm* ptm) tm* GetGmtTime(const ImPlotTime& t, tm* ptm)
@ -687,6 +685,8 @@ tm* GetGmtTime(const ImPlotTime& t, tm* ptm)
ImPlotTime MkLocTime(struct tm *ptm) { ImPlotTime MkLocTime(struct tm *ptm) {
ImPlotTime t; ImPlotTime t;
t.S = mktime(ptm); t.S = mktime(ptm);
if (t.S < 0)
t.S = 0;
return t; return t;
} }
@ -715,6 +715,35 @@ inline tm* GetTime(const ImPlotTime& t, tm* ptm) {
return GetGmtTime(t,ptm); return GetGmtTime(t,ptm);
} }
ImPlotTime MakeTime(int year, int month, int day, int hour, int min, int sec, int us) {
tm& Tm = GImPlot->Tm;
int yr = year - 1900;
if (yr < 0)
yr = 0;
sec = sec + us / 1000000;
us = us % 1000000;
Tm.tm_sec = sec;
Tm.tm_min = min;
Tm.tm_hour = hour;
Tm.tm_mday = day;
Tm.tm_mon = month;
Tm.tm_year = yr;
ImPlotTime t = MkTime(&Tm);
t.Us = us;
return t;
}
int GetYear(const ImPlotTime& t) {
tm& Tm = GImPlot->Tm;
GetTime(t, &Tm);
return Tm.tm_year + 1900;
}
ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) { ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
tm& Tm = GImPlot->Tm; tm& Tm = GImPlot->Tm;
ImPlotTime t_out = t; ImPlotTime t_out = t;
@ -725,16 +754,20 @@ ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
case ImPlotTimeUnit_Min: t_out.S += count * 60; break; case ImPlotTimeUnit_Min: t_out.S += count * 60; break;
case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break; case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break;
case ImPlotTimeUnit_Day: t_out.S += count * 86400; break; case ImPlotTimeUnit_Day: t_out.S += count * 86400; break;
case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) { // this might have a bug case ImPlotTimeUnit_Mo: for (int i = 0; i < abs(count); ++i) {
GetTime(t_out, &Tm); GetTime(t_out, &Tm);
t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon); if (count > 0)
t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon);
else if (count < 0)
t_out.S -= 86400 * GetDaysInMonth(Tm.tm_year + 1900 - (Tm.tm_mon == 0 ? 1 : 0), Tm.tm_mon == 0 ? 11 : Tm.tm_mon - 1); // NOT WORKING
} }
break; break;
case ImPlotTimeUnit_Yr: for (int i = 0; i < count; ++i) { case ImPlotTimeUnit_Yr: for (int i = 0; i < abs(count); ++i) {
if (IsLeapYear(GetYear(t_out))) if (count > 0)
t_out.S += 366 * 86400; t_out.S += 86400 * (365 + (int)IsLeapYear(GetYear(t_out)));
else else if (count < 0)
t_out.S += 365 * 86400; t_out.S -= 86400 * (365 + (int)IsLeapYear(GetYear(t_out) - 1));
// this is incorrect if leap year and we are past Feb 28
} }
break; break;
default: break; default: break;
@ -771,25 +804,19 @@ ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
return t.S - t1.S < t2.S - t.S ? t1 : t2; return t.S - t1.S < t2.S - t.S ? t1 : t2;
} }
int GetYear(const ImPlotTime& t) { ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& tod_part) {
tm& Tm = GImPlot->Tm; tm& Tm = GImPlot->Tm;
GetTime(t, &Tm); GetTime(date_part, &GImPlot->Tm);
return Tm.tm_year + 1900; int y = Tm.tm_year;
} int m = Tm.tm_mon;
int d = Tm.tm_mday;
ImPlotTime MakeYear(int year) { GetTime(tod_part, &GImPlot->Tm);
int yr = year - 1900; Tm.tm_year = y;
if (yr < 0) Tm.tm_mon = m;
yr = 0; Tm.tm_mday = d;
tm& Tm = GImPlot->Tm; ImPlotTime t = MkTime(&Tm);
Tm.tm_sec = 0; t.Us = tod_part.Us;
Tm.tm_min = 0; return t;
Tm.tm_hour = 0;
Tm.tm_mday = 1;
Tm.tm_mon = 0;
Tm.tm_year = yr;
Tm.tm_sec = 0;
return MkTime(&Tm);
} }
int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) { int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
@ -810,23 +837,24 @@ int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
static const char mnames[12][4] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; static const char mnames[12][4] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
switch(fmt) { switch(fmt) {
case ImPlotTimeFmt_Us: return snprintf(buffer, size, ".%03d %03d", ms, us); case ImPlotTimeFmt_Us: return snprintf(buffer, size, ".%03d %03d", ms, us);
case ImPlotTimeFmt_SUs: return snprintf(buffer, size, ":%02d.%03d %03d", sec, ms, us); case ImPlotTimeFmt_SUs: return snprintf(buffer, size, ":%02d.%03d %03d", sec, ms, us);
case ImPlotTimeFmt_SMs: return snprintf(buffer, size, ":%02d.%03d", sec, ms); case ImPlotTimeFmt_SMs: return snprintf(buffer, size, ":%02d.%03d", sec, ms);
case ImPlotTimeFmt_S: return snprintf(buffer, size, ":%02d", sec); case ImPlotTimeFmt_S: return snprintf(buffer, size, ":%02d", sec);
case ImPlotTimeFmt_HrMinS: return snprintf(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap); case ImPlotTimeFmt_HrMinS: return snprintf(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap);
case ImPlotTimeFmt_HrMin: return snprintf(buffer, size, "%d:%02d%s", hr, min, ap); case ImPlotTimeFmt_HrMin: return snprintf(buffer, size, "%d:%02d%s", hr, min, ap);
case ImPlotTimeFmt_Hr: return snprintf(buffer, size, "%d%s", hr, ap); case ImPlotTimeFmt_Hr: return snprintf(buffer, size, "%d%s", hr, ap);
case ImPlotTimeFmt_DayMo: return snprintf(buffer, size, "%d/%d", mon, day); case ImPlotTimeFmt_DayMo: return snprintf(buffer, size, "%d/%d", mon, day);
case ImPlotTimeFmt_DayMoHr: return snprintf(buffer, size, "%d/%d %d%s", mon, day, hr, ap); case ImPlotTimeFmt_DayMoHr: return snprintf(buffer, size, "%d/%d %d%s", mon, day, hr, ap);
case ImPlotTimeFmt_DayMoHrMin: return snprintf(buffer, size, "%d/%d %d:%02d%s", mon, day, hr, min, ap); case ImPlotTimeFmt_DayMoHrMin: return snprintf(buffer, size, "%d/%d %d:%02d%s", mon, day, hr, min, ap);
case ImPlotTimeFmt_DayMoYr: return snprintf(buffer, size, "%d/%d/%02d", mon, day, yr); case ImPlotTimeFmt_DayMoYr: return snprintf(buffer, size, "%d/%d/%02d", mon, day, yr);
case ImPlotTimeFmt_DayMoYrHrMin: return snprintf(buffer, size, "%d/%d/%02d %d:%02d%s", mon, day, yr, hr, min, ap); case ImPlotTimeFmt_DayMoYrHrMin: return snprintf(buffer, size, "%d/%d/%02d %d:%02d%s", mon, day, yr, hr, min, ap);
case ImPlotTimeFmt_DayMoYrHrMinS: return snprintf(buffer, size, "%d/%d/%02d %d:%02d:%02d%s", mon, day, yr, hr, min, sec, ap); case ImPlotTimeFmt_DayMoYrHrMinS: return snprintf(buffer, size, "%d/%d/%02d %d:%02d:%02d%s", mon, day, yr, hr, min, sec, ap);
case ImPlotTimeFmt_MoYr: return snprintf(buffer, size, "%s %d", mnames[Tm.tm_mon], year); case ImPlotTimeFmt_DayMoYrHrMinSUs: return snprintf(buffer, size, "%d/%d/%d %d:%02d:%02d.%03d%03d%s", mon, day, year, hr, min, sec, ms, us, ap);
case ImPlotTimeFmt_Mo: return snprintf(buffer, size, "%s", mnames[Tm.tm_mon]); case ImPlotTimeFmt_MoYr: return snprintf(buffer, size, "%s %d", mnames[Tm.tm_mon], year);
case ImPlotTimeFmt_Yr: return snprintf(buffer, size, "%d", year); case ImPlotTimeFmt_Mo: return snprintf(buffer, size, "%s", mnames[Tm.tm_mon]);
default: return 0; case ImPlotTimeFmt_Yr: return snprintf(buffer, size, "%d", year);
default: return 0;
} }
} }
@ -839,23 +867,24 @@ void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt) {
// Returns the nominally largest possible width for a time format // Returns the nominally largest possible width for a time format
inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) { inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
switch (fmt) { switch (fmt) {
case ImPlotTimeFmt_Us: return ImGui::CalcTextSize(".888 888").x; // .428 552 case ImPlotTimeFmt_Us: return ImGui::CalcTextSize(".888 888").x; // .428 552
case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888 888").x; // :29.428 552 case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888 888").x; // :29.428 552
case ImPlotTimeFmt_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428 case ImPlotTimeFmt_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428
case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29 case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29
case ImPlotTimeFmt_HrMinS: return ImGui::CalcTextSize("88:88:88pm").x; // 7:21:29pm case ImPlotTimeFmt_HrMinS: return ImGui::CalcTextSize("88:88:88pm").x; // 7:21:29pm
case ImPlotTimeFmt_HrMin: return ImGui::CalcTextSize("88:88pm").x; // 7:21pm case ImPlotTimeFmt_HrMin: return ImGui::CalcTextSize("88:88pm").x; // 7:21pm
case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("88pm").x; // 7pm case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("88pm").x; // 7pm
case ImPlotTimeFmt_DayMo: return ImGui::CalcTextSize("88/88").x; // 10/3 case ImPlotTimeFmt_DayMo: return ImGui::CalcTextSize("88/88").x; // 10/3
case ImPlotTimeFmt_DayMoHr: return ImGui::CalcTextSize("88/88 88pm").x; // 10/3 7:21pm case ImPlotTimeFmt_DayMoHr: return ImGui::CalcTextSize("88/88 88pm").x; // 10/3 7:21pm
case ImPlotTimeFmt_DayMoHrMin: return ImGui::CalcTextSize("88/88 88:88pm").x; // 10/3 7:21pm case ImPlotTimeFmt_DayMoHrMin: return ImGui::CalcTextSize("88/88 88:88pm").x; // 10/3 7:21pm
case ImPlotTimeFmt_DayMoYr: return ImGui::CalcTextSize("88/88/88").x; // 10/3/1991 case ImPlotTimeFmt_DayMoYr: return ImGui::CalcTextSize("88/88/88").x; // 10/3/1991
case ImPlotTimeFmt_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/88 88:88pm").x; // 10/3/91 7:21pm case ImPlotTimeFmt_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/88 88:88pm").x; // 10/3/91 7:21pm
case ImPlotTimeFmt_DayMoYrHrMinS: return ImGui::CalcTextSize("88/88/88 88:88:88pm").x; // 10/3/91 7:21:29pm case ImPlotTimeFmt_DayMoYrHrMinS: return ImGui::CalcTextSize("88/88/88 88:88:88pm").x; // 10/3/91 7:21:29pm
case ImPlotTimeFmt_MoYr: return ImGui::CalcTextSize("MMM 8888").x; // Oct 1991 case ImPlotTimeFmt_DayMoYrHrMinSUs: return ImGui::CalcTextSize("88/88/8888 88:88:88.888888pm").x; // 10/3/1991 7:21:29.123456pm
case ImPlotTimeFmt_Mo: return ImGui::CalcTextSize("MMM").x; // Oct case ImPlotTimeFmt_MoYr: return ImGui::CalcTextSize("MMM 8888").x; // Oct 1991
case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991 case ImPlotTimeFmt_Mo: return ImGui::CalcTextSize("MMM").x; // Oct
default: return 0; case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991
default: return 0;
} }
} }
@ -1004,7 +1033,7 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
const int step = (int)interval <= 0 ? 1 : (int)interval; const int step = (int)interval <= 0 ? 1 : (int)interval;
for (int y = graphmin; y < graphmax; y += step) { for (int y = graphmin; y < graphmax; y += step) {
ImPlotTime t = MakeYear(y); ImPlotTime t = MakeTime(y);
if (t >= t_min && t <= t_max) { if (t >= t_min && t <= t_max) {
ImPlotTick tick(t.ToDouble(), true, true); ImPlotTick tick(t.ToDouble(), true, true);
tick.Level = 0; tick.Level = 0;
@ -1661,63 +1690,115 @@ inline void EndDisabledControls(bool cond) {
} }
} }
inline void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) {
ImGui::PushItemWidth(75); ImGui::PushItemWidth(75);
bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always; ImPlotAxis& axis = *state.Axis;
bool logscale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale); bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always;
bool timescale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_Time); bool logscale = ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale);
bool grid = !ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_NoGridLines); bool timescale = ImHasFlag(axis.Flags, ImPlotAxisFlags_Time);
bool ticks = !ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_NoTickMarks); bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines);
bool labels = !ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_NoTickLabels); bool ticks = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks);
double drag_speed = (state.Axis->Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * state.Axis->Range.Size(); // recover from almost equal axis limits. bool labels = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels);
double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits.
BeginDisabledControls(total_lock); if (timescale) {
if (ImGui::Checkbox("##LockMin", &state.LockMin)) ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min);
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMin); ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max);
EndDisabledControls(total_lock);
ImGui::SameLine(); BeginDisabledControls(total_lock);
BeginDisabledControls(state.LockMin); if (ImGui::Checkbox("##LockMin", &state.LockMin))
double temp_min = state.Axis->Range.Min; ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMin);
if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, state.Axis->Range.Max - DBL_EPSILON)) EndDisabledControls(total_lock);
state.Axis->SetMin(temp_min); ImGui::SameLine();
EndDisabledControls(state.LockMin); BeginDisabledControls(state.LockMin);
if (ImGui::BeginMenu("Min Time")) {
if (ShowTimePicker("mintime", &tmin)) {
if (tmin >= tmax)
tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);
axis.SetRange(tmin.ToDouble(),tmax.ToDouble());
}
ImGui::Separator();
if (ShowDatePicker("mindate",&axis.PickerLevel,&axis.PickerTimeMin,&tmin,&tmax)) {
tmin = CombineDateTime(axis.PickerTimeMin, tmin);
if (tmin >= tmax)
tmax = AddTime(tmin, ImPlotTimeUnit_S, 1);
axis.SetRange(tmin.ToDouble(), tmax.ToDouble());
}
ImGui::EndMenu();
}
EndDisabledControls(state.LockMin);
BeginDisabledControls(total_lock); BeginDisabledControls(total_lock);
if (ImGui::Checkbox("##LockMax", &state.LockMax)) if (ImGui::Checkbox("##LockMax", &state.LockMax))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LockMax); ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMax);
EndDisabledControls(total_lock); EndDisabledControls(total_lock);
ImGui::SameLine();
BeginDisabledControls(state.LockMax);
if (ImGui::BeginMenu("Max Time")) {
if (ShowTimePicker("maxtime", &tmax)) {
if (tmax <= tmin)
tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);
axis.SetRange(tmin.ToDouble(),tmax.ToDouble());
}
ImGui::Separator();
if (ShowDatePicker("maxdate",&axis.PickerLevel,&axis.PickerTimeMax,&tmin,&tmax)) {
tmax = CombineDateTime(axis.PickerTimeMax, tmax);
if (tmax <= tmin)
tmin = AddTime(tmax, ImPlotTimeUnit_S, -1);
axis.SetRange(tmin.ToDouble(), tmax.ToDouble());
}
ImGui::EndMenu();
}
EndDisabledControls(state.LockMax);
}
else {
BeginDisabledControls(total_lock);
if (ImGui::Checkbox("##LockMin", &state.LockMin))
ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMin);
EndDisabledControls(total_lock);
ImGui::SameLine();
BeginDisabledControls(state.LockMin);
double temp_min = axis.Range.Min;
if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON))
axis.SetMin(temp_min);
EndDisabledControls(state.LockMin);
ImGui::SameLine(); BeginDisabledControls(total_lock);
BeginDisabledControls(state.LockMax); if (ImGui::Checkbox("##LockMax", &state.LockMax))
double temp_max = state.Axis->Range.Max; ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMax);
if (DragFloat("Max", &temp_max, (float)drag_speed, state.Axis->Range.Min + DBL_EPSILON, HUGE_VAL)) EndDisabledControls(total_lock);
state.Axis->SetMax(temp_max); ImGui::SameLine();
EndDisabledControls(state.LockMax); BeginDisabledControls(state.LockMax);
double temp_max = axis.Range.Max;
if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL))
axis.SetMax(temp_max);
EndDisabledControls(state.LockMax);
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::Checkbox("Invert", &state.Invert)) if (ImGui::Checkbox("Invert", &state.Invert))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Invert); ImFlipFlag(axis.Flags, ImPlotAxisFlags_Invert);
BeginDisabledControls(timescale && time_allowed); BeginDisabledControls(timescale && time_allowed);
if (ImGui::Checkbox("Log Scale", &logscale)) if (ImGui::Checkbox("Log Scale", &logscale))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale); ImFlipFlag(axis.Flags, ImPlotAxisFlags_LogScale);
EndDisabledControls(timescale && time_allowed); EndDisabledControls(timescale && time_allowed);
if (time_allowed) { if (time_allowed) {
BeginDisabledControls(logscale); BeginDisabledControls(logscale);
if (ImGui::Checkbox("Time", &timescale)) if (ImGui::Checkbox("Time", &timescale))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Time); ImFlipFlag(axis.Flags, ImPlotAxisFlags_Time);
EndDisabledControls(logscale); EndDisabledControls(logscale);
} }
ImGui::Separator(); ImGui::Separator();
if (ImGui::Checkbox("Grid Lines", &grid)) if (ImGui::Checkbox("Grid Lines", &grid))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_NoGridLines); ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines);
if (ImGui::Checkbox("Tick Marks", &ticks)) if (ImGui::Checkbox("Tick Marks", &ticks))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_NoTickMarks); ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks);
if (ImGui::Checkbox("Labels", &labels)) if (ImGui::Checkbox("Labels", &labels))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_NoTickLabels); ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels);
} }
@ -3109,6 +3190,306 @@ void ShowUserGuide() {
ImGui::BulletText("Click legend label icons to show/hide plot items."); ImGui::BulletText("Click legend label icons to show/hide plot items.");
} }
bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1, const ImPlotTime* t2) {
ImGui::PushID(id);
ImGui::BeginGroup();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));
static const char* names_mo[] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
static const char* abrvs_mo[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static const char* abrvs_wd[] = {"Su","Mo","Tu","We","Th","Fr","Sa"};
ImGuiStyle& style = ImGui::GetStyle();
ImVec4 col_txt = style.Colors[ImGuiCol_Text];
ImVec4 col_dis = style.Colors[ImGuiCol_TextDisabled];
const float ht = ImGui::GetFrameHeight();
ImVec2 cell_size(ht*1.25f,ht);
char buff[32];
bool clk = false;
tm& Tm = GImPlot->Tm;
const int min_yr = 1970;
const int max_yr = 2999;
// t1 parts
int t1_mo = 0; int t1_md = 0; int t1_yr = 0;
if (t1 != NULL) {
GetTime(*t1,&Tm);
t1_mo = Tm.tm_mon;
t1_md = Tm.tm_mday;
t1_yr = Tm.tm_year + 1900;
}
// t2 parts
int t2_mo = 0; int t2_md = 0; int t2_yr = 0;
if (t2 != NULL) {
GetTime(*t2,&Tm);
t2_mo = Tm.tm_mon;
t2_md = Tm.tm_mday;
t2_yr = Tm.tm_year + 1900;
}
// day widget
if (*level == 0) {
*t = FloorTime(*t, ImPlotTimeUnit_Day);
GetTime(*t, &Tm);
const int this_year = Tm.tm_year + 1900;
const int last_year = this_year - 1;
const int next_year = this_year + 1;
const int this_mon = Tm.tm_mon;
const int last_mon = this_mon == 0 ? 11 : this_mon - 1;
const int next_mon = this_mon == 11 ? 0 : this_mon + 1;
const int days_this_mo = GetDaysInMonth(this_year, this_mon);
const int days_last_mo = GetDaysInMonth(this_mon == 0 ? last_year : this_year, last_mon);
ImPlotTime t_first_mo = FloorTime(*t,ImPlotTimeUnit_Mo);
GetTime(t_first_mo,&Tm);
const int first_wd = Tm.tm_wday;
// month year
snprintf(buff, 32, "%s %d", names_mo[this_mon], this_year);
if (ImGui::Button(buff))
*level = 1;
ImGui::SameLine(5*cell_size.x);
BeginDisabledControls(this_year <= min_yr && this_mon == 0);
if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
*t = AddTime(*t, ImPlotTimeUnit_Mo, -1);
EndDisabledControls(this_year <= min_yr && this_mon == 0);
ImGui::SameLine();
BeginDisabledControls(this_year >= max_yr && this_mon == 11);
if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
*t = AddTime(*t, ImPlotTimeUnit_Mo, 1);
EndDisabledControls(this_year >= max_yr && this_mon == 11);
// render weekday abbreviations
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
for (int i = 0; i < 7; ++i) {
ImGui::Button(abrvs_wd[i],cell_size);
if (i != 6) { ImGui::SameLine(); }
}
ImGui::PopItemFlag();
// 0 = last mo, 1 = this mo, 2 = next mo
int mo = first_wd > 0 ? 0 : 1;
int day = mo == 1 ? 1 : days_last_mo - first_wd + 1;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 7; ++j) {
if (mo == 0 && day > days_last_mo) {
mo = 1; day = 1;
}
else if (mo == 1 && day > days_this_mo) {
mo = 2; day = 1;
}
const int now_yr = (mo == 0 && this_mon == 0) ? last_year : ((mo == 2 && this_mon == 11) ? next_year : this_year);
const int now_mo = mo == 0 ? last_mon : (mo == 1 ? this_mon : next_mon);
const int now_md = day;
const bool off_mo = mo == 0 || mo == 2;
const bool t1_or_t2 = (t1 != NULL && t1_mo == now_mo && t1_yr == now_yr && t1_md == now_md) ||
(t2 != NULL && t2_mo == now_mo && t2_yr == now_yr && t2_md == now_md);
if (off_mo)
ImGui::PushStyleColor(ImGuiCol_Text, col_dis);
if (t1_or_t2) {
ImGui::PushStyleColor(ImGuiCol_Button, col_dis);
ImGui::PushStyleColor(ImGuiCol_Text, col_txt);
}
ImGui::PushID(i*7+j);
snprintf(buff,32,"%d",day);
if (now_yr == min_yr-1 || now_yr == max_yr+1) {
ImGui::Dummy(cell_size);
}
else if (ImGui::Button(buff,cell_size) && !clk) {
*t = MakeTime(now_yr, now_mo, now_md);
clk = true;
}
ImGui::PopID();
if (t1_or_t2)
ImGui::PopStyleColor(2);
if (off_mo)
ImGui::PopStyleColor();
if (j != 6)
ImGui::SameLine();
day++;
}
}
}
// month widget
else if (*level == 1) {
*t = FloorTime(*t, ImPlotTimeUnit_Mo);
GetTime(*t, &Tm);
int this_yr = Tm.tm_year + 1900;
snprintf(buff, 32, "%d", this_yr);
if (ImGui::Button(buff))
*level = 2;
BeginDisabledControls(this_yr <= min_yr);
ImGui::SameLine(5*cell_size.x);
if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
*t = AddTime(*t, ImPlotTimeUnit_Yr, -1);
EndDisabledControls(this_yr <= min_yr);
ImGui::SameLine();
BeginDisabledControls(this_yr >= max_yr);
if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
*t = AddTime(*t, ImPlotTimeUnit_Yr, 1);
EndDisabledControls(this_yr >= max_yr);
// ImGui::Dummy(cell_size);
cell_size.x *= 7.0f/4.0f;
cell_size.y *= 7.0f/3.0f;
int mo = 0;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
const bool t1_or_t2 = (t1 != NULL && t1_yr == this_yr && t1_mo == mo) ||
(t2 != NULL && t2_yr == this_yr && t2_mo == mo);
if (t1_or_t2)
ImGui::PushStyleColor(ImGuiCol_Button, col_dis);
if (ImGui::Button(abrvs_mo[mo],cell_size) && !clk) {
*t = MakeTime(this_yr, mo);
*level = 0;
}
if (t1_or_t2)
ImGui::PopStyleColor();
if (j != 3)
ImGui::SameLine();
mo++;
}
}
}
else if (*level == 2) {
*t = FloorTime(*t, ImPlotTimeUnit_Yr);
int this_yr = GetYear(*t);
int yr = this_yr - this_yr % 20;
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
snprintf(buff,32,"%d-%d",yr,yr+19);
ImGui::Button(buff);
ImGui::PopItemFlag();
ImGui::SameLine(5*cell_size.x);
BeginDisabledControls(yr <= min_yr);
if (ImGui::ArrowButtonEx("##Up",ImGuiDir_Up,cell_size))
*t = MakeTime(yr-20);
EndDisabledControls(yr <= min_yr);
ImGui::SameLine();
BeginDisabledControls(yr + 20 >= max_yr);
if (ImGui::ArrowButtonEx("##Down",ImGuiDir_Down,cell_size))
*t = MakeTime(yr+20);
EndDisabledControls(yr+ 20 >= max_yr);
// ImGui::Dummy(cell_size);
cell_size.x *= 7.0f/4.0f;
cell_size.y *= 7.0f/5.0f;
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 4; ++j) {
const bool t1_or_t2 = (t1 != NULL && t1_yr == yr) || (t2 != NULL && t2_yr == yr);
if (t1_or_t2)
ImGui::PushStyleColor(ImGuiCol_Button, col_dis);
snprintf(buff,32,"%d",yr);
if (yr<1970||yr>3000) {
ImGui::Dummy(cell_size);
}
else if (ImGui::Button(buff,cell_size)) {
*t = MakeTime(yr);
*level = 1;
}
if (t1_or_t2)
ImGui::PopStyleColor();
if (j != 3)
ImGui::SameLine();
yr++;
}
}
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::EndGroup();
ImGui::PopID();
return clk;
}
bool ShowTimePicker(const char* id, ImPlotTime* t) {
ImGui::PushID(id);
tm& Tm = GImPlot->Tm;
GetTime(*t,&Tm);
static const char* nums[] = { "00","01","02","03","04","05","06","07","08","09",
"10","11","12","13","14","15","16","17","18","19",
"20","21","22","23","24","25","26","27","28","29",
"30","31","32","33","34","35","36","37","38","39",
"40","41","42","43","44","45","46","47","48","49",
"50","51","52","53","54","55","56","57","58","59"};
static const char* am_pm[] = {"am","pm"};
int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12;
int min = Tm.tm_min;
int sec = Tm.tm_sec;
int ap = Tm.tm_hour < 12 ? 0 : 1;
bool changed = false;
ImVec2 spacing = ImGui::GetStyle().ItemSpacing;
spacing.x = 0;
float width = ImGui::CalcTextSize("888").x;
float height = ImGui::GetFrameHeight();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing);
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize,2.0f);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));
ImGui::SetNextItemWidth(width);
if (ImGui::BeginCombo("##hr",nums[hr],ImGuiComboFlags_NoArrowButton)) {
for (int i = 1; i < 13; ++i) {
if (ImGui::Selectable(nums[i],i==hr)) {
hr = i;
changed = true;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::Text(":");
ImGui::SameLine();
ImGui::SetNextItemWidth(width);
if (ImGui::BeginCombo("##min",nums[min],ImGuiComboFlags_NoArrowButton)) {
for (int i = 0; i < 60; ++i) {
if (ImGui::Selectable(nums[i],i==min)) {
min = i;
changed = true;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::Text(":");
ImGui::SameLine();
ImGui::SetNextItemWidth(width);
if (ImGui::BeginCombo("##sec",nums[sec],ImGuiComboFlags_NoArrowButton)) {
for (int i = 0; i < 60; ++i) {
if (ImGui::Selectable(nums[i],i==sec)) {
sec = i;
changed = true;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Button(am_pm[ap],ImVec2(height,height))) {
ap = 1 - ap;
changed = true;
}
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(2);
ImGui::PopID();
if (changed) {
hr = hr % 12 + ap * 12;
Tm.tm_hour = hr;
Tm.tm_min = min;
Tm.tm_sec = sec;
*t = MkTime(&Tm);
}
return changed;
}
void StyleColorsAuto(ImPlotStyle* dst) { void StyleColorsAuto(ImPlotStyle* dst) {
ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle(); ImPlotStyle* style = dst ? dst : &ImPlot::GetStyle();
ImVec4* colors = style->Colors; ImVec4* colors = style->Colors;

View File

@ -623,7 +623,7 @@ void ShowDemoWindow(bool* p_open) {
} }
ImPlot::SetNextPlotLimits(t_min,t_max,0,1); ImPlot::SetNextPlotLimits(t_min,t_max,0,1);
if (ImPlot::BeginPlot("##Time", "Time", "Value", ImVec2(-1,0), 0, ImPlotAxisFlags_Time)) { if (ImPlot::BeginPlot("##Time", NULL, NULL, ImVec2(-1,0), 0, ImPlotAxisFlags_Time)) {
if (data != NULL) { if (data != NULL) {
// downsample our data // downsample our data
int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1; int downsample = (int)ImPlot::GetPlotLimits().X.Size() / 1000 + 1;
@ -1212,7 +1212,7 @@ void ShowDemoWindow(bool* p_open) {
ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs); ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs);
ImPlot::GetStyle().UseLocalTime = false; ImPlot::GetStyle().UseLocalTime = false;
ImPlot::SetNextPlotLimits(1546300800, 1571961600, 1250, 1600); ImPlot::SetNextPlotLimits(1546300800, 1571961600, 1250, 1600);
if (ImPlot::BeginPlot("Candlestick Chart","Day","USD",ImVec2(-1,0),0,ImPlotAxisFlags_Time)) { if (ImPlot::BeginPlot("Candlestick Chart","Day","USD",ImVec2(-1,-1),0,ImPlotAxisFlags_Time)) {
MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol); MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }

View File

@ -183,28 +183,55 @@ enum ImPlotTimeUnit_ {
}; };
enum ImPlotTimeFmt_ { enum ImPlotTimeFmt_ {
ImPlotTimeFmt_Us, // .428 552 ImPlotTimeFmt_Us, // .428 552
ImPlotTimeFmt_SUs, // :29.428 552 ImPlotTimeFmt_SUs, // :29.428 552
ImPlotTimeFmt_SMs, // :29.428 ImPlotTimeFmt_SMs, // :29.428
ImPlotTimeFmt_S, // :29 ImPlotTimeFmt_S, // :29
ImPlotTimeFmt_HrMinS, // 7:21:29pm ImPlotTimeFmt_HrMinS, // 7:21:29pm
ImPlotTimeFmt_HrMin, // 7:21pm ImPlotTimeFmt_HrMin, // 7:21pm
ImPlotTimeFmt_Hr, // 7pm ImPlotTimeFmt_Hr, // 7pm
ImPlotTimeFmt_DayMo, // 10/3 ImPlotTimeFmt_DayMo, // 10/3
ImPlotTimeFmt_DayMoHr, // 10/3 7pm ImPlotTimeFmt_DayMoHr, // 10/3 7pm
ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm
ImPlotTimeFmt_DayMoYr, // 10/3/91 ImPlotTimeFmt_DayMoYr, // 10/3/91
ImPlotTimeFmt_DayMoYrHrMin, // 10/3/91 7:21pm ImPlotTimeFmt_DayMoYrHrMin, // 10/3/91 7:21pm
ImPlotTimeFmt_DayMoYrHrMinS, // 10/3/91 7:21:29pm ImPlotTimeFmt_DayMoYrHrMinS, // 10/3/91 7:21:29pm
ImPlotTimeFmt_MoYr, // Oct 1991 ImPlotTimeFmt_DayMoYrHrMinSUs, // 10/3/1991 7:21:29.123456pm
ImPlotTimeFmt_Mo, // Oct ImPlotTimeFmt_MoYr, // Oct 1991
ImPlotTimeFmt_Yr // 1991 ImPlotTimeFmt_Mo, // Oct
ImPlotTimeFmt_Yr // 1991
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] ImPlot Structs // [SECTION] ImPlot Structs
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/// Two part timestamp struct.
struct ImPlotTime {
time_t S; // second part
int Us; // microsecond part
ImPlotTime() { S = 0; Us = 0; }
ImPlotTime(time_t s, int us = 0) { S = s + us / 1000000; Us = us % 1000000; }
void RollOver() { S = S + Us / 1000000; Us = Us % 1000000; }
double ToDouble() const { return (double)S + (double)Us / 1000000.0; }
static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); }
};
static inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); }
static inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); }
static inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs.S == rhs.S && lhs.Us == rhs.Us; }
static inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; }
static inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return rhs < lhs; }
static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs < rhs || lhs == rhs; }
static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs > rhs || lhs == rhs; }
// Storage for colormap modifiers // Storage for colormap modifiers
struct ImPlotColormapMod { struct ImPlotColormapMod {
ImPlotColormapMod(const ImVec4* colormap, int colormap_size) { ImPlotColormapMod(const ImVec4* colormap, int colormap_size) {
@ -297,6 +324,8 @@ struct ImPlotAxis
bool HoveredTot; bool HoveredTot;
double* LinkedMin; double* LinkedMin;
double* LinkedMax; double* LinkedMax;
ImPlotTime PickerTimeMin, PickerTimeMax;
int PickerLevel;
ImPlotAxis() { ImPlotAxis() {
Flags = PreviousFlags = ImPlotAxisFlags_None; Flags = PreviousFlags = ImPlotAxisFlags_None;
@ -306,6 +335,7 @@ struct ImPlotAxis
HoveredExt = false; HoveredExt = false;
HoveredTot = false; HoveredTot = false;
LinkedMin = LinkedMax = NULL; LinkedMin = LinkedMax = NULL;
PickerLevel = 0;
} }
bool SetMin(double _min) { bool SetMin(double _min) {
@ -317,6 +347,7 @@ struct ImPlotAxis
if (_min >= Range.Max) if (_min >= Range.Max)
return false; return false;
Range.Min = _min; Range.Min = _min;
PickerTimeMin = ImPlotTime::FromDouble(Range.Min);
return true; return true;
}; };
@ -329,6 +360,7 @@ struct ImPlotAxis
if (_max <= Range.Min) if (_max <= Range.Min)
return false; return false;
Range.Max = _max; Range.Max = _max;
PickerTimeMax = ImPlotTime::FromDouble(Range.Max);
return true; return true;
}; };
@ -336,6 +368,8 @@ struct ImPlotAxis
Range.Min = _min; Range.Min = _min;
Range.Max = _max; Range.Max = _max;
Constrain(); Constrain();
PickerTimeMin = ImPlotTime::FromDouble(Range.Min);
PickerTimeMax = ImPlotTime::FromDouble(Range.Max);
} }
void SetRange(const ImPlotRange& range) { void SetRange(const ImPlotRange& range) {
@ -585,32 +619,6 @@ struct ImPlotAxisScale
} }
}; };
/// Two part timestamp struct.
struct ImPlotTime {
time_t S; // second part
int Us; // microsecond part
ImPlotTime() { S = 0; Us = 0; }
ImPlotTime(time_t s, int us = 0) { S = s + us / 1000000; Us = us % 1000000; }
void RollOver() { S = S + Us / 1000000; Us = Us % 1000000; }
double ToDouble() const { return (double)S + (double)Us / 1000000.0; }
static ImPlotTime FromDouble(double t) { return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000)); }
};
static inline ImPlotTime operator+(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return ImPlotTime(lhs.S + rhs.S, lhs.Us + rhs.Us); }
static inline ImPlotTime operator-(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return ImPlotTime(lhs.S - rhs.S, lhs.Us - rhs.Us); }
static inline bool operator==(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs.S == rhs.S && lhs.Us == rhs.Us; }
static inline bool operator<(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs.S == rhs.S ? lhs.Us < rhs.Us : lhs.S < rhs.S; }
static inline bool operator>(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return rhs < lhs; }
static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs < rhs || lhs == rhs; }
static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs)
{ return lhs > rhs || lhs == rhs; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Internal API // [SECTION] Internal API
// No guarantee of forward compatibility here! // No guarantee of forward compatibility here!
@ -638,6 +646,9 @@ IMPLOT_API ImPlotState* GetCurrentPlot();
// Busts the cache for every plot in the current context // Busts the cache for every plot in the current context
IMPLOT_API void BustPlotCache(); IMPLOT_API void BustPlotCache();
// Shows a plot's context menu.
IMPLOT_API void ShowPlotContextMenu(ImPlotState& plot);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Item Utils // [SECTION] Item Utils
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -688,6 +699,9 @@ IMPLOT_API void PushLinkedAxis(ImPlotAxis& axis);
// Updates axis internal range from points for linked axes. // Updates axis internal range from points for linked axes.
IMPLOT_API void PullLinkedAxis(ImPlotAxis& axis); IMPLOT_API void PullLinkedAxis(ImPlotAxis& axis);
// Shows an axis's context menu.
IMPLOT_API void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed = false);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Legend Utils // [SECTION] Legend Utils
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -792,48 +806,57 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri
// Returns true if year is leap year (366 days long) // Returns true if year is leap year (366 days long)
inline bool IsLeapYear(int year) { inline bool IsLeapYear(int year) {
if (year % 4 != 0) return false; return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
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. #month is zero indexed. // Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed.
inline int GetDaysInMonth(int year, int month) { inline int GetDaysInMonth(int year, int month) {
static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return days[month] + (int)(month == 1 && IsLeapYear(year)); return days[month] + (int)(month == 1 && IsLeapYear(year));
} }
// Make a timestamp from a tm struct expressed as a UTC time (i.e. GMT timezone). // Make a UNIX timestamp from a tm struct expressed in UTC time (i.e. GMT timezone).
IMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm); IMPLOT_API ImPlotTime MkGmtTime(struct tm *ptm);
// Make a tm struct from a timestamp expressed as a UTC time (i.e. GMT timezone). // Make a tm struct expressed in UTC time (i.e. GMT timezone) from a UNIX timestamp.
IMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm); IMPLOT_API tm* GetGmtTime(const ImPlotTime& t, tm* ptm);
// Make a timestamp from a tm struct expressed as a local time. // Make a UNIX timestamp from a tm struct expressed in local time.
IMPLOT_API ImPlotTime MkLocTime(struct tm *ptm); IMPLOT_API ImPlotTime MkLocTime(struct tm *ptm);
// Make a tm struct from a timestamp expressed as a local time. // Make a tm struct expressed in local time from a UNIX timestamp.
IMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm); IMPLOT_API tm* GetLocTime(const ImPlotTime& t, tm* ptm);
// NB: These functions only work if there is a current ImPlotContext because the // NB: The following functions only work if there is a current ImPlotContext because the
// internal tm struct is owned by the context! // internal tm struct is owned by the context! They are aware of ImPlotStyle.UseLocalTime.
// Adds time to a timestamp. #count must be positive! // Make a timestamp from time components.
// year[1970-3000], month[0-11], day[1-31], hour[0-23], min[0-59], sec[0-59], us[0,999999]
IMPLOT_API ImPlotTime MakeTime(int year, int month = 0, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0);
// Get year component from timestamp [1970-3000]
IMPLOT_API int GetYear(const ImPlotTime& t);
// Adds or subtracts time from a timestamp. #count > 0 to add, < 0 to subtract.
IMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count); IMPLOT_API ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count);
// Rounds a timestamp down to nearest. // Rounds a timestamp down to nearest unit.
IMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit); IMPLOT_API ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Rounds a timestamp up to the nearest unit. // Rounds a timestamp up to the nearest unit.
IMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit); IMPLOT_API ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Rounds a timestamp up or down to the nearest unit. // Rounds a timestamp up or down to the nearest unit.
IMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit); IMPLOT_API ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Combines the date of one timestamp with the time-of-day of another timestamp.
IMPLOT_API ImPlotTime CombineDateTime(const ImPlotTime& date_part, const ImPlotTime& time_part);
// Get year from timestamp // Formulates a timestamp t into a buffer according to fmt.
IMPLOT_API int GetYear(const ImPlotTime& t);
// Make a timestamp starting at the first day of a year
IMPLOT_API ImPlotTime MakeYear(int year);
// Formates a timestamp t into a buffer according to fmt.
IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt); IMPLOT_API int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt);
// Prints a timestamp to console // Prints a timestamp to console
IMPLOT_API void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt); IMPLOT_API void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt = ImPlotTimeFmt_DayMoYrHrMinSUs);
// Shows a date picker widget block (year/month/day).
// #level = 0 for day, 1 for month, 2 for year. Modified by user interaction.
// #t will be set when a day is clicked and the function will return true.
// #t1 and #t2 are optional dates to highlight.
IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const ImPlotTime* t1 = NULL, const ImPlotTime* t2 = NULL);
// Shows a time picker widget block (hour/min/sec).
// #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] Internal / Experimental Plotters // [SECTION] Internal / Experimental Plotters