1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-13 22:48:50 -05:00

time-axes complete

This commit is contained in:
epezent 2020-09-06 00:06:51 -05:00
parent d3ea373cc7
commit defc529219
3 changed files with 164 additions and 126 deletions

View File

@ -671,23 +671,30 @@ inline tm* GetGmTime(const time_t* time, tm* tm)
}
ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
tm& Tm = GImPlot->Tm;
ImPlotTime t_out = t;
switch(unit) {
case ImPlotTimeUnit_Us: return ImPlotTime(t.S, t.Us + count);
case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, t.Us + count * 1000);
case ImPlotTimeUnit_Us: t_out.Us += count; break;
case ImPlotTimeUnit_Ms: t_out.Us += count * 1000; break;
case ImPlotTimeUnit_S: t_out.S += count; break;
case ImPlotTimeUnit_Min: t_out.S += count * 60; break;
case ImPlotTimeUnit_Hr: t_out.S += count * 3600; break;
case ImPlotTimeUnit_Day: t_out.S += count * 86400; break;
case ImPlotTimeUnit_Yr: count *= 12; // fall-through
case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) {
GetGmTime(&t.S, &GImPlot->Tm);
int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon);
t_out = AddTime(t_out, ImPlotTimeUnit_Day, days);
case ImPlotTimeUnit_Mo: for (int i = 0; i < count; ++i) { // this might have a bug
GetGmTime(&t.S, &Tm);
t_out.S += 86400 * GetDaysInMonth(Tm.tm_year + 1900, Tm.tm_mon);
}
break;
case ImPlotTimeUnit_Yr: for (int i = 0; i < count; ++i) {
if (IsLeapYear(GetYear(t_out)))
t_out.S += 366 * 86400;
else
t_out.S += 365 * 86400;
}
break;
default: break;
}
t_out.RollOver();
return t_out;
}
@ -730,13 +737,17 @@ ImPlotTime 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;
tm& Tm = GImPlot->Tm;
Tm.tm_sec = 0;
Tm.tm_min = 0;
Tm.tm_hour = 0;
Tm.tm_mday = 1;
Tm.tm_mon = 0;
Tm.tm_year = yr;
Tm.tm_sec = 0;
Tm.tm_isdst = -1;
time_t s = MakeGmTime(&Tm);
return ImPlotTime(s);
}
int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
@ -763,12 +774,13 @@ int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
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_HrMin: return snprintf(buffer, size, "%d:%02d%s", hr, min, ap);
case ImPlotTimeFmt_Hr: return snprintf(buffer, size, "%u%s", hr, ap);
case ImPlotTimeFmt_DayMo: return snprintf(buffer, size, "%d/%d", mon, day);
case ImPlotTimeFmt_DayMoHrMin: return snprintf(buffer, size, "%d/%d %u:%02d%s", mon, day, hr, min, ap);
case ImPlotTimeFmt_DayMoYr: return snprintf(buffer, size, "%d/%d/%d", mon, day, yr);
case ImPlotTimeFmt_DayMoYrHrMin: return snprintf(buffer, size, "%d/%d/%d %u:%02d%s", mon, day, yr, hr, min, ap);
case ImPlotTimeFmt_DayMoYrHrMinS: return snprintf(buffer, size, "%d/%d/%d %u:%02d:%02d%s", mon, day, yr, hr, min, sec, 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_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_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_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_Mo: return snprintf(buffer, size, "%s", mnames[Tm.tm_mon]);
case ImPlotTimeFmt_Yr: return snprintf(buffer, size, "%d", year);
@ -776,7 +788,7 @@ int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
}
}
void PrintTime(double t, ImPlotTimeFmt fmt) {
void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt) {
static char buff[32];
FormatTime(t, buff, 32, fmt);
printf("%s\n",buff);
@ -791,8 +803,9 @@ inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29
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_Hr: return ImGui::CalcTextSize("8pm").x; // 7pm
case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("88pm").x; // 7pm
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_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_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/88 88:88pm").x; // 10/3/91 7:21pm
@ -804,16 +817,23 @@ inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
}
}
inline void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt) {
inline void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotTimeFmt fmt) {
char temp[32];
if (tick.ShowLabel) {
tick.BufferOffset = buffer.size();
FormatTime(tick.PlotPos, temp, 32, fmt);
FormatTime(t, temp, 32, fmt);
buffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
}
}
inline bool TimeLabelSame(const char* l1, const char* l2) {
size_t len1 = strlen(l1);
size_t len2 = strlen(l2);
size_t n = len1 < len2 ? len1 : len2;
return strcmp(l1 + len1 - n, l2 + len2 - n) == 0;
}
static const ImPlotTimeFmt TimeFormatLevel0[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_Us,
ImPlotTimeFmt_SMs,
@ -830,9 +850,9 @@ static const ImPlotTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_HrMinS,
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_MoYr,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_Yr
};
@ -843,82 +863,86 @@ static const ImPlotTimeFmt TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_DayMoYrHrMin,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_MoYr,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_Yr
};
static const ImPlotTimeFmt TimeFormatMouseCursor[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_Us,
ImPlotTimeFmt_SUs,
ImPlotTimeFmt_SMs,
ImPlotTimeFmt_HrMinS,
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_DayMoHr,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_MoYr
};
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 unit1 = unit0 + 1; // level = 1 (bottom)
// get time format specs
const ImPlotTimeFmt fmt0 = TimeFormatLevel0[unit0];
const ImPlotTimeFmt fmt1 = TimeFormatLevel1[unit1];
const ImPlotTimeFmt fmtf = TimeFormatLevel1First[unit1];
const ImPlotTime t_min(range.Min);
const ImPlotTime t_max(range.Max);
// min max times
const ImPlotTime t_min = ImPlotTime::FromDouble(range.Min);
const ImPlotTime t_max = ImPlotTime::FromDouble(range.Max);
// maximum allowable density of labels
const float max_density = 0.5f;
const float max_density = 0.5f;
// book keeping
bool first = true;
const char* last_major = NULL;
const char* last_major = NULL;
if (unit0 != ImPlotTimeUnit_Yr) {
// 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(fmt0);
// nominal pixels taken up by labels
const float fmt0_width = GetTimeLabelWidth(fmt0);
const float fmt1_width = GetTimeLabelWidth(fmt1);
const float fmtf_width = GetTimeLabelWidth(fmtf);
// 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 / fmt0_width);
// the minor step size (level 0)
const int step = GetTimeStep(minor_per_major, unit0);
// generate ticks
ImPlotTime t1 = FloorTime(ImPlotTime(range.Min), unit1);
t1 = t1;
ImPlotTime t1 = FloorTime(ImPlotTime::FromDouble(range.Min), unit1);
while (t1 < t_max) {
// get next major
const ImPlotTime t2 = AddTime(t1, unit1, 1);
// 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.Labels,fmt0);
LabelTickTime(tick_min,ticks.Labels,t1,fmt0);
ticks.AddTick(tick_min);
// major level 1 tick
ImPlotTick tick_maj(t1.ToDouble(),true,true);
tick_maj.Level = 1;
LabelTickTime(tick_maj,ticks.Labels,first ? fmtf : fmt1);
const char* this_major = ticks.Labels.Buf.Data + tick_maj.BufferOffset;
if (last_major == NULL)
last_major = this_major;
else if (strcmp(last_major,this_major) == 0)
tick_maj.ShowLabel = false;
else
last_major = this_major;
ticks.AddTick(tick_maj);
if (first) first = false;
LabelTickTime(tick_maj,ticks.Labels,t1, last_major == NULL ? fmtf : fmt1);
const char* this_major = ticks.Labels.Buf.Data + tick_maj.BufferOffset;
if (last_major && TimeLabelSame(last_major,this_major))
tick_maj.ShowLabel = false;
last_major = this_major;
ticks.AddTick(tick_maj);
}
// add minor ticks up until next major
const ImPlotTime t2 = AddTime(t1, unit1, 1);
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;
if (t12 >= t_min && t12 <= t_max) {
ImPlotTick tick(t12.ToDouble(),false,px_to_t2 >= minor_label_width);
ImPlotTick tick(t12.ToDouble(),false,px_to_t2 >= fmt0_width);
tick.Level = 0;
LabelTickTime(tick,ticks.Labels,fmt0);
LabelTickTime(tick,ticks.Labels,t12,fmt0);
ticks.AddTick(tick);
// if (first) {
// ImPlotTick tick_maj(t12,true,true);
// tick_maj.Level = 1;
// LabelTickTime(tick_maj,ticks.Labels,TimeFormatLevel1[unit1]);
// ticks.AddTick(tick_maj);
// first = false;
// }
if (last_major == NULL && 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.Labels,t12,fmtf);
last_major = ticks.Labels.Buf.Data + tick_maj.BufferOffset;
ticks.AddTick(tick_maj);
}
}
t12 = AddTime(t12, unit0, step);
}
@ -929,22 +953,23 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
else {
const float label_width = GetTimeLabelWidth(TimeFormatLevel0[ImPlotTimeUnit_Yr]);
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 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);
const double interval = NiceNum(nice_range / (max_labels - 1), true);
const int graphmin = (int)(floor(year_min / interval) * interval);
const int graphmax = (int)(ceil(year_max / interval) * interval);
const int step = (int)interval <= 0 ? 1 : (int)interval;
ImPlotTime t1 = MakeYear(graphmin);
while (t1 < range.Max) {
if (t1 >= t_min && t1 <= t_max) {
ImPlotTick tick(t1.ToDouble(), true, true);
for (int y = graphmin; y < graphmax; y += step) {
ImPlotTime t = MakeYear(y);
if (t >= t_min && t <= t_max) {
ImPlotTick tick(t.ToDouble(), true, true);
tick.Level = 0;
LabelTickTime(tick, ticks.Labels, TimeFormatLevel0[ImPlotTimeUnit_Yr]);
LabelTickTime(tick, ticks.Labels, t, TimeFormatLevel0[ImPlotTimeUnit_Yr]);
ticks.AddTick(tick);
}
t1 = AddTime(t1, ImPlotTimeUnit_Yr, step);
}
}
}
}
@ -966,10 +991,14 @@ void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col) {
// BeginPlot()
//-----------------------------------------------------------------------------
bool BeginPlot(const char* title, const char* x_label, const char* y_label, const ImVec2& size, ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags) {
bool BeginPlot(const char* title, const char* x_label, const char* y_label, const ImVec2& size,
ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags)
{
IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?");
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!");
IM_ASSERT_USER_ERROR(!(ImHasFlag(x_flags, ImPlotAxisFlags_Time) && ImHasFlag(x_flags, ImPlotAxisFlags_LogScale)), "ImPlotAxisFlags_Time and ImPlotAxisFlags_LogScale cannot be enabled at the same time!");
IM_ASSERT_USER_ERROR(!ImHasFlag(y_flags, ImPlotAxisFlags_Time), "Y axes cannot display time formatted labels!");
// FRONT MATTER -----------------------------------------------------------
@ -1002,6 +1031,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// There's probably an easy bit mask trick I'm not aware of.
if (flags != plot.PreviousFlags)
plot.Flags = flags;
if (x_flags != plot.XAxis.PreviousFlags)
plot.XAxis.Flags = x_flags;
if (y_flags != plot.YAxis[0].PreviousFlags)
plot.YAxis[0].Flags = y_flags;
if (y2_flags != plot.YAxis[1].PreviousFlags)
@ -1106,9 +1137,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
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_bot = (gp.X.HasLabels ? txt_height + gp.Style.LabelPadding.y + (gp.X.IsTime ? txt_height + gp.Style.LabelPadding.y : 0) : 0)
+ (x_label ? txt_height + gp.Style.LabelPadding.y : 0);
const float plot_height = gp.BB_Canvas.GetHeight() - pad_top - pad_bot;
@ -1573,14 +1603,14 @@ inline void EndDisabledControls(bool cond) {
}
}
inline void ShowAxisContextMenu(ImPlotAxisState& state) {
inline void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) {
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);
bool logscale = ImHasFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
bool timescale = 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);
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.
BeginDisabledControls(total_lock);
@ -1611,10 +1641,18 @@ inline void ShowAxisContextMenu(ImPlotAxisState& state) {
if (ImGui::Checkbox("Invert", &state.Invert))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Invert);
BeginDisabledControls(timescale && time_allowed);
if (ImGui::Checkbox("Log Scale", &logscale))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_LogScale);
// if (ImGui::Checkbox("Time", &timesale))
// ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Time);
EndDisabledControls(timescale && time_allowed);
if (time_allowed) {
BeginDisabledControls(logscale);
if (ImGui::Checkbox("Time", &timescale))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_Time);
EndDisabledControls(logscale);
}
ImGui::Separator();
if (ImGui::Checkbox("Grid Lines", &grid))
ImFlipFlag(state.Axis->Flags, ImPlotAxisFlags_GridLines);
@ -1629,7 +1667,7 @@ void ShowPlotContextMenu(ImPlotState& plot) {
ImPlotContext& gp = *GImPlot;
if (ImGui::BeginMenu("X-Axis")) {
ImGui::PushID("X");
ShowAxisContextMenu(gp.X);
ShowAxisContextMenu(gp.X, true);
ImGui::PopID();
ImGui::EndMenu();
}
@ -1648,7 +1686,7 @@ void ShowPlotContextMenu(ImPlotState& plot) {
}
if (ImGui::BeginMenu(buf)) {
ImGui::PushID(i);
ShowAxisContextMenu(gp.Y[i]);
ShowAxisContextMenu(gp.Y[i], false);
ImGui::PopID();
ImGui::EndMenu();
}
@ -1721,10 +1759,11 @@ void EndPlot() {
if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks)) {
for (int t = 0; t < gp.XTicks.Size; t++) {
ImPlotTick *xt = &gp.XTicks.Ticks[t];
DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Plot.Max.y),
ImVec2(xt->PixelPos, gp.BB_Plot.Max.y - (xt->Major ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x)),
gp.Col_X.Major,
xt->Major ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x);
if (xt->Level == 0)
DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Plot.Max.y),
ImVec2(xt->PixelPos, gp.BB_Plot.Max.y - (xt->Major ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x)),
gp.Col_X.Major,
xt->Major ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x);
}
}
PopPlotClipRect();
@ -1916,6 +1955,12 @@ void EndPlot() {
if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) {
writer.Write("%.3E", gp.MousePos[0].x);
}
else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Time)) {
ImPlotTimeUnit unit = GetUnitForRange(plot.XAxis.Range.Size() / (gp.BB_Plot.GetWidth() / 100));
const int written = FormatTime(ImPlotTime::FromDouble(gp.MousePos[0].x), &writer.Buffer[writer.Pos], writer.Size - writer.Pos - 1, TimeFormatMouseCursor[unit]);
if (written > 0)
writer.Pos += ImMin(written, writer.Size - writer.Pos - 1);
}
else {
double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : plot.XAxis.Range.Size();
writer.Write("%.*f", Precision(range_x), gp.MousePos[0].x);
@ -2001,7 +2046,7 @@ void EndPlot() {
ImGui::OpenPopup("##XContext");
if (ImGui::BeginPopup("##XContext")) {
ImGui::Text("X-Axis"); ImGui::Separator();
ShowAxisContextMenu(gp.X);
ShowAxisContextMenu(gp.X, true);
ImGui::EndPopup();
}
@ -2016,7 +2061,7 @@ void EndPlot() {
else {
ImGui::Text("Y-Axis %d", i + 1); ImGui::Separator();
}
ShowAxisContextMenu(gp.Y[i]);
ShowAxisContextMenu(gp.Y[i], false);
ImGui::EndPopup();
}
ImGui::PopID();

View File

@ -152,7 +152,7 @@ void ShowDemoWindow(bool* p_open) {
return;
}
ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(530, 750), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver);
ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("Tools")) {
@ -591,23 +591,10 @@ void ShowDemoWindow(bool* p_open) {
ImPlot::EndPlot();
}
}
if (ImGui::CollapsingHeader("Time Formatting")) {
static double min = 1599242863*0.5;
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 (ImGui::CollapsingHeader("Time Formatted Axes")) {
static double min = 1577836800; // 01/01/2020 @ 12:00:00am (UTC)
static double max = 1609459200; // 01/01/2021 @ 12:00:00am (UTC)
ImPlot::SetNextPlotLimits(min,max,0,1);
if (ImPlot::BeginPlot("##Time", "UTC Time", "Y-Axis", ImVec2(-1,0), ImPlotFlags_Default, ImPlotAxisFlags_Default | ImPlotAxisFlags_Time)) {
ImPlot::EndPlot();
@ -615,8 +602,6 @@ void ShowDemoWindow(bool* p_open) {
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Multiple Y-Axes")) {
static t_float xs[1001], xs2[1001], ys1[1001], ys2[1001], ys3[1001];
for (int i = 0; i < 1001; ++i) {
xs[i] = (i*0.1f);
@ -1294,7 +1279,7 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
// custom tool
if (ImPlot::IsPlotHovered() && tooltip) {
ImPlotPoint mouse = ImPlot::GetPlotMousePos();
mouse.x = ImPlot::RoundTime(ImPlotTime(mouse.x), ImPlotTimeUnit_Day).ToDouble();
mouse.x = ImPlot::RoundTime(ImPlotTime::FromDouble(mouse.x), ImPlotTimeUnit_Day).ToDouble();
float tool_l = ImPlot::PlotToPixels(mouse.x - half_width * 1.5, mouse.y).x;
float tool_r = ImPlot::PlotToPixels(mouse.x + half_width * 1.5, mouse.y).x;
float tool_t = ImPlot::GetPlotPos().y;
@ -1308,7 +1293,7 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
if (idx != -1) {
ImGui::BeginTooltip();
char buff[32];
ImPlot::FormatTime(xs[idx],buff,32,ImPlotTimeFmt_DayMoYr);
ImPlot::FormatTime(ImPlotTime::FromDouble(xs[idx]),buff,32,ImPlotTimeFmt_DayMoYr);
ImGui::Text("Day: %s", buff);
ImGui::Text("Open: $%.2f", opens[idx]);
ImGui::Text("Close: $%.2f", closes[idx]);

View File

@ -126,10 +126,10 @@ struct ImOffsetCalculator {
struct ImBufferWriter
{
char* Buffer;
size_t Size;
size_t Pos;
int Size;
int Pos;
ImBufferWriter(char* buffer, size_t size) {
ImBufferWriter(char* buffer, int size) {
Buffer = buffer;
Size = size;
Pos = 0;
@ -140,7 +140,7 @@ struct ImBufferWriter
va_start(argp, fmt);
const int written = ::vsnprintf(&Buffer[Pos], Size - Pos - 1, fmt, argp);
if (written > 0)
Pos += ImMin(size_t(written), Size-Pos-1);
Pos += ImMin(written, Size-Pos-1);
va_end(argp);
}
};
@ -191,6 +191,7 @@ enum ImPlotTimeFmt_ {
ImPlotTimeFmt_HrMin, // 7:21pm
ImPlotTimeFmt_Hr, // 7pm
ImPlotTimeFmt_DayMo, // 10/3
ImPlotTimeFmt_DayMoHr, // 10/3 7pm
ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm
ImPlotTimeFmt_DayMoYr, // 10/3/91
ImPlotTimeFmt_DayMoYrHrMin, // 10/3/91 7:21pm
@ -580,13 +581,17 @@ struct ImPlotAxisScale
struct ImPlotTime {
time_t S;
int Us;
ImPlotTime(time_t s, int us) {
ImPlotTime() { S = 0; Us = 0; }
ImPlotTime(time_t s, int us = 0) {
S = s + us / 1000000;
Us = us % 1000000;
}
ImPlotTime(double t) {
S = (time_t)t;
Us = (int)(t * 1000000 - floor(t) * 1000000);
void RollOver() {
S = S + Us / 1000000;
Us = Us % 1000000;
}
static ImPlotTime FromDouble(double t) {
return ImPlotTime((time_t)t, (int)(t * 1000000 - floor(t) * 1000000));
}
double ToDouble() const { return (double)S + (double)Us / 1000000.0; }
};
@ -696,7 +701,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, ImPlotTimeFmt fmt);
void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotTimeFmt fmt);
// Populates a list of ImPlotTicks with normal spaced and formatted ticks
void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks);
@ -780,6 +785,9 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri
// Time Utils
//-----------------------------------------------------------------------------
// NB: These functions only work if there is a currnet context because the
// internal tm struct is owned by the context!
// Returns true if year is leap year (366 days long)
inline bool IsLeapYear(int year) {
if (year % 4 != 0) return false;
@ -787,7 +795,7 @@ inline bool IsLeapYear(int year) {
if (year % 100 == 0) return false;
return true;
}
// Returns the number of days in a month, accounting for Feb. leap years.
// Returns the number of days in a month, accounting for Feb. leap years. #month is zero indexed.
inline int GetDaysInMonth(int year, int month) {
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));