1
0
Fork 0
mirror of https://github.com/gwm17/implot.git synced 2024-11-13 14:38:51 -05:00

Adds SetNextPlotFormatX/Y for custom tick label formatting (#198)

* add SetNextPlotFormat

* work on pruning

* finish up fmt
This commit is contained in:
Evan Pezent 2021-03-25 09:19:00 -06:00 committed by GitHub
parent 4b0f9c9495
commit ed1baf471a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 112 deletions

View File

@ -689,45 +689,55 @@ void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interacta
// Tick Utils
//-----------------------------------------------------------------------------
void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer) {
char temp[32];
if (tick.ShowLabel) {
tick.TextOffset = buffer.size();
snprintf(temp, 32, "%.10g", tick.PlotPos);
buffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset);
}
}
void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) {
char temp[32];
if (tick.ShowLabel) {
tick.TextOffset = buffer.size();
snprintf(temp, 32, "%.0E", tick.PlotPos);
buffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset);
}
}
void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks) {
void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt) {
const int idx0 = ticks.Size;
const int nMinor = 10;
const int nMajor = ImMax(2, (int)IM_ROUND(pix / (orn == ImPlotOrientation_Horizontal ? 400.0f : 300.0f)));
const double nice_range = NiceNum(range.Size() * 0.99, false);
const double interval = NiceNum(nice_range / (nMajor - 1), true);
const double graphmin = floor(range.Min / interval) * interval;
const double graphmax = ceil(range.Max / interval) * interval;
bool first_major_set = false;
int first_major_idx = 0;
char dummy[32];
sprintf(dummy,fmt,-ImAbs(interval / nMinor));
ImVec2 dummy_size = ImGui::CalcTextSize(dummy);
ImVec2 total_size(0,0);
for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) {
if (range.Contains(major))
ticks.Append(major, true, true, LabelTickDefault);
// is this zero? combat zero formatting issues
if (major-interval < 0 && major+interval > 0)
major = 0;
if (range.Contains(major)) {
if (!first_major_set) {
first_major_idx = ticks.Size;
first_major_set = true;
}
ticks.Append(major, true, true, fmt);
total_size += dummy_size;
}
for (int i = 1; i < nMinor; ++i) {
double minor = major + i * interval / nMinor;
if (range.Contains(minor))
ticks.Append(minor, false, true, LabelTickDefault);
if (range.Contains(minor)) {
ticks.Append(minor, false, true, fmt);
total_size += dummy_size;
}
}
}
// prune if necessary
if ((orn == ImPlotOrientation_Horizontal && total_size.x > pix) || (orn == ImPlotOrientation_Vertical && total_size.y > pix)) {
for (int i = first_major_idx-1; i >= idx0; i -= 2)
ticks.Ticks[i].ShowLabel = false;
for (int i = first_major_idx+1; i < ticks.Size; i += 2)
ticks.Ticks[i].ShowLabel = false;
}
}
void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks) {
void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt) {
if (range.Min <= 0 || range.Max <= 0)
return;
const int nMajor = orn == ImPlotOrientation_Horizontal ? ImMax(2, (int)IM_ROUND(pix * 0.01f)) : ImMax(2, (int)IM_ROUND(pix * 0.02f));
double log_min = ImLog10(range.Min);
double log_max = ImLog10(range.Max);
int exp_step = ImMax(1,(int)(log_max - log_min) / nMajor);
@ -742,7 +752,7 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollect
double major2 = ImPow(10, (double)(e + 1));
double interval = (major2 - major1) / 9;
if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON))
ticks.Append(major1, true, true, LabelTickScientific);
ticks.Append(major1, true, true, fmt);
for (int j = 0; j < exp_step; ++j) {
major1 = ImPow(10, (double)(e+j));
major2 = ImPow(10, (double)(e+j+1));
@ -750,25 +760,25 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollect
for (int i = 1; i < (9 + (int)(j < (exp_step - 1))); ++i) {
double minor = major1 + i * interval;
if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON))
ticks.Append(minor, false, false, LabelTickScientific);
ticks.Append(minor, false, false, fmt);
}
}
}
}
void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks) {
void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, const char* fmt) {
for (int i = 0; i < n; ++i) {
ImPlotTick tick(values[i], false, true);
if (labels != NULL) {
ImPlotTick tick(values[i], false, true);
tick.TextOffset = ticks.TextBuffer.size();
ticks.TextBuffer.append(labels[i], labels[i] + strlen(labels[i]) + 1);
tick.LabelSize = ImGui::CalcTextSize(labels[i]);
ticks.Append(tick);
}
else {
LabelTickDefault(tick, ticks.TextBuffer);
ticks.Append(values[i], false, true, fmt);
}
ticks.Append(tick);
}
}
@ -1256,12 +1266,18 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollecti
// Axis Utils
//-----------------------------------------------------------------------------
static inline int AxisPrecision(const ImPlotAxis& axis, const ImPlotTickCollection& ticks) {
const double range = ticks.Size > 1 ? (ticks.Ticks[1].PlotPos - ticks.Ticks[0].PlotPos) : axis.Range.Size();
return Precision(range);
}
static inline double RoundAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value) {
return RoundTo(value, AxisPrecision(axis,ticks));
}
int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value, char* buff, int size) {
ImPlotContext& gp = *GImPlot;
if (ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale)) {
return snprintf(buff, size, "%.3E", value);
}
else if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) {
if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) {
ImPlotTimeUnit unit = (axis.Orientation == ImPlotOrientation_Horizontal)
? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100))
: GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100));
@ -1521,9 +1537,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con
for (int i = 0; i < IMPLOT_Y_AXES; i++) {
if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) {
if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale))
AddTicksLogarithmic(plot.YAxis[i].Range, ImMax(2, (int)IM_ROUND(plot_height * 0.02f)) ,gp.YTicks[i]);
AddTicksLogarithmic(plot.YAxis[i].Range, plot_height, ImPlotOrientation_Vertical, gp.YTicks[i], GetFormatY(i));
else
AddTicksDefault(plot.YAxis[i].Range, ImMax(2, (int)IM_ROUND(0.0025 * plot_height)), IMPLOT_SUB_DIV, gp.YTicks[i]);
AddTicksDefault(plot.YAxis[i].Range, plot_height, ImPlotOrientation_Vertical, gp.YTicks[i], GetFormatY(i));
}
}
@ -1547,9 +1563,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con
if (plot.XAxis.IsTime())
AddTicksTime(plot.XAxis.Range, plot_width, gp.XTicks);
else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale))
AddTicksLogarithmic(plot.XAxis.Range, (int)IM_ROUND(plot_width * 0.01f), gp.XTicks);
AddTicksLogarithmic(plot.XAxis.Range, plot_width, ImPlotOrientation_Horizontal, gp.XTicks, GetFormatX());
else
AddTicksDefault(plot.XAxis.Range, ImMax(2, (int)IM_ROUND(0.0025 * plot_width)), IMPLOT_SUB_DIV, gp.XTicks);
AddTicksDefault(plot.XAxis.Range, plot_width, ImPlotOrientation_Horizontal, gp.XTicks, GetFormatX());
}
// (5) calc plot bb
@ -2242,10 +2258,8 @@ void EndPlot() {
// AXIS STATES ------------------------------------------------------------
const bool any_y_locked = plot.AnyYInputLocked();
const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging;
// FINAL RENDER -----------------------------------------------------------
// render ticks
@ -2284,10 +2298,7 @@ void EndPlot() {
if (axis_count >= 3) {
// Draw a bar next to the ticks to act as a visual separator.
DrawList.AddLine(
ImVec2(x_start, plot.PlotRect.Min.y),
ImVec2(x_start, plot.PlotRect.Max.y),
GetStyleColorU32(ImPlotCol_YAxisGrid3), 1);
DrawList.AddLine(ImVec2(x_start, plot.PlotRect.Min.y), ImVec2(x_start, plot.PlotRect.Max.y), GetStyleColorU32(ImPlotCol_YAxisGrid3), 1);
}
}
ImGui::PopClipRect();
@ -2358,52 +2369,34 @@ void EndPlot() {
DrawList.AddLine(v3, v4, col);
}
// render mouse pos (TODO: use LabelAxisValue)
// render mouse pos
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos) && plot.PlotHovered) {
char buffer[128] = {};
ImBufferWriter writer(buffer, sizeof(buffer));
// x
if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) {
writer.Write("%.3E", gp.MousePos[0].x);
}
else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Time)) {
if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Time)) {
ImPlotTimeUnit unit = GetUnitForRange(plot.XAxis.Range.Size() / (plot.PlotRect.GetWidth() / 100));
const int written = FormatDateTime(ImPlotTime::FromDouble(gp.MousePos[0].x), &writer.Buffer[writer.Pos], writer.Size - writer.Pos - 1, GetDateTimeFmt(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);
writer.Write(GetFormatX(), RoundAxisValue(plot.XAxis, gp.XTicks, gp.MousePos[0].x));
}
// y1
if (ImHasFlag(plot.YAxis[0].Flags, ImPlotAxisFlags_LogScale)) {
writer.Write(",%.3E", gp.MousePos[0].y);
}
else {
double range_y = gp.YTicks[0].Size > 1 ? (gp.YTicks[0].Ticks[1].PlotPos - gp.YTicks[0].Ticks[0].PlotPos) : plot.YAxis[0].Range.Size();
writer.Write(",%.*f", Precision(range_y), gp.MousePos[0].y);
}
writer.Write(", ");
writer.Write(GetFormatY(0), RoundAxisValue(plot.YAxis[0], gp.YTicks[0], gp.MousePos[0].y));
// y2
if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)) {
if (ImHasFlag(plot.YAxis[1].Flags, ImPlotAxisFlags_LogScale)) {
writer.Write(",(%.3E)", gp.MousePos[1].y);
}
else {
double range_y = gp.YTicks[1].Size > 1 ? (gp.YTicks[1].Ticks[1].PlotPos - gp.YTicks[1].Ticks[0].PlotPos) : plot.YAxis[1].Range.Size();
writer.Write(",(%.*f)", Precision(range_y), gp.MousePos[1].y);
}
writer.Write(", (");
writer.Write(GetFormatY(1), RoundAxisValue(plot.YAxis[1], gp.YTicks[1], gp.MousePos[1].y));
writer.Write(")");
}
// y3
if (ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)) {
if (ImHasFlag(plot.YAxis[2].Flags, ImPlotAxisFlags_LogScale)) {
writer.Write(",(%.3E)", gp.MousePos[2].y);
}
else {
double range_y = gp.YTicks[2].Size > 1 ? (gp.YTicks[2].Ticks[1].PlotPos - gp.YTicks[2].Ticks[0].PlotPos) : plot.YAxis[2].Range.Size();
writer.Write(",(%.*f)", Precision(range_y), gp.MousePos[2].y);
}
writer.Write(", (");
writer.Write(GetFormatY(2), RoundAxisValue(plot.YAxis[2], gp.YTicks[2], gp.MousePos[2].y));
writer.Write(")");
}
const ImVec2 size = ImGui::CalcTextSize(buffer);
const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MousePosLocation, gp.Style.MousePosPadding);
@ -2618,7 +2611,7 @@ void SetNextPlotTicksX(const double* values, int n_ticks, const char* const labe
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksX() needs to be called before BeginPlot()!");
gp.NextPlotData.ShowDefaultTicksX = show_default;
AddTicksCustom(values, labels, n_ticks, gp.XTicks);
AddTicksCustom(values, labels, n_ticks, gp.XTicks, GetFormatX());
}
void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* const labels[], bool show_default) {
@ -2633,7 +2626,7 @@ void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labe
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES");
gp.NextPlotData.ShowDefaultTicksY[y_axis] = show_default;
AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis]);
AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis], GetFormatY(y_axis));
}
void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[], bool show_default, ImPlotYAxis y_axis) {
@ -2643,6 +2636,21 @@ void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* cons
SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis);
}
void SetNextPlotFormatX(const char* fmt){
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotFormatX() needs to be called before BeginPlot()!");
gp.NextPlotData.HasFmtX = true;
ImStrncpy(gp.NextPlotData.FmtX, fmt, 16);
}
void SetNextPlotFormatY(const char* fmt, ImPlotYAxis y_axis) {
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotFormatY() needs to be called before BeginPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES");
gp.NextPlotData.HasFmtY[y_axis] = true;
ImStrncpy(gp.NextPlotData.FmtY[y_axis], fmt, 16);
}
void SetPlotYAxis(ImPlotYAxis y_axis) {
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!");
@ -3453,7 +3461,7 @@ void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const I
}
}
void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, ImPlotColormap cmap) {
void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, ImPlotColormap cmap, const char* fmt) {
ImGuiContext &G = *GImGui;
ImGuiWindow * Window = G.CurrentWindow;
if (Window->SkipItems)
@ -3473,7 +3481,7 @@ void ColormapScale(const char* label, double scale_min, double scale_max, const
ImPlotRange range(scale_min,scale_max);
gp.CTicks.Reset();
AddTicksDefault(range, ImMax(2, (int)IM_ROUND(0.0025 * frame_size.y)), IMPLOT_SUB_DIV, gp.CTicks);
AddTicksDefault(range, frame_size.y, ImPlotOrientation_Vertical, gp.CTicks, fmt);
const float txt_off = gp.Style.LabelPadding.x;
const float pad_right = txt_off + gp.CTicks.MaxWidth + (label_size.x > 0 ? txt_off + label_size.y : 0);

View File

@ -513,19 +513,23 @@ IMPLOT_API void SetNextPlotLimits(double xmin, double xmax, double ymin, double
// Set the X axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the X axis limits will be locked.
IMPLOT_API void SetNextPlotLimitsX(double xmin, double xmax, ImGuiCond cond = ImGuiCond_Once);
// Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked.
IMPLOT_API void SetNextPlotLimitsY(double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once, ImPlotYAxis y_axis = 0);
IMPLOT_API void SetNextPlotLimitsY(double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once, ImPlotYAxis y_axis = ImPlotYAxis_1);
// Links the next plot limits to external values. Set to NULL for no linkage. The pointer data must remain valid until the matching call to EndPlot.
IMPLOT_API void LinkNextPlotLimits(double* xmin, double* xmax, double* ymin, double* ymax, double* ymin2 = NULL, double* ymax2 = NULL, double* ymin3 = NULL, double* ymax3 = NULL);
// Fits the next plot axes to all plotted data if they are unlocked (equivalent to double-clicks).
IMPLOT_API void FitNextPlotAxes(bool x = true, bool y = true, bool y2 = true, bool y3 = true);
// Set the X axis ticks and optionally the labels for the next plot. To keep the default ticks, set #show_default=true.
IMPLOT_API void SetNextPlotTicksX(const double* values, int n_ticks, const char* const labels[] = NULL, bool show_default = false);
IMPLOT_API void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* const labels[] = NULL, bool show_default = false);
// Set the X axis ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true.
IMPLOT_API void SetNextPlotTicksX(const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false);
IMPLOT_API void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false);
// Set the Y axis ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true.
IMPLOT_API void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[] = NULL, bool keep_default = false, ImPlotYAxis y_axis = ImPlotYAxis_1);
IMPLOT_API void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[] = NULL, bool keep_default = false, ImPlotYAxis y_axis = ImPlotYAxis_1);
// Set the Y axis ticks and optionally the labels for the next plot. To keep the default ticks, set #show_default=true.
IMPLOT_API void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[] = NULL, bool show_default = false, ImPlotYAxis y_axis = 0);
IMPLOT_API void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[] = NULL, bool show_default = false, ImPlotYAxis y_axis = 0);
// Set the format for numeric X axis labels (default="%g"). Formated values will be doubles (i.e. don't supply %d, %i, etc.). Not applicable if ImPlotAxisFlags_Time enabled.
IMPLOT_API void SetNextPlotFormatX(const char* fmt);
// Set the format for numeric Y axis labels (default="%g"). Formated values will be doubles (i.e. don't supply %d, %i, etc.).
IMPLOT_API void SetNextPlotFormatY(const char* fmt, ImPlotYAxis y_axis=ImPlotYAxis_1);
// The following functions MUST be called BETWEEN Begin/EndPlot!
@ -778,7 +782,7 @@ IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO);
IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO);
// Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel").
IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO);
IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO, const char* fmt = "%g");
// Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1].
IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = NULL, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO);
// Shows a button with a colormap gradient brackground.

View File

@ -1420,8 +1420,11 @@ void ShowDemoWindow(bool* p_open) {
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Custom Ticks##")) {
static bool custom_ticks = true;
static bool custom_fmt = true;
static bool custom_ticks = false;
static bool custom_labels = true;
ImGui::Checkbox("Show Custom Format", &custom_fmt);
ImGui::SameLine();
ImGui::Checkbox("Show Custom Ticks", &custom_ticks);
if (custom_ticks) {
ImGui::SameLine();
@ -1433,11 +1436,17 @@ void ShowDemoWindow(bool* p_open) {
static const char* ylabels[] = {"One","Three","Seven","Nine"};
static double yticks_aux[] = {0.2,0.4,0.6};
static const char* ylabels_aux[] = {"A","B","C","D","E","F"};
if (custom_fmt) {
ImPlot::SetNextPlotFormatX("%g ms");
ImPlot::SetNextPlotFormatY("%g Hz", ImPlotYAxis_1);
ImPlot::SetNextPlotFormatY("%g dB", ImPlotYAxis_2);
ImPlot::SetNextPlotFormatY("%g km", ImPlotYAxis_3);
}
if (custom_ticks) {
ImPlot::SetNextPlotTicksX(&pi,1,custom_labels ? pi_str : NULL, true);
ImPlot::SetNextPlotTicksY(yticks, 4, custom_labels ? ylabels : NULL);
ImPlot::SetNextPlotTicksY(yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false, 1);
ImPlot::SetNextPlotTicksY(0, 1, 6, custom_labels ? ylabels_aux : NULL, false, 2);
ImPlot::SetNextPlotTicksY(yticks, 4, custom_labels ? ylabels : NULL, ImPlotYAxis_1);
ImPlot::SetNextPlotTicksY(yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false, ImPlotYAxis_2);
ImPlot::SetNextPlotTicksY(0, 1, 6, custom_labels ? ylabels_aux : NULL, false, ImPlotYAxis_3);
}
ImPlot::SetNextPlotLimits(2.5,5,0,10);
if (ImPlot::BeginPlot("Custom Ticks", NULL, NULL, ImVec2(-1,0), ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3)) {
@ -1545,8 +1554,9 @@ void ShowDemoWindow(bool* p_open) {
ImGui::SameLine(); ImGui::ColorEdit4("##Bull", &bullCol.x, ImGuiColorEditFlags_NoInputs);
ImGui::SameLine(); ImGui::ColorEdit4("##Bear", &bearCol.x, ImGuiColorEditFlags_NoInputs);
ImPlot::GetStyle().UseLocalTime = false;
ImPlot::SetNextPlotFormatY("$%.0f");
ImPlot::SetNextPlotLimits(1546300800, 1571961600, 1250, 1600);
if (ImPlot::BeginPlot("Candlestick Chart","Day","USD",ImVec2(-1,0),0,ImPlotAxisFlags_Time)) {
if (ImPlot::BeginPlot("Candlestick Chart",NULL,NULL,ImVec2(-1,0),0,ImPlotAxisFlags_Time)) {
MyImPlot::PlotCandlestick("GOOGL",dates, opens, closes, lows, highs, 218, tooltip, 0.25f, bullCol, bearCol);
ImPlot::EndPlot();
}

View File

@ -51,14 +51,14 @@
// The maximum number of supported y-axes (DO NOT CHANGE THIS)
#define IMPLOT_Y_AXES 3
// The number of times to subdivided grid divisions (best if a multiple of 1, 2, and 5)
#define IMPLOT_SUB_DIV 10
// Zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click)
#define IMPLOT_ZOOM_RATE 0.1f
// Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS)
#define IMPLOT_MIN_TIME 0
// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS)
#define IMPLOT_MAX_TIME 32503680000
// Default label format for axis labels
#define IMPLOT_LABEL_FMT "%g"
//-----------------------------------------------------------------------------
// [SECTION] Macros
@ -528,6 +528,7 @@ struct ImPlotTick
struct ImPlotTickCollection {
ImVector<ImPlotTick> Ticks;
ImGuiTextBuffer TextBuffer;
float TotalWidthMax;
float TotalWidth;
float TotalHeight;
float MaxWidth;
@ -536,22 +537,28 @@ struct ImPlotTickCollection {
ImPlotTickCollection() { Reset(); }
void Append(const ImPlotTick& tick) {
const ImPlotTick& Append(const ImPlotTick& tick) {
if (tick.ShowLabel) {
TotalWidth += tick.ShowLabel ? tick.LabelSize.x : 0;
TotalHeight += tick.ShowLabel ? tick.LabelSize.y : 0;
MaxWidth = tick.LabelSize.x > MaxWidth ? tick.LabelSize.x : MaxWidth;
MaxHeight = tick.LabelSize.y > MaxHeight ? tick.LabelSize.y : MaxHeight;
TotalWidth += tick.ShowLabel ? tick.LabelSize.x : 0;
TotalHeight += tick.ShowLabel ? tick.LabelSize.y : 0;
MaxWidth = tick.LabelSize.x > MaxWidth ? tick.LabelSize.x : MaxWidth;
MaxHeight = tick.LabelSize.y > MaxHeight ? tick.LabelSize.y : MaxHeight;
}
Ticks.push_back(tick);
Size++;
return Ticks.back();
}
void Append(double value, bool major, bool show_label, void (*labeler)(ImPlotTick& tick, ImGuiTextBuffer& buf)) {
const ImPlotTick& Append(double value, bool major, bool show_label, const char* fmt) {
ImPlotTick tick(value, major, show_label);
if (labeler)
labeler(tick, TextBuffer);
Append(tick);
if (show_label && fmt != NULL) {
char temp[32];
tick.TextOffset = TextBuffer.size();
snprintf(temp, 32, fmt, tick.PlotPos);
TextBuffer.append(temp, temp + strlen(temp) + 1);
tick.LabelSize = ImGui::CalcTextSize(TextBuffer.Buf.Data + tick.TextOffset);
}
return Append(tick);
}
const char* GetText(int idx) const {
@ -788,6 +795,10 @@ struct ImPlotNextPlotData
bool HasYRange[IMPLOT_Y_AXES];
bool ShowDefaultTicksX;
bool ShowDefaultTicksY[IMPLOT_Y_AXES];
char FmtX[16];
char FmtY[IMPLOT_Y_AXES][16];
bool HasFmtX;
bool HasFmtY[IMPLOT_Y_AXES];
bool FitX;
bool FitY[IMPLOT_Y_AXES];
double* LinkedXmin;
@ -800,11 +811,13 @@ struct ImPlotNextPlotData
void Reset() {
HasXRange = false;
ShowDefaultTicksX = true;
HasFmtX = false;
FitX = false;
LinkedXmin = LinkedXmax = NULL;
for (int i = 0; i < IMPLOT_Y_AXES; ++i) {
HasYRange[i] = false;
ShowDefaultTicksY[i] = true;
HasFmtY[i] = false;
FitY[i] = false;
LinkedYmin[i] = LinkedYmax[i] = NULL;
}
@ -993,6 +1006,10 @@ IMPLOT_API void PullLinkedAxis(ImPlotAxis& axis);
// Shows an axis's context menu.
IMPLOT_API void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed = false);
// Get format spec for axis
static inline const char* GetFormatX() { return GImPlot->NextPlotData.HasFmtX ? GImPlot->NextPlotData.FmtX : IMPLOT_LABEL_FMT; }
static inline const char* GetFormatY(ImPlotYAxis y) { return GImPlot->NextPlotData.HasFmtY[y] ? GImPlot->NextPlotData.FmtY[y] : IMPLOT_LABEL_FMT; }
//-----------------------------------------------------------------------------
// [SECTION] Legend Utils
//-----------------------------------------------------------------------------
@ -1010,21 +1027,17 @@ IMPLOT_API void ShowAltLegend(const char* title_id, ImPlotOrientation orientatio
// [SECTION] Tick Utils
//-----------------------------------------------------------------------------
// Label a tick with default formatting.
IMPLOT_API void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer);
// Label a tick with scientific formating.
IMPLOT_API void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer);
// Label a tick with time formatting.
IMPLOT_API void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotDateTimeFmt fmt);
// Populates a list of ImPlotTicks with normal spaced and formatted ticks
IMPLOT_API void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks);
IMPLOT_API void AddTicksDefault(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt);
// Populates a list of ImPlotTicks with logarithmic space and formatted ticks
IMPLOT_API void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks);
IMPLOT_API void AddTicksLogarithmic(const ImPlotRange& range, float pix, ImPlotOrientation orn, ImPlotTickCollection& ticks, const char* fmt);
// Populates a list of ImPlotTicks with time formatted ticks.
IMPLOT_API void AddTicksTime(const ImPlotRange& range, float plot_width, ImPlotTickCollection& ticks);
// Populates a list of ImPlotTicks with custom spaced and labeled ticks
IMPLOT_API void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks);
IMPLOT_API void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlotTickCollection& ticks, const char* fmt);
// Create a a string label for a an axis value
IMPLOT_API int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, double value, char* buff, int size);
@ -1057,7 +1070,7 @@ static inline ImVec2 CalcTextSizeVertical(const char *text) {
// Returns white or black text given background color
static inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.5 ? IM_COL32_BLACK : IM_COL32_WHITE; }
static inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); }
// Lights or darkens a color for hover
// Lightens or darkens a color for hover
static inline ImU32 CalcHoverColor(ImU32 col) { return ImMixU32(col, CalcTextColor(col), 32); }
// Clamps a label position so that it fits a rect defined by Min/Max
@ -1091,6 +1104,8 @@ static inline int OrderOfMagnitude(double val) { return val == 0 ? 0 : (int)(flo
static inline int OrderToPrecision(int order) { return order > 0 ? 0 : 1 - order; }
// Returns a floating point precision to use given a value
static inline int Precision(double val) { return OrderToPrecision(OrderOfMagnitude(val)); }
// Round a value to a given precision
static inline double RoundTo(double val, int prec) { double p = pow(10,(double)prec); return floor(val*p+0.5)/p; }
// Returns the intersection point of two lines A and B (assumes they are not parallel!)
static inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) {
@ -1118,7 +1133,7 @@ static inline T OffsetAndStride(const T* data, int idx, int count, int offset, i
// Calculate histogram bin counts and widths
template <typename T>
void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) {
static inline void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) {
switch (meth) {
case ImPlotBin_Sqrt:
bins_out = (int)ceil(sqrt(count));