diff --git a/implot.cpp b/implot.cpp index 7941d12..66a0fd5 100644 --- a/implot.cpp +++ b/implot.cpp @@ -338,11 +338,9 @@ void Reset(ImPlotContext* ctx) { ctx->LegendIndices.shrink(0); ctx->LegendLabels.Buf.shrink(0); // reset ticks/labels - ctx->XTicks.shrink(0); - ctx->XTickLabels.Buf.shrink(0); + ctx->XTicks.Reset(); for (int i = 0; i < 3; ++i) { - ctx->YTicks[i].shrink(0); - ctx->YTickLabels[i].Buf.shrink(0); + ctx->YTicks[i].Reset(); } // reset extents/fit ctx->FitThisFrame = false; @@ -539,23 +537,43 @@ const char* GetLegendLabel(int i) { // Tick Utils //----------------------------------------------------------------------------- -void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImVector &out) { +void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer) { + char temp[32]; + if (tick.ShowLabel) { + tick.BufferOffset = buffer.size(); + sprintf(temp, "%.10g", tick.PlotPos); + buffer.append(temp, temp + strlen(temp) + 1); + tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset); + } +} + +void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) { + char temp[32]; + if (tick.ShowLabel) { + tick.BufferOffset = buffer.size(); + sprintf(temp, "%.0E", tick.PlotPos); + 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); const double graphmin = floor(range.Min / interval) * interval; const double graphmax = ceil(range.Max / interval) * interval; for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { - if (range.Contains(major)) - out.push_back(ImPlotTick(major, true, true)); + if (range.Contains(major)) + ticks.AddTick(major, true, true, LabelTickDefault); for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; - if (range.Contains(minor)) - out.push_back(ImPlotTick(minor, false, true)); + if (range.Contains(minor)) + ticks.AddTick(minor, false, true, LabelTickDefault); } } } -void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImVector& out) { +void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks) { if (range.Min <= 0 || range.Max <= 0) return; double log_min = ImLog10(range.Min); @@ -571,76 +589,50 @@ void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImVector= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) - out.push_back(ImPlotTick(major1, true, true)); + if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) + ticks.AddTick(major1, true, true, LabelTickScientific); for (int j = 0; j < exp_step; ++j) { major1 = ImPow(10, (double)(e+j)); major2 = ImPow(10, (double)(e+j+1)); interval = (major2 - major1) / 9; 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)) - out.push_back(ImPlotTick(minor, false, false)); + if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) + ticks.AddTick(minor, false, false, LabelTickScientific); + } } } } -void AddTicksCustom(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer) { + +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); - tick.BufferOffset = buffer.size(); + ImPlotTick tick(values[i], false, true); + tick.BufferOffset = ticks.Labels.size(); if (labels != NULL) { - buffer.append(labels[i], labels[i] + strlen(labels[i]) + 1); + ticks.Labels.append(labels[i], labels[i] + strlen(labels[i]) + 1); tick.LabelSize = ImGui::CalcTextSize(labels[i]); - tick.Labeled = true; } - ticks.push_back(tick); + ticks.AddTick(tick); } } -void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) { - char temp[32]; - for (int t = 0; t < ticks.Size; t++) { - ImPlotTick *tk = &ticks[t]; - if (tk->ShowLabel && !tk->Labeled) { - tk->BufferOffset = buffer.size(); - if (scientific) - sprintf(temp, "%.0E", tk->PlotPos); - else - sprintf(temp, "%.10g", tk->PlotPos); - buffer.append(temp, temp + strlen(temp) + 1); - tk->LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tk->BufferOffset); - tk->Labeled = true; - } - } -} - -float MaxTickLabelWidth(const ImVector& ticks) { - float w = 0; - for (int i = 0; i < ticks.Size; ++i) - w = (ticks[i].ShowLabel && ticks[i].LabelSize.x > w) ? ticks[i].LabelSize.x : w; - return w; -} - -float SumTickLabelWidth(const ImVector& ticks) { - float sum = 0; - for (int i = 0; i < ticks.Size; ++i) - sum += ticks[i].ShowLabel ? ticks[i].LabelSize.x : 0; - return sum; -} - -float SumTickLabelHeight(const ImVector& ticks) { - float sum = 0; - for (int i = 0; i < ticks.Size; ++i) - sum += ticks[i].ShowLabel ? ticks[i].LabelSize.y : 0; - return sum; -} - //----------------------------------------------------------------------------- // Axis Utils //----------------------------------------------------------------------------- +void ConstrainAxis(ImPlotAxis& axis) { + axis.Range.Min = ConstrainNan(ConstrainInf(axis.Range.Min)); + axis.Range.Max = ConstrainNan(ConstrainInf(axis.Range.Max)); + if (ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale)) { + axis.Range.Min = ConstrainLog(axis.Range.Min); + axis.Range.Max = ConstrainLog(axis.Range.Max); + } + if (axis.Range.Max <= axis.Range.Min) + axis.Range.Max = axis.Range.Min + DBL_EPSILON; +} + void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col) { const ImVec4 col_label = GetStyleColorVec4(axis_flag); const ImVec4 col_grid = GetStyleColorVec4(axis_flag + 1); @@ -744,30 +736,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // CONSTRAINTS ------------------------------------------------------------ - plot.XAxis.Range.Min = ConstrainNan(ConstrainInf(plot.XAxis.Range.Min)); - plot.XAxis.Range.Max = ConstrainNan(ConstrainInf(plot.XAxis.Range.Max)); - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - plot.YAxis[i].Range.Min = ConstrainNan(ConstrainInf(plot.YAxis[i].Range.Min)); - plot.YAxis[i].Range.Max = ConstrainNan(ConstrainInf(plot.YAxis[i].Range.Max)); - } - - if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) - plot.XAxis.Range.Min = ConstrainLog(plot.XAxis.Range.Min); - if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) - plot.XAxis.Range.Max = ConstrainLog(plot.XAxis.Range.Max); - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - plot.YAxis[i].Range.Min = ConstrainLog(plot.YAxis[i].Range.Min); - if (ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) - plot.YAxis[i].Range.Max = ConstrainLog(plot.YAxis[i].Range.Max); - } - - if (plot.XAxis.Range.Max <= plot.XAxis.Range.Min) - plot.XAxis.Range.Max = plot.XAxis.Range.Min + DBL_EPSILON; - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].Range.Max <= plot.YAxis[i].Range.Min) - plot.YAxis[i].Range.Max = plot.YAxis[i].Range.Min + DBL_EPSILON; - } + ConstrainAxis(plot.XAxis); + for (int i = 0; i < IMPLOT_Y_AXES; i++) + ConstrainAxis(plot.YAxis[i]); // AXIS COLORS ----------------------------------------------------------------- @@ -822,31 +793,16 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } - // label ticks - if (gp.X.HasLabels) - LabelTicks(gp.XTicks, ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Scientific) || ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTickLabels); - - float max_label_widths[IMPLOT_Y_AXES]; - for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.Y[i].Present && gp.Y[i].HasLabels) { - LabelTicks(gp.YTicks[i], ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific) || ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTickLabels[i]); - max_label_widths[i] = MaxTickLabelWidth(gp.YTicks[i]); - } - else { - max_label_widths[i] = 0; - } - } - // plot bb const ImVec2 title_size = ImGui::CalcTextSize(title, NULL, true); 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); const float pad_left = (y_label ? txt_height + gp.Style.LabelPadding.x : 0) - + (gp.Y[0].HasLabels ? max_label_widths[0] + gp.Style.LabelPadding.x : 0); - const float pad_right = ((gp.Y[1].Present && gp.Y[1].HasLabels) ? max_label_widths[1] + gp.Style.LabelPadding.x : 0) + + (gp.Y[0].HasLabels ? gp.YTicks[0].MaxWidth + gp.Style.LabelPadding.x : 0); + const float pad_right = ((gp.Y[1].Present && gp.Y[1].HasLabels) ? gp.YTicks[1].MaxWidth + gp.Style.LabelPadding.x : 0) + ((gp.Y[1].Present && gp.Y[2].Present) ? gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y : 0) - + ((gp.Y[2].Present && gp.Y[2].HasLabels) ? max_label_widths[2] + gp.Style.LabelPadding.x : 0); + + ((gp.Y[2].Present && gp.Y[2].HasLabels) ? gp.YTicks[2].MaxWidth + gp.Style.LabelPadding.x : 0); gp.BB_Plot = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot)); @@ -860,7 +816,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // axis label reference gp.YAxisReference[0] = gp.BB_Plot.Min.x; gp.YAxisReference[1] = gp.BB_Plot.Max.x; - gp.YAxisReference[2] = !gp.Y[1].Present ? gp.BB_Plot.Max.x : (gp.YAxisReference[1] + (gp.Y[1].HasLabels ? gp.Style.LabelPadding.x + max_label_widths[1] : 0) + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y); + gp.YAxisReference[2] = !gp.Y[1].Present ? gp.BB_Plot.Max.x : (gp.YAxisReference[1] + (gp.Y[1].HasLabels ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y); // y axis regions bb and hover ImRect yAxisRegion_bb[IMPLOT_Y_AXES]; @@ -1148,17 +1104,17 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // render axes PushPlotClipRect(); - // transform ticks + // transform ticks (TODO: Move this into ImPlotTickCollection) if (gp.RenderX) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks.Ticks[t]; xt->PixelPos = PlotToPixels(xt->PlotPos, 0, 0).x; } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { if (gp.RenderY[i]) { for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImPlotTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; yt->PixelPos = PlotToPixels(0, yt->PlotPos, i).y; } } @@ -1171,7 +1127,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min); for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick& xt = gp.XTicks[t]; + ImPlotTick& xt = gp.XTicks.Ticks[t]; if (xt.Major) DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), gp.Col_X.Major, gp.Style.MajorGridSize.x); else if (density < 0.2f) @@ -1186,7 +1142,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min); for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImPlotTick& yt = gp.YTicks[i][t]; + ImPlotTick& yt = gp.YTicks[i].Ticks[t]; if (yt.Major) DrawList.AddLine(ImVec2(gp.BB_Plot.Min.x, yt.PixelPos), ImVec2(gp.BB_Plot.Max.x, yt.PixelPos), gp.Col_Y[i].Major, gp.Style.MajorGridSize.y); else if (density < 0.2f) @@ -1220,19 +1176,19 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true); if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks.Ticks[t]; if (xt->ShowLabel && xt->PixelPos >= gp.BB_Plot.Min.x - 1 && xt->PixelPos <= gp.BB_Plot.Max.x + 1) - DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, gp.BB_Plot.Max.y + gp.Style.LabelPadding.y), xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTickLabels.Buf.Data + xt->BufferOffset); + DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, gp.BB_Plot.Max.y + gp.Style.LabelPadding.y), xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetLabel(t)); } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { if (gp.Y[i].Present && ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { for (int t = 0; t < gp.YTicks[i].Size; t++) { - const float x_start = gp.YAxisReference[i] + (i == 0 ? (-gp.Style.LabelPadding.x - gp.YTicks[i][t].LabelSize.x) : gp.Style.LabelPadding.x); - ImPlotTick *yt = &gp.YTicks[i][t]; + const float x_start = gp.YAxisReference[i] + (i == 0 ? (-gp.Style.LabelPadding.x - gp.YTicks[i].Ticks[t].LabelSize.x) : gp.Style.LabelPadding.x); + ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; if (yt->ShowLabel && yt->PixelPos >= gp.BB_Plot.Min.y - 1 && yt->PixelPos <= gp.BB_Plot.Max.y + 1) { ImVec2 start(x_start, yt->PixelPos - 0.5f * yt->LabelSize.y); - DrawList.AddText(start, yt->Major ? gp.Col_Y[i].MajTxt : gp.Col_Y[i].MinTxt, gp.YTickLabels[i].Buf.Data + yt->BufferOffset); + DrawList.AddText(start, yt->Major ? gp.Col_Y[i].MajTxt : gp.Col_Y[i].MinTxt, gp.YTicks[i].GetLabel(t)); } } } @@ -1373,12 +1329,17 @@ void ShowPlotContextMenu(ImPlotState& plot) { if (ImGui::MenuItem("Legend",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Legend))) { ImFlipFlag(plot.Flags, ImPlotFlags_Legend); } -#if 0 - if (ImGui::BeginMenu("Metrics")) { - ImGui::PushItemWidth(75); +#ifdef IMPLOT_DEBUG + if (ImGui::BeginMenu("Debug")) { + ImGui::PushItemWidth(50); ImGui::LabelText("Plots", "%d", gp.Plots.GetSize()); - ImGui::LabelText("Color Modifiers", "%d", gp.ColorModifiers.size()); - ImGui::LabelText("Style Modifiers", "%d", gp.StyleModifiers.size()); + ImGui::LabelText("Color Mods", "%d", gp.ColorModifiers.size()); + ImGui::LabelText("Style Mods", "%d", gp.StyleModifiers.size()); + ImGui::TextUnformatted(gp.XTicks.Labels.Buf.Data, gp.XTicks.Labels.Buf.Data + gp.XTicks.Labels.size()); + ImGui::TextUnformatted(gp.YTicks[0].Labels.Buf.Data, gp.YTicks[0].Labels.Buf.Data + gp.YTicks[0].Labels.size()); + // ImGui::TextUnformatted(gp.YTicks[1].Labels.Buf.Data, gp.YTicks[1].Labels.Buf.Data + gp.YTicks[1].Labels.size()); + // ImGui::TextUnformatted(gp.YTicks[2].Labels.Buf.Data, gp.YTicks[2].Labels.Buf.Data + gp.YTicks[2].Labels.size()); + ImGui::PopItemWidth(); ImGui::EndMenu(); } @@ -1412,7 +1373,7 @@ void EndPlot() { PushPlotClipRect(); if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks)) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImPlotTick *xt = &gp.XTicks[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, @@ -1432,7 +1393,7 @@ void EndPlot() { float direction = (i == 0) ? 1.0f : -1.0f; bool no_major = axis_count >= 3; for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImPlotTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; ImVec2 start = ImVec2(x_start, yt->PixelPos); DrawList.AddLine(start, start + ImVec2(direction * ((!no_major && yt->Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y), 0), @@ -1605,7 +1566,7 @@ void EndPlot() { writer.Write("%.3E", gp.MousePos[0].x); } else { - double range_x = gp.XTicks.Size > 1 ? (gp.XTicks[1].PlotPos - gp.XTicks[0].PlotPos) : plot.XAxis.Range.Size(); + 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); } // y1 @@ -1613,7 +1574,7 @@ void EndPlot() { writer.Write(",%.3E", gp.MousePos[0].y); } else { - double range_y = gp.YTicks[0].Size > 1 ? (gp.YTicks[0][1].PlotPos - gp.YTicks[0][0].PlotPos) : plot.YAxis[0].Range.Size(); + 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); } // y2 @@ -1622,7 +1583,7 @@ void EndPlot() { writer.Write(",(%.3E)", gp.MousePos[1].y); } else { - double range_y = gp.YTicks[1].Size > 1 ? (gp.YTicks[1][1].PlotPos - gp.YTicks[1][0].PlotPos) : plot.YAxis[1].Range.Size(); + 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); } } @@ -1632,7 +1593,7 @@ void EndPlot() { writer.Write(",(%.3E)", gp.MousePos[2].y); } else { - double range_y = gp.YTicks[2].Size > 1 ? (gp.YTicks[2][1].PlotPos - gp.YTicks[2][0].PlotPos) : plot.YAxis[2].Range.Size(); + 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); } } @@ -1761,7 +1722,7 @@ void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels, b 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, gp.XTickLabels); + AddTicksCustom(values, labels, n_ticks, gp.XTicks); } void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels, bool show_default) { @@ -1776,7 +1737,7 @@ void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels, b 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], gp.YTickLabels[y_axis]); + AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis]); } void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels, bool show_default, int y_axis) { @@ -2239,18 +2200,14 @@ ImVec4 NextColormapColor() { void ShowColormapScale(double scale_min, double scale_max, float height) { ImPlotContext& gp = *GImPlot; - static ImVector ticks; - static ImGuiTextBuffer txt_buff; + static ImPlotTickCollection ticks; + ticks.Reset(); ImPlotRange range; range.Min = scale_min; range.Max = scale_max; - ticks.shrink(0); - txt_buff.Buf.shrink(0); + AddTicksDefault(range, 10, 0, ticks); - LabelTicks(ticks, false, txt_buff); - float max_width = 0; - for (int i = 0; i < ticks.Size; ++i) - max_width = ticks[i].LabelSize.x > max_width ? ticks[i].LabelSize.x : max_width; + ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; @@ -2260,7 +2217,7 @@ void ShowColormapScale(double scale_min, double scale_max, float height) { const float bar_w = 20; ImDrawList &DrawList = *Window->DrawList; - ImVec2 size(bar_w + txt_off + max_width + 2 * gp.Style.PlotPadding.x, height); + ImVec2 size(bar_w + txt_off + ticks.MaxWidth + 2 * gp.Style.PlotPadding.x, height); ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + size); ImGui::ItemSize(bb_frame); if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) @@ -2280,10 +2237,10 @@ void ShowColormapScale(double scale_min, double scale_max, float height) { ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); for (int i = 0; i < ticks.Size; ++i) { - float ypos = ImRemap((float)ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); + float ypos = ImRemap((float)ticks.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); if (ypos < bb_grad.Max.y - 2 && ypos > bb_grad.Min.y + 2) - DrawList.AddLine(ImVec2(bb_grad.Max.x-1, ypos), ImVec2(bb_grad.Max.x - (ticks[i].Major ? 10.0f : 5.0f), ypos), col_border, 1.0f); - DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks[i].LabelSize.y * 0.5f), ImGui::GetColorU32(ImGuiCol_Text), txt_buff.Buf.Data + ticks[i].BufferOffset); + DrawList.AddLine(ImVec2(bb_grad.Max.x-1, ypos), ImVec2(bb_grad.Max.x - (ticks.Ticks[i].Major ? 10.0f : 5.0f), ypos), col_border, 1.0f); + DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks.Ticks[i].LabelSize.y * 0.5f), ImGui::GetColorU32(ImGuiCol_Text), ticks.GetLabel(i)); } ImGui::PopClipRect(); diff --git a/implot_internal.h b/implot_internal.h index db71914..40f4907 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -176,16 +176,56 @@ struct ImPlotTick int BufferOffset; bool Major; bool ShowLabel; - bool Labeled; ImPlotTick(double value, bool major, bool show_label) { - PlotPos = value; - Major = major; - ShowLabel = show_label; - Labeled = false; + PlotPos = value; + Major = major; + ShowLabel = show_label; + BufferOffset = -1; } }; +// Collection of ticks +struct ImPlotTickCollection { + ImVector Ticks; + ImGuiTextBuffer Labels; + float TotalWidth; + float TotalHeight; + float MaxWidth; + float MaxHeight; + int Size; + + void AddTick(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; + } + Ticks.push_back(tick); + Size++; + } + + void AddTick(double value, bool major, bool show_label, void (*labeler)(ImPlotTick& tick, ImGuiTextBuffer& buf)) { + ImPlotTick tick(value, major, show_label); + if (labeler) + labeler(tick, Labels); + AddTick(tick); + } + + const char* GetLabel(int idx) { + return Labels.Buf.Data + Ticks[idx].BufferOffset; + } + + void Reset() { + Ticks.shrink(0); + Labels.Buf.shrink(0); + TotalWidth = TotalHeight = MaxWidth = MaxHeight = 0; + Size = 0; + } + +}; + // Axis state information that must persist after EndPlot struct ImPlotAxis { @@ -337,10 +377,8 @@ struct ImPlotContext { ImPlotAxisState Y[IMPLOT_Y_AXES]; // Tick Marks and Labels - ImVector XTicks; - ImVector YTicks[IMPLOT_Y_AXES]; - ImGuiTextBuffer XTickLabels; - ImGuiTextBuffer YTickLabels[IMPLOT_Y_AXES]; + ImPlotTickCollection XTicks; + ImPlotTickCollection YTicks[IMPLOT_Y_AXES]; float YAxisReference[IMPLOT_Y_AXES]; // Transformations and Data Extents @@ -437,25 +475,24 @@ int GetLegendCount(); // Gets the ith entry string for the current legend const char* GetLegendLabel(int i); +// Label a tick with default formatting +void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer); +// Label a tick with scientific formating +void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer); // Populates a list of ImPlotTicks with normal spaced and formatted ticks -void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImVector &out); +void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTickCollection& ticks); // Populates a list of ImPlotTicks with logarithmic space and formatted ticks -void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImVector& out); +void AddTicksLogarithmic(const ImPlotRange& range, int nMajor, ImPlotTickCollection& ticks); // Populates a list of ImPlotTicks with custom spaced and labeled ticks -void AddTicksCustom(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer); -// Creates label information for a list of ImPlotTick -void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer); -// Gets the widest visible (i.e. ShowLabel = true) label size from a list of ticks -float MaxTickLabelWidth(const ImVector& ticks); -// Sums the widths of visible ticks (i.e. ShowLabel = true) ticks -float SumTickLabelWidth(const ImVector& ticks); -// Sums the heights of visible (i.e. ShowLabel = true) ticks -float SumTickLabelHeight(const ImVector& ticks); +void AddTicksCustom(const double* values, const char** labels, int n, ImPlotTickCollection& ticks); + +// Constrains an axis range +void ConstrainAxis(ImPlotAxis& axis); +// Updates axis ticks, lins, and label colors +void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col); // Rounds x to powers of 2,5 and 10 for generating axis labels (from Graphics Gems 1 Chapter 11.2) double NiceNum(double x, bool round); -// Updates axis ticks, lins, and label colors -void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col); // Draws vertical text. The position is the bottom left of the text rect. void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = NULL); // Calculates the size of vertical text