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

convert to ImPlotTime usage

This commit is contained in:
epezent 2020-09-05 12:25:44 -05:00
parent f92625a462
commit d3ea373cc7
3 changed files with 152 additions and 128 deletions

View File

@ -617,9 +617,9 @@ inline int LowerBoundStep(int max_divs, const int* divs, const int* step, int si
inline int GetTimeStep(int max_divs, ImPlotTimeUnit unit) {
if (unit == ImPlotTimeUnit_Ms || unit == ImPlotTimeUnit_Us) {
static const int step[] = {500,250,200,100,50,25,20,10,5};
static const int divs[] = {2,4,5,10,20,40,50,100,200};
return LowerBoundStep(max_divs, divs, step, 9);
static const int step[] = {500,250,200,100,50,25,20,10,5,2,1};
static const int divs[] = {2,4,5,10,20,40,50,100,200,500,1000};
return LowerBoundStep(max_divs, divs, step, 11);
}
if (unit == ImPlotTimeUnit_S || unit == ImPlotTimeUnit_Min) {
static const int step[] = {30,15,10,5,1};
@ -670,63 +670,63 @@ inline tm* GetGmTime(const time_t* time, tm* tm)
#endif
}
double AddTime(double t, ImPlotTimeUnit unit, int count) {
ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count) {
ImPlotTime t_out = t;
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_Us: return ImPlotTime(t.S, t.Us + count);
case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, t.Us + count * 1000);
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) {
time_t s = (time_t)t;
GetGmTime(&s, &GImPlot->Tm);
GetGmTime(&t.S, &GImPlot->Tm);
int days = GetDaysInMonth(GImPlot->Tm.tm_year, GImPlot->Tm.tm_mon);
t = AddTime(t, ImPlotTimeUnit_Day, days);
t_out = AddTime(t_out, ImPlotTimeUnit_Day, days);
}
return t;
default: return t;
break;
default: break;
}
return t_out;
}
double FloorTime(double t, ImPlotTimeUnit unit) {
time_t s = (time_t)t;
GetGmTime(&s, &GImPlot->Tm);
ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
GetGmTime(&t.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_S: return ImPlotTime(t.S, 0);
case ImPlotTimeUnit_Ms: return ImPlotTime(t.S, (t.Us / 1000) * 1000);
case ImPlotTimeUnit_Us: return t;
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;
default: return t;
}
s = MakeGmTime(&GImPlot->Tm);
return (double)s;
return ImPlotTime(MakeGmTime(&GImPlot->Tm),0);
}
double CeilTime(double t, ImPlotTimeUnit unit) {
ImPlotTime CeilTime(const ImPlotTime& 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;
ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit) {
ImPlotTime t1 = FloorTime(t, unit);
ImPlotTime t2 = AddTime(t1,unit,1);
if (t1.S == t2.S)
return t.Us - t1.Us < t2.Us - t.Us ? t1 : t2;
return t.S - t1.S < t2.S - t.S ? t1 : t2;
}
int GetYear(double t) {
time_t s = (time_t)t;
int GetYear(const ImPlotTime& t) {
tm& Tm = GImPlot->Tm;
GetGmTime(&s, &Tm);
GetGmTime(&t.S, &Tm);
return Tm.tm_year + 1900;
}
double MakeYear(int year) {
ImPlotTime MakeYear(int year) {
int yr = year - 1900;
if (yr < 0)
yr = 0;
@ -739,17 +739,13 @@ double MakeYear(int year) {
return (double)s;
}
int FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) {
printf("%.6f\n",t);
time_t s = (time_t)t;
int ms = (int)(t * 1000 - floor(t) * 1000);
ms = ms % 10 == 9 ? ms + 1 : ms; // don't like this
int us = (int)(t * 1000000 - floor(t) * 1000000);
us = us % 10 == 9 ? us + 1 : us; // don't like this
int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt) {
tm& Tm = GImPlot->Tm;
GetGmTime(&s, &Tm);
GetGmTime(&t.S, &Tm);
const char* ap = Tm.tm_hour < 12 ? "am" : "pm";
const int us = t.Us % 1000;
const int ms = t.Us / 1000;
const int sec = Tm.tm_sec;
const int min = Tm.tm_min;
const int hr = (Tm.tm_hour == 0 || Tm.tm_hour == 12) ? 12 : Tm.tm_hour % 12;
@ -761,8 +757,8 @@ int FormatTime(double 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"};
switch(fmt) {
case ImPlotTimeFmt_Us: return snprintf(buffer, size, ".%06d", us);
case ImPlotTimeFmt_SUs: return snprintf(buffer, size, ":%02d.%06d", sec, 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_SMs: return snprintf(buffer, size, ":%02d.%03d", sec, ms);
case ImPlotTimeFmt_S: return snprintf(buffer, size, ":%02d", sec);
case ImPlotTimeFmt_HrMinS: return snprintf(buffer, size, "%d:%02d:%02d%s", hr, min, sec, ap);
@ -773,6 +769,7 @@ int FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt) {
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_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);
default: return 0;
@ -788,8 +785,8 @@ void PrintTime(double t, ImPlotTimeFmt fmt) {
// Returns the nominally largest possible width for a time format
inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
switch (fmt) {
case ImPlotTimeFmt_Us: return ImGui::CalcTextSize(".888888").x; // .428552
case ImPlotTimeFmt_SUs: return ImGui::CalcTextSize(":88.888888").x; // :29.428552
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_SMs: return ImGui::CalcTextSize(":88.888").x; // :29.428
case ImPlotTimeFmt_S: return ImGui::CalcTextSize(":88").x; // :29
case ImPlotTimeFmt_HrMinS: return ImGui::CalcTextSize("88:88:88pm").x; // 7:21:29pm
@ -800,6 +797,7 @@ inline float GetTimeLabelWidth(ImPlotTimeFmt fmt) {
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_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_Mo: return ImGui::CalcTextSize("MMM").x; // Oct
case ImPlotTimeFmt_Yr: return ImGui::CalcTextSize("8888").x; // 1991
default: return 0;
@ -834,7 +832,7 @@ static const ImPlotTimeFmt TimeFormatLevel1[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_HrMin,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_DayMo,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_MoYr,
ImPlotTimeFmt_Yr
};
@ -845,7 +843,7 @@ static const ImPlotTimeFmt TimeFormatLevel1First[ImPlotTimeUnit_COUNT] = {
ImPlotTimeFmt_DayMoYrHrMin,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_DayMoYr,
ImPlotTimeFmt_Yr,
ImPlotTimeFmt_MoYr,
ImPlotTimeFmt_Yr
};
@ -859,10 +857,16 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
const ImPlotTimeFmt fmt1 = TimeFormatLevel1[unit1];
const ImPlotTimeFmt fmtf = TimeFormatLevel1First[unit1];
const ImPlotTime t_min(range.Min);
const ImPlotTime t_max(range.Max);
// maximum allowable density of labels
const float max_density = 0.5f;
// book keeping
bool first = true;
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]);
@ -873,16 +877,17 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
// the minor step size (level 0)
const int step = GetTimeStep(minor_per_major, unit0);
// generate ticks
double t1 = FloorTime(range.Min, unit1);
while (t1 < range.Max) {
if (range.Contains(t1)) {
ImPlotTime t1 = FloorTime(ImPlotTime(range.Min), unit1);
t1 = t1;
while (t1 < t_max) {
if (t1 >= t_min && t1 <= t_max) {
// minor level 0 tick
ImPlotTick tick_min(t1,true,true);
ImPlotTick tick_min(t1.ToDouble(),true,true);
tick_min.Level = 0;
LabelTickTime(tick_min,ticks.Labels,fmt0);
ticks.AddTick(tick_min);
// major level 1 tick
ImPlotTick tick_maj(t1,true,true);
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;
@ -897,14 +902,13 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
if (first) first = false;
}
// add minor ticks up until next major
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);
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)/range.Size()) * plot_width;
if (range.Contains(t12)) {
ImPlotTick tick(t12,false,px_to_t2 >= minor_label_width);
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);
tick.Level = 0;
LabelTickTime(tick,ticks.Labels,fmt0);
ticks.AddTick(tick);
@ -931,10 +935,10 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
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);
ImPlotTime t1 = MakeYear(graphmin);
while (t1 < range.Max) {
if (range.Contains(t1)) {
ImPlotTick tick(t1, true, true);
if (t1 >= t_min && t1 <= t_max) {
ImPlotTick tick(t1.ToDouble(), true, true);
tick.Level = 0;
LabelTickTime(tick, ticks.Labels, TimeFormatLevel0[ImPlotTimeUnit_Yr]);
ticks.AddTick(tick);
@ -1028,18 +1032,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// NextPlotData -----------------------------------------------------------
if (gp.NextPlotData.HasXRange) {
if (just_created || gp.NextPlotData.XRangeCond == ImGuiCond_Always) {
plot.XAxis.Range = gp.NextPlotData.X;
plot.XAxis.Constrain();
}
if (just_created || gp.NextPlotData.XRangeCond == ImGuiCond_Always)
plot.XAxis.SetRange(gp.NextPlotData.X);
}
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (gp.NextPlotData.HasYRange[i]) {
if (just_created || gp.NextPlotData.YRangeCond[i] == ImGuiCond_Always) {
plot.YAxis[i].Range = gp.NextPlotData.Y[i];
plot.YAxis[i].Constrain();
}
if (just_created || gp.NextPlotData.YRangeCond[i] == ImGuiCond_Always)
plot.YAxis[i].SetRange(gp.NextPlotData.Y[i]);
}
}
@ -1236,7 +1236,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.XAxis.SetMin(gp.X.Invert ? plot_br.x : plot_tl.x);
if (!gp.X.LockMax)
plot.XAxis.SetMax(gp.X.Invert ? plot_tl.x : plot_br.x);
// ConstrainAxis(plot.XAxis);
}
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (!gp.Y[i].Lock && plot.YAxis[i].Dragging) {
@ -1246,7 +1245,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.YAxis[i].SetMin(gp.Y[i].Invert ? plot_tl.y : plot_br.y);
if (!gp.Y[i].LockMax)
plot.YAxis[i].SetMax(gp.Y[i].Invert ? plot_br.y : plot_tl.y);
// ConstrainAxis(plot.YAxis[i]);
}
}
// Set the mouse cursor based on which axes are moving.
@ -1301,7 +1299,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.XAxis.SetMin(gp.X.Invert ? plot_br.x : plot_tl.x);
if (!gp.X.LockMax)
plot.XAxis.SetMax(gp.X.Invert ? plot_tl.x : plot_br.x);
// ConstrainAxis(plot.XAxis);
}
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (plot.YAxis[i].HoveredTot && !gp.Y[i].Lock) {
@ -1312,7 +1309,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.YAxis[i].SetMin(gp.Y[i].Invert ? plot_tl.y : plot_br.y);
if (!gp.Y[i].LockMax)
plot.YAxis[i].SetMax(gp.Y[i].Invert ? plot_br.y : plot_tl.y);
// ConstrainAxis(plot.YAxis[i]);
}
}
}
@ -1977,7 +1973,6 @@ void EndPlot() {
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].Range.Min = (gp.ExtentsY[i].Min);
@ -1990,7 +1985,6 @@ void EndPlot() {
plot.YAxis[i].Range.Min -= FLT_EPSILON;
}
plot.YAxis[i].Constrain();
// ConstrainAxis(plot.YAxis[i]);
}
}

View File

@ -1271,7 +1271,8 @@ void StyleSeaborn() {
namespace MyImPlot {
int BinarySearch(const double* arr, int l, int r, double x) {
template <typename T>
int BinarySearch(const T* arr, int l, int r, T x) {
if (r >= l) {
int mid = l + (r - l) / 2;
if (arr[mid] == x)
@ -1284,6 +1285,39 @@ int BinarySearch(const double* arr, int l, int r, double x) {
}
void PlotCandlestick(const char* label_id, const double* xs, const double* opens, const double* closes, const double* lows, const double* highs, int count, bool tooltip, float width_percent, ImVec4 bullCol, ImVec4 bearCol) {
// get ImGui window DrawList
ImDrawList* draw_list = ImPlot::GetPlotDrawList();
// calc real value width
double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent;
// custom tool
if (ImPlot::IsPlotHovered() && tooltip) {
ImPlotPoint mouse = ImPlot::GetPlotMousePos();
mouse.x = ImPlot::RoundTime(ImPlotTime(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;
float tool_b = tool_t + ImPlot::GetPlotSize().y;
ImPlot::PushPlotClipRect();
draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128,128,128,64));
ImPlot::PopPlotClipRect();
// find mouse location index
int idx = BinarySearch(xs, 0, count - 1, mouse.x);
// render tool tip (won't be affected by plot clip rect)
if (idx != -1) {
ImGui::BeginTooltip();
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]);
ImGui::Text("High: $%.2f", highs[idx]);
ImGui::EndTooltip();
}
}
// begin plot item
if (ImPlot::BeginItem(label_id)) {
// override legend icon color
@ -1295,10 +1329,6 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
ImPlot::FitPoint(ImPlotPoint(xs[i], highs[i]));
}
}
// get ImGui window DrawList
ImDrawList* draw_list = ImPlot::GetPlotDrawList();
// calc real value width
double half_width = count > 1 ? (xs[1] - xs[0]) * width_percent : width_percent;
// render data
for (int i = 0; i < count; ++i) {
ImVec2 open_pos = ImPlot::PlotToPixels(xs[i] - half_width, opens[i]);
@ -1309,30 +1339,7 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens
draw_list->AddLine(low_pos, high_pos, color);
draw_list->AddRectFilled(open_pos, close_pos, color);
}
// custom tool
if (ImPlot::IsPlotHovered() && tooltip) {
ImPlotPoint mouse = ImPlot::GetPlotMousePos();
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;
float tool_b = tool_t + ImPlot::GetPlotSize().y;
draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(0,255,255,64));
// find mouse location index
int idx = BinarySearch(xs, 0, count - 1, mouse.x);
// render tool tip (won't be affected by plot clip rect)
if (idx != -1) {
ImGui::BeginTooltip();
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]);
ImGui::Text("High: $%.2f", highs[idx]);
ImGui::EndTooltip();
}
}
// end plot item
ImPlot::EndItem();
}

View File

@ -195,6 +195,7 @@ enum ImPlotTimeFmt_ {
ImPlotTimeFmt_DayMoYr, // 10/3/91
ImPlotTimeFmt_DayMoYrHrMin, // 10/3/91 7:21pm
ImPlotTimeFmt_DayMoYrHrMinS, // 10/3/91 7:21:29pm
ImPlotTimeFmt_MoYr, // Oct 1991
ImPlotTimeFmt_Mo, // Oct
ImPlotTimeFmt_Yr // 1991
};
@ -307,11 +308,8 @@ struct ImPlotAxis
_min = ImConstrainNan(ImConstrainInf(_min));
if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale))
_min = ImConstrainLog(_min);
if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) {
_min = ImConstrainTime(_min);
if ((Range.Max - _min) < 0.0001)
return false;
}
if (ImHasFlag(Flags, ImPlotAxisFlags_Time))
_min = ImConstrainTime(_min);
if (_min >= Range.Max)
return false;
Range.Min = _min;
@ -322,17 +320,24 @@ struct ImPlotAxis
_max = ImConstrainNan(ImConstrainInf(_max));
if (ImHasFlag(Flags, ImPlotAxisFlags_LogScale))
_max = ImConstrainLog(_max);
if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) {
_max = ImConstrainTime(_max);
if ((_max - Range.Min) < 0.0001)
return false;
}
if (ImHasFlag(Flags, ImPlotAxisFlags_Time))
_max = ImConstrainTime(_max);
if (_max <= Range.Min)
return false;
Range.Max = _max;
return true;
};
void SetRange(double _min, double _max) {
Range.Min = _min;
Range.Max = _max;
Constrain();
}
void SetRange(const ImPlotRange& range) {
SetRange(range.Min, range.Max);
}
void Constrain() {
Range.Min = ImConstrainNan(ImConstrainInf(Range.Min));
Range.Max = ImConstrainNan(ImConstrainInf(Range.Max));
@ -343,8 +348,6 @@ struct ImPlotAxis
if (ImHasFlag(Flags, ImPlotAxisFlags_Time)) {
Range.Min = ImConstrainTime(Range.Min);
Range.Max = ImConstrainTime(Range.Max);
if (Range.Size() < 0.0001)
Range.Max = Range.Min + 0.0001; // TBD
}
if (Range.Max <= Range.Min)
Range.Max = Range.Min + DBL_EPSILON;
@ -576,14 +579,33 @@ struct ImPlotAxisScale
/// Two part time struct.
struct ImPlotTime {
time_t S;
time_t Us;
ImPlotTime(time_t s, time_t us) { S = s; Us = us;}
int Us;
ImPlotTime(time_t s, int us) {
S = s + us / 1000000;
Us = us % 1000000;
}
ImPlotTime(double t) {
S = (time_t)t;
Us = (int)(t * 1000000 - floor(t) * 1000000);
}
double ToDouble() const { return (double)S + (double)Us / 1000000.0; }
};
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
// No guarantee of forward compatibility here!
@ -653,7 +675,8 @@ inline bool FitThisFrame() { return GImPlot->FitThisFrame; }
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; }
inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2)
{ return r1.Min <= r2.Max && r2.Min <= r1.Max; }
//-----------------------------------------------------------------------------
// [SECTION] Legend Utils
@ -771,23 +794,23 @@ inline int GetDaysInMonth(int year, int month) {
}
// Adds time to a timestamp. #count must be positive!
double AddTime(double t, ImPlotTimeUnit unit, int count);
ImPlotTime AddTime(const ImPlotTime& t, ImPlotTimeUnit unit, int count);
// Rounds a timestamp down to nearest.
double FloorTime(double t, ImPlotTimeUnit unit);
ImPlotTime FloorTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Rounds a timestamp up to the nearest unit.
double CeilTime(double t, ImPlotTimeUnit unit);
ImPlotTime CeilTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Rounds a timestamp up or down to the nearest unit.
double RoundTime(double t, ImPlotTimeUnit unit);
ImPlotTime RoundTime(const ImPlotTime& t, ImPlotTimeUnit unit);
// Get year from timestamp
int GetYear(double t);
int GetYear(const ImPlotTime& t);
// Make a timestamp starting at the first day of a year
double MakeYear(int year);
ImPlotTime MakeYear(int year);
// Formates a timestamp t into a buffer according to fmt.
int FormatTime(double t, char* buffer, int size, ImPlotTimeFmt fmt);
int FormatTime(const ImPlotTime& t, char* buffer, int size, ImPlotTimeFmt fmt);
// Prints a timestamp to console
void PrintTime(double t, ImPlotTimeFmt fmt);
void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt);
//-----------------------------------------------------------------------------
// [SECTION] Internal / Experimental Plotters