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

tidy up time-axes so far

This commit is contained in:
epezent 2020-09-04 23:30:45 -05:00
parent e0450d00af
commit 61e6b5118e
3 changed files with 320 additions and 287 deletions

View File

@ -517,16 +517,6 @@ void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) {
}
}
void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt) {
char temp[32];
if (tick.ShowLabel) {
tick.BufferOffset = buffer.size();
FormatTime(tick.PlotPos, temp, 32, fmt);
buffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
}
}
void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks) {
const double nice_range = NiceNum(range.Size() * 0.99, false);
const double interval = NiceNum(nice_range / (nMajor - 1), true);
@ -575,6 +565,68 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollect
}
}
void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) {
for (int i = 0; i < n; ++i) {
ImPlotTick tick(values[i], false, true);
if (labels != NULL) {
tick.BufferOffset = ticks.Labels.size();
ticks.Labels.append(labels[i], labels[i] + strlen(labels[i]) + 1);
tick.LabelSize = ImGui::CalcTextSize(labels[i]);
}
else {
LabelTickDefault(tick, ticks.Labels);
}
ticks.AddTick(tick);
}
}
//-----------------------------------------------------------------------------
// Time Ticks and Utils
//-----------------------------------------------------------------------------
// this may not be thread safe?
static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {
0.000001,
0.001,
1,
60,
3600,
86400,
2629800,
31557600
};
static const ImPlotTimeFmt TimeFormatLevel0[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_SUs,
ImPlotTimeFmt_SMs,
ImPlotTimeFmt_S,
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_Hr,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_Mo,
ImPlotTimeFmt_Yr
};
static const ImPlotTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_Yr
};
inline ImPlotTimeUnit GetUnitForRange(double range) {
static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME};
for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {
if (range <= cutoffs[i])
return (ImPlotTimeUnit)i;
}
return ImPlotTimeUnit_Yr;
}
inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int size) {
if (max_divs < divs[0])
return 0;
@ -614,16 +666,200 @@ inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
return 0;
}
inline time_t MakeGmTime(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 += GetDaysInMonth(year, m) * 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* GetGmTime(const time_t* time, tm* tm)
{
#ifdef _WIN32
if (gmtime_s(tm, time) == 0)
return tm;
else
return NULL;
#else
return gmtime_r(time, tm);
#endif
}
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;
GetGmTime(&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;
}
}
double FloorTime(double t, ImPlotTimeUnit unit) {
time_t s = (time_t)t;
GetGmTime(&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 = MakeGmTime(&GImPlot->Tm);
return (double)s;
}
double CeilTime(double t, ImPlotTimeUnit unit) {
return AddTime(FloorTime(t, unit), unit, 1);
}
double RoundTime(double t, ImPlotTimeUnit unit) {
double t1 = FloorTime(t, unit);
double t2 = AddTime(t1,unit,1);
return t - t1 < t2 - t ? t1 : t2;
}
int GetYear(double t) {
time_t s = (time_t)t;
tm& Tm = GImPlot->Tm;
GetGmTime(&s, &Tm);
return Tm.tm_year + 1900;
}
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;
}
void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) {
time_t s = (time_t)t;
int ms = (int)(t * 1000 - floor(t) * 1000);
ms = ms % 10 == 9 ? ms + 1 : ms;
int us = (int)(t * 1000000 - floor(t) * 1000000);
us = us % 10 == 9 ? us + 1 : us;
tm& Tm = GImPlot->Tm;
GetGmTime(&s, &Tm);
switch(fmt) {
case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break;
case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break;
case ImPlotTimeFmt_DayMo: snprintf(buffer, size, "%d/%d", Tm.tm_mon + 1, Tm.tm_mday); break;
case ImPlotTimeFmt_DayMoHrMin:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_DayMoYr: snprintf(buffer, size, "%d/%d/%d", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900); break;
case ImPlotTimeFmt_DayMoYrHrMin:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "%d/%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min);
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "%d/%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min);
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%d/%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d/%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_Hr:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "12am");
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "12pm");
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%uam", Tm.tm_hour);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%upm", Tm.tm_hour - 12);
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, "%d:%02dam", Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d:%02dpm", Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_S: snprintf(buffer, size, ":%02d", Tm.tm_sec); break;
case ImPlotTimeFmt_SMs: snprintf(buffer, size, ":%02d.%03d", Tm.tm_sec, ms); break;
case ImPlotTimeFmt_SUs: snprintf(buffer, size, ":%02d.%06d", Tm.tm_sec, us); break;
default: break;
}
}
void PrintTime(double t, ImPlotTimeFmt fmt) {
static char buff[32];
FormatTime(t, buff, 32, fmt);
printf("%s\n",buff);
}
// Returns the nominally largest possible width for a time format
inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
switch (fmt) {
case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888888").x; // :29.428552
case ImPlotTimeFmt_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428
case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29
case ImPlotTimeFmt_HrMin: return ImGui::CalcTextSize("88:88pm").x; // 7:21pm
case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("8pm").x; // 7pm
case ImPlotTimeFmt_DayMo: return ImGui::CalcTextSize("88/88").x; // 10/3
case ImPlotTimeFmt_DayMoHrMin: return ImGui::CalcTextSize("88/88 88:88pm").x; // 10/3 7:21pm
case ImPlotTimeFmt_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/8888 88:88pm").x; // 10/3/1991 7:21pm
case ImPlotTimeFmt_Mo: return ImGui::CalcTextSize("MMM").x; // Oct
case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991
default: return 0;
}
}
inline void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, ImPlotTimeFmt fmt) {
char temp[32];
if (tick.ShowLabel) {
tick.BufferOffset = buffer.size();
FormatTime(tick.PlotPos, temp, 32, fmt);
buffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset);
}
}
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)
// maximum allowable density of labels
const float max_density = 0.5f;
const float max_density = 0.66f;
if (unit0 != ImPlotTimeUnit_Yr) {
// pixels per major (level 1) division
const float pix_per_major_div = plot_width / (float)(range.Size() / TimeUnitSpans[unit1]);
@ -647,8 +883,9 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
ticks.AddTick(tick_min);
}
// add minor ticks up until next major
if (minor_per_major > 1) {
double t2 = AddTime(t1, unit1, 1);
const double t2 = AddTime(t1, unit1, 1);
const ImPlotRange r12(t1,t2);
if (minor_per_major > 1 && RangesOverlap(range,r12)) {
double t12 = AddTime(t1, unit0, step);
while (t12 < t2) {
float px_to_t2 = (float)((t2 - t12)/range.Size()) * plot_width;
@ -661,22 +898,18 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
t12 = AddTime(t12, unit0, step);
}
}
t1 = AddTime(t1, unit1, 1);
t1 = t2;
}
}
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 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);
const int step = (int)interval <= 0 ? 1 : (int)interval;
double t1 = MakeYear(graphmin);
while (t1 < range.Max) {
if (range.Contains(t1)) {
@ -685,26 +918,9 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
LabelTickTime(tick, ticks.Labels, TimeFormatLevel0[ImPlotTimeUnit_Yr]);
ticks.AddTick(tick);
}
t1 = AddTime(t1, ImPlotTimeUnit_Yr, (int)interval);
t1 = AddTime(t1, ImPlotTimeUnit_Yr, step);
}
}
// printf("%d , %d\n",minor_per_major,step);
}
void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks) {
for (int i = 0; i < n; ++i) {
ImPlotTick tick(values[i], false, true);
if (labels != NULL) {
tick.BufferOffset = ticks.Labels.size();
ticks.Labels.append(labels[i], labels[i] + strlen(labels[i]) + 1);
tick.LabelSize = ImGui::CalcTextSize(labels[i]);
}
else {
LabelTickDefault(tick, ticks.Labels);
}
ticks.AddTick(tick);
}
}
//-----------------------------------------------------------------------------
@ -1730,27 +1946,29 @@ void EndPlot() {
if (gp.FitThisFrame && (gp.VisibleItemCount > 0 || plot.Queried)) {
if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsX.Min)) {
plot.XAxis.SetMin(gp.ExtentsX.Min);
plot.XAxis.Range.Min = (gp.ExtentsX.Min);
}
if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsX.Max)) {
plot.XAxis.SetMax(gp.ExtentsX.Max);
plot.XAxis.Range.Max = (gp.ExtentsX.Max);
}
if ((plot.XAxis.Range.Max - plot.XAxis.Range.Min) <= (2.0 * FLT_EPSILON)) {
plot.XAxis.Range.Max += FLT_EPSILON;
plot.XAxis.Range.Min -= FLT_EPSILON;
}
plot.XAxis.Constrain();
// ConstrainAxis(plot.XAxis);
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsY[i].Min)) {
plot.YAxis[i].SetMin(gp.ExtentsY[i].Min);
plot.YAxis[i].Range.Min = (gp.ExtentsY[i].Min);
}
if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsY[i].Max)) {
plot.YAxis[i].SetMax(gp.ExtentsY[i].Max);
plot.YAxis[i].Range.Max = (gp.ExtentsY[i].Max);
}
if ((plot.YAxis[i].Range.Max - plot.YAxis[i].Range.Min) <= (2.0 * FLT_EPSILON)) {
plot.YAxis[i].Range.Max += FLT_EPSILON;
plot.YAxis[i].Range.Min -= FLT_EPSILON;
}
plot.YAxis[i].Constrain();
// ConstrainAxis(plot.YAxis[i]);
}
}

View File

@ -1138,7 +1138,7 @@ void ShowDemoWindow(bool* p_open) {
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Custom Plotters and Tooltips")) {
ImGui::BulletText("You can create custom plotters or extend ImPlot using implot_internal.h.");
double dates[] = {0,1,2,3,4,5,6,7,8,9,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,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217};
double dates[] = {1546300800,1546387200,1546473600,1546560000,1546819200,1546905600,1546992000,1547078400,1547164800,1547424000,1547510400,1547596800,1547683200,1547769600,1547942400,1548028800,1548115200,1548201600,1548288000,1548374400,1548633600,1548720000,1548806400,1548892800,1548979200,1549238400,1549324800,1549411200,1549497600,1549584000,1549843200,1549929600,1550016000,1550102400,1550188800,1550361600,1550448000,1550534400,1550620800,1550707200,1550793600,1551052800,1551139200,1551225600,1551312000,1551398400,1551657600,1551744000,1551830400,1551916800,1552003200,1552262400,1552348800,1552435200,1552521600,1552608000,1552867200,1552953600,1553040000,1553126400,1553212800,1553472000,1553558400,1553644800,1553731200,1553817600,1554076800,1554163200,1554249600,1554336000,1554422400,1554681600,1554768000,1554854400,1554940800,1555027200,1555286400,1555372800,1555459200,1555545600,1555632000,1555891200,1555977600,1556064000,1556150400,1556236800,1556496000,1556582400,1556668800,1556755200,1556841600,1557100800,1557187200,1557273600,1557360000,1557446400,1557705600,1557792000,1557878400,1557964800,1558051200,1558310400,1558396800,1558483200,1558569600,1558656000,1558828800,1558915200,1559001600,1559088000,1559174400,1559260800,1559520000,1559606400,1559692800,1559779200,1559865600,1560124800,1560211200,1560297600,1560384000,1560470400,1560729600,1560816000,1560902400,1560988800,1561075200,1561334400,1561420800,1561507200,1561593600,1561680000,1561939200,1562025600,1562112000,1562198400,1562284800,1562544000,1562630400,1562716800,1562803200,1562889600,1563148800,1563235200,1563321600,1563408000,1563494400,1563753600,1563840000,1563926400,1564012800,1564099200,1564358400,1564444800,1564531200,1564617600,1564704000,1564963200,1565049600,1565136000,1565222400,1565308800,1565568000,1565654400,1565740800,1565827200,1565913600,1566172800,1566259200,1566345600,1566432000,1566518400,1566777600,1566864000,1566950400,1567036800,1567123200,1567296000,1567382400,1567468800,1567555200,1567641600,1567728000,1567987200,1568073600,1568160000,1568246400,1568332800,1568592000,1568678400,1568764800,1568851200,1568937600,1569196800,1569283200,1569369600,1569456000,1569542400,1569801600,1569888000,1569974400,1570060800,1570147200,1570406400,1570492800,1570579200,1570665600,1570752000,1571011200,1571097600,1571184000,1571270400,1571356800,1571616000,1571702400,1571788800,1571875200,1571961600};
double opens[] = {1284.7,1319.9,1318.7,1328,1317.6,1321.6,1314.3,1325,1319.3,1323.1,1324.7,1321.3,1323.5,1322,1281.3,1281.95,1311.1,1315,1314,1313.1,1331.9,1334.2,1341.3,1350.6,1349.8,1346.4,1343.4,1344.9,1335.6,1337.9,1342.5,1337,1338.6,1337,1340.4,1324.65,1324.35,1349.5,1371.3,1367.9,1351.3,1357.8,1356.1,1356,1347.6,1339.1,1320.6,1311.8,1314,1312.4,1312.3,1323.5,1319.1,1327.2,1332.1,1320.3,1323.1,1328,1330.9,1338,1333,1335.3,1345.2,1341.1,1332.5,1314,1314.4,1310.7,1314,1313.1,1315,1313.7,1320,1326.5,1329.2,1314.2,1312.3,1309.5,1297.4,1293.7,1277.9,1295.8,1295.2,1290.3,1294.2,1298,1306.4,1299.8,1302.3,1297,1289.6,1302,1300.7,1303.5,1300.5,1303.2,1306,1318.7,1315,1314.5,1304.1,1294.7,1293.7,1291.2,1290.2,1300.4,1284.2,1284.25,1301.8,1295.9,1296.2,1304.4,1323.1,1340.9,1341,1348,1351.4,1351.4,1343.5,1342.3,1349,1357.6,1357.1,1354.7,1361.4,1375.2,1403.5,1414.7,1433.2,1438,1423.6,1424.4,1418,1399.5,1435.5,1421.25,1434.1,1412.4,1409.8,1412.2,1433.4,1418.4,1429,1428.8,1420.6,1441,1460.4,1441.7,1438.4,1431,1439.3,1427.4,1431.9,1439.5,1443.7,1425.6,1457.5,1451.2,1481.1,1486.7,1512.1,1515.9,1509.2,1522.3,1513,1526.6,1533.9,1523,1506.3,1518.4,1512.4,1508.8,1545.4,1537.3,1551.8,1549.4,1536.9,1535.25,1537.95,1535.2,1556,1561.4,1525.6,1516.4,1507,1493.9,1504.9,1506.5,1513.1,1506.5,1509.7,1502,1506.8,1521.5,1529.8,1539.8,1510.9,1511.8,1501.7,1478,1485.4,1505.6,1511.6,1518.6,1498.7,1510.9,1510.8,1498.3,1492,1497.7,1484.8,1494.2,1495.6,1495.6,1487.5,1491.1,1495.1,1506.4};
double highs[] = {1284.75,1320.6,1327,1330.8,1326.8,1321.6,1326,1328,1325.8,1327.1,1326,1326,1323.5,1322.1,1282.7,1282.95,1315.8,1316.3,1314,1333.2,1334.7,1341.7,1353.2,1354.6,1352.2,1346.4,1345.7,1344.9,1340.7,1344.2,1342.7,1342.1,1345.2,1342,1350,1324.95,1330.75,1369.6,1374.3,1368.4,1359.8,1359,1357,1356,1353.4,1340.6,1322.3,1314.1,1316.1,1312.9,1325.7,1323.5,1326.3,1336,1332.1,1330.1,1330.4,1334.7,1341.1,1344.2,1338.8,1348.4,1345.6,1342.8,1334.7,1322.3,1319.3,1314.7,1316.6,1316.4,1315,1325.4,1328.3,1332.2,1329.2,1316.9,1312.3,1309.5,1299.6,1296.9,1277.9,1299.5,1296.2,1298.4,1302.5,1308.7,1306.4,1305.9,1307,1297.2,1301.7,1305,1305.3,1310.2,1307,1308,1319.8,1321.7,1318.7,1316.2,1305.9,1295.8,1293.8,1293.7,1304.2,1302,1285.15,1286.85,1304,1302,1305.2,1323,1344.1,1345.2,1360.1,1355.3,1363.8,1353,1344.7,1353.6,1358,1373.6,1358.2,1369.6,1377.6,1408.9,1425.5,1435.9,1453.7,1438,1426,1439.1,1418,1435,1452.6,1426.65,1437.5,1421.5,1414.1,1433.3,1441.3,1431.4,1433.9,1432.4,1440.8,1462.3,1467,1443.5,1444,1442.9,1447,1437.6,1440.8,1445.7,1447.8,1458.2,1461.9,1481.8,1486.8,1522.7,1521.3,1521.1,1531.5,1546.1,1534.9,1537.7,1538.6,1523.6,1518.8,1518.4,1514.6,1540.3,1565,1554.5,1556.6,1559.8,1541.9,1542.9,1540.05,1558.9,1566.2,1561.9,1536.2,1523.8,1509.1,1506.2,1532.2,1516.6,1519.7,1515,1519.5,1512.1,1524.5,1534.4,1543.3,1543.3,1542.8,1519.5,1507.2,1493.5,1511.4,1525.8,1522.2,1518.8,1515.3,1518,1522.3,1508,1501.5,1503,1495.5,1501.1,1497.9,1498.7,1492.1,1499.4,1506.9,1520.9};
double lows[] = {1282.85,1315,1318.7,1309.6,1317.6,1312.9,1312.4,1319.1,1319,1321,1318.1,1321.3,1319.9,1312,1280.5,1276.15,1308,1309.9,1308.5,1312.3,1329.3,1333.1,1340.2,1347,1345.9,1338,1340.8,1335,1332,1337.9,1333,1336.8,1333.2,1329.9,1340.4,1323.85,1324.05,1349,1366.3,1351.2,1349.1,1352.4,1350.7,1344.3,1338.9,1316.3,1308.4,1306.9,1309.6,1306.7,1312.3,1315.4,1319,1327.2,1317.2,1320,1323,1328,1323,1327.8,1331.7,1335.3,1336.6,1331.8,1311.4,1310,1309.5,1308,1310.6,1302.8,1306.6,1313.7,1320,1322.8,1311,1312.1,1303.6,1293.9,1293.5,1291,1277.9,1294.1,1286,1289.1,1293.5,1296.9,1298,1299.6,1292.9,1285.1,1288.5,1296.3,1297.2,1298.4,1298.6,1302,1300.3,1312,1310.8,1301.9,1292,1291.1,1286.3,1289.2,1289.9,1297.4,1283.65,1283.25,1292.9,1295.9,1290.8,1304.2,1322.7,1336.1,1341,1343.5,1345.8,1340.3,1335.1,1341.5,1347.6,1352.8,1348.2,1353.7,1356.5,1373.3,1398,1414.7,1427,1416.4,1412.7,1420.1,1396.4,1398.8,1426.6,1412.85,1400.7,1406,1399.8,1404.4,1415.5,1417.2,1421.9,1415,1413.7,1428.1,1434,1435.7,1427.5,1429.4,1423.9,1425.6,1427.5,1434.8,1422.3,1412.1,1442.5,1448.8,1468.2,1484.3,1501.6,1506.2,1498.6,1488.9,1504.5,1518.3,1513.9,1503.3,1503,1506.5,1502.1,1503,1534.8,1535.3,1541.4,1528.6,1525.6,1535.25,1528.15,1528,1542.6,1514.3,1510.7,1505.5,1492.1,1492.9,1496.8,1493.1,1503.4,1500.9,1490.7,1496.3,1505.3,1505.3,1517.9,1507.4,1507.1,1493.3,1470.5,1465,1480.5,1501.7,1501.4,1493.3,1492.1,1505.1,1495.7,1478,1487.1,1480.8,1480.6,1487,1488.3,1484.8,1484,1490.7,1490.4,1503.1};
@ -1150,8 +1150,8 @@ void ShowDemoWindow(bool* p_open) {
static ImVec4 bearCol = ImVec4(0.853f, 0.050f, 0.310f, 1.000f);
ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs);
ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs);
ImPlot::SetNextPlotLimits(0, 218, 1250, 1600);
if (ImPlot::BeginPlot("Candlestick Chart","Day","USD")) {
ImPlot::SetNextPlotLimits(1546300800, 1571961600, 1250, 1600);
if (ImPlot::BeginPlot("Candlestick Chart","Day","USD",ImVec2(-1,-1),ImPlotFlags_Default, ImPlotAxisFlags_Default | ImPlotAxisFlags_Time)) {
MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol);
ImPlot::EndPlot();
}
@ -1287,7 +1287,7 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
// begin plot item
if (ImPlot::BeginItem(label_id)) {
// override legend icon color
ImPlot::GetCurrentItem()->Color = ImVec4(1,1,1,1);
ImPlot::GetCurrentItem()->Color = ImVec4(0.25f,0.25f,0.25f,1);
// fit data if requested
if (ImPlot::FitThisFrame()) {
for (int i = 0; i < count; ++i) {
@ -1312,7 +1312,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 = round(mouse.x);
mouse.x = ImPlot::RoundTime(mouse.x, ImPlotTimeUnit_Day);
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;
@ -1323,7 +1323,9 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
// render tool tip (won't be affected by plot clip rect)
if (idx != -1) {
ImGui::BeginTooltip();
ImGui::Text("Day: %.0f", xs[idx]);
char buff[32];
ImPlot::FormatTime(xs[idx],buff,32,ImPlotTimeFmt_DayMoYr);
ImGui::Text("Day: %s", buff);
ImGui::Text("Open: $%.2f", opens[idx]);
ImGui::Text("Close: $%.2f", closes[idx]);
ImGui::Text("Low: $%.2f", lows[idx]);

View File

@ -170,6 +170,32 @@ enum ImPlotScale_ {
ImPlotScale_LogLog // log x, log y
};
enum ImPlotTimeUnit_ { // primary
ImPlotTimeUnit_Us, // microsecond :29.428552
ImPlotTimeUnit_Ms, // millisecond :29.428
ImPlotTimeUnit_S, // second :29
ImPlotTimeUnit_Min, // minute 7:21pm
ImPlotTimeUnit_Hr, // hour 7pm
ImPlotTimeUnit_Day, // day 10/3
ImPlotTimeUnit_Mo, // month Oct
ImPlotTimeUnit_Yr, // year 1991
ImPlotTimeUnit_COUNT
};
enum ImPlotTimeFmt_ {
ImPlotTimeFmt_SUs, // :29.428552
ImPlotTimeFmt_SMs, // :29.428
ImPlotTimeFmt_S, // :29
ImPlotTimeFmt_HrMin, // 7:21pm
ImPlotTimeFmt_Hr, // 7pm
ImPlotTimeFmt_DayMo, // 10/3
ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm
ImPlotTimeFmt_DayMoYr, // 10/3/1991
ImPlotTimeFmt_DayMoYrHrMin, // 10/3/1991 7:21pm
ImPlotTimeFmt_Mo, // Oct
ImPlotTimeFmt_Yr // 1991
};
//-----------------------------------------------------------------------------
// [SECTION] ImPlot Structs
//-----------------------------------------------------------------------------
@ -612,6 +638,9 @@ inline bool FitThisFrame() { return GImPlot->FitThisFrame; }
// Extends the current plots axes so that it encompasses point p
void FitPoint(const ImPlotPoint& p);
// Returns true if two ranges overlap
inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) { return r1.Min <= r2.Max && r2.Min <= r1.Max; }
//-----------------------------------------------------------------------------
// [SECTION] Legend Utils
//-----------------------------------------------------------------------------
@ -711,256 +740,40 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri
}
//-----------------------------------------------------------------------------
// [SECTION] Time Utils
// Time Utils
//-----------------------------------------------------------------------------
enum ImPlotTimeUnit_ { // primary
ImPlotTimeUnit_Us, // microsecond :29.428552
ImPlotTimeUnit_Ms, // millisecond :29.428
ImPlotTimeUnit_S, // second :29
ImPlotTimeUnit_Min, // minute 7:21pm
ImPlotTimeUnit_Hr, // hour 7pm
ImPlotTimeUnit_Day, // day 10/3
ImPlotTimeUnit_Mo, // month Oct
ImPlotTimeUnit_Yr, // year 1991
ImPlotTimeUnit_COUNT
};
enum ImPlotTimeFmt_ {
ImPlotTimeFmt_SUs, // :29.428552
ImPlotTimeFmt_SMs, // :29.428
ImPlotTimeFmt_S, // :29
ImPlotTimeFmt_HrMin, // 7:21pm
ImPlotTimeFmt_Hr, // 7pm
ImPlotTimeFmt_DayMo, // 10/3
ImPlotTimeFmt_DayMoHrMin, // 10/3 7:21pm
ImPlotTimeFmt_DayMoYrHrMin, // 10/3/1991 7:21pm
ImPlotTimeFmt_Mo, // Oct
ImPlotTimeFmt_Yr // 1991
};
static const int DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const double TimeUnitSpans[ImPlotTimeUnit_COUNT] = {0.000001, 0.001, 1, 60, 3600, 86400, 2629800, 31557600};
static const ImPlotTimeFmt TimeFormatLevel0[ImPlotTimeUnit_COUNT] =
{
ImPlotTimeFmt_SUs,
ImPlotTimeFmt_SMs,
ImPlotTimeFmt_S,
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_Hr,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_Mo,
ImPlotTimeFmt_Yr
};
static const ImPlotTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] =
{
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMoHrMin,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_Yr
};
inline ImPlotTimeUnit GetUnitForRange(double range) {
static double cutoffs[ImPlotTimeUnit_COUNT] = {0.001, 1, 60, 3600, 86400, 2629800, 31557600, IMPLOT_MAX_TIME};
for (int i = 0; i < ImPlotTimeUnit_COUNT; ++i) {
if (range <= cutoffs[i])
return (ImPlotTimeUnit)i;
}
return ImPlotTimeUnit_Yr;
}
// Returns true if year is leap year (366 days long)
inline bool IsLeapYear(int year) {
if (year % 4 != 0) return false;
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));
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));
}
inline time_t MakeGmTime(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;
}
// Adds time to a timestamp. #count must be positive!
double AddTime(double t, ImPlotTimeUnit unit, int count);
// Rounds a timestamp down to nearest.
double FloorTime(double t, ImPlotTimeUnit unit);
// Rounds a timestamp up to the nearest unit.
double CeilTime(double t, ImPlotTimeUnit unit);
// Rounds a timestamp up or down to the nearest unit.
double RoundTime(double t, ImPlotTimeUnit unit);
inline tm* GetGmTime(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
}
// Get year from timestamp
int GetYear(double t);
// Make a timestamp starting at the first day of a year
double MakeYear(int year);
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;
GetGmTime(&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 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) {
time_t s = (time_t)t;
GetGmTime(&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 = MakeGmTime(&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);
ms = ms % 10 == 9 ? ms + 1 : ms;
int us = (int)(t * 1000000 - floor(t) * 1000000);
us = us % 10 == 9 ? us + 1 : us;
tm& Tm = GImPlot->Tm;
IM_ASSERT(GetGmTime(&s, &Tm) != NULL);
switch(fmt) {
case ImPlotTimeFmt_Yr: strftime(buffer, size, "%Y", &Tm); break;
case ImPlotTimeFmt_Mo: strftime(buffer, size, "%b", &Tm); break;
case ImPlotTimeFmt_DayMo: snprintf(buffer, size, "%d/%d", Tm.tm_mon + 1, Tm.tm_mday); break;
case ImPlotTimeFmt_DayMoHrMin:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_min);
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_DayMoYrHrMin:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "%d/%d/%d 12:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min);
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "%d/%d/%d 12:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_min);
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%d/%d/%d %u:%02dam", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d/%d/%d %u:%02dpm", Tm.tm_mon + 1, Tm.tm_mday, Tm.tm_year + 1900, Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_Hr:
if (Tm.tm_hour == 0)
snprintf(buffer, size, "12am");
else if (Tm.tm_hour == 12)
snprintf(buffer, size, "12pm");
else if (Tm.tm_hour < 12)
snprintf(buffer, size, "%uam", Tm.tm_hour);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%upm", Tm.tm_hour - 12);
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, "%d:%02dam", Tm.tm_hour, Tm.tm_min);
else if (Tm.tm_hour > 12)
snprintf(buffer, size, "%d:%02dpm", Tm.tm_hour - 12, Tm.tm_min);
break;
case ImPlotTimeFmt_S: snprintf(buffer, size, ":%02d", Tm.tm_sec); break;
case ImPlotTimeFmt_SMs: snprintf(buffer, size, ":%02d.%03d", Tm.tm_sec, ms); break;
case ImPlotTimeFmt_SUs: snprintf(buffer, size, ":%02d.%06d", Tm.tm_sec, us); break;
default: break;
}
}
inline void PrintTime(double t, ImPlotTimeFmt fmt) {
char buff[32];
FormatTime(t, buff, 32, fmt);
printf("%s\n",buff);
}
// Returns the nominally largest possible width for a time format
inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
switch (fmt) {
case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888888").x; // :29.428552
case ImPlotTimeFmt_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428
case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29
case ImPlotTimeFmt_HrMin: return ImGui::CalcTextSize("88:88pm").x; // 7:21pm
case ImPlotTimeFmt_Hr: return ImGui::CalcTextSize("8pm").x; // 7pm
case ImPlotTimeFmt_DayMo: return ImGui::CalcTextSize("88/88").x; // 10/3
case ImPlotTimeFmt_DayMoHrMin: return ImGui::CalcTextSize("88/88 88:88pm").x; // 10/3 7:21pm
case ImPlotTimeFmt_DayMoYrHrMin: return ImGui::CalcTextSize("88/88/8888 88:88pm").x; // 10/3/1991 7:21pm
case ImPlotTimeFmt_Mo: return ImGui::CalcTextSize("MMM").x; // Oct
case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991
default: return 0;
}
}
// Formates a timestamp t into a buffer according to fmt.
void FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt);
// Prints a timestamp to console
void PrintTime(double t, ImPlotTimeFmt fmt);
//-----------------------------------------------------------------------------
// [SECTION] Internal / Experimental Plotters