From 13e430a9e5d5920c887d1596d6db6aeaa9906582 Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 19 Sep 2020 20:54:19 -0500 Subject: [PATCH] add annotation API --- implot.cpp | 288 +++++++++++++++++++++++++++------------------- implot.h | 74 +++++++----- implot_demo.cpp | 34 ++++++ implot_internal.h | 84 +++++++++++--- implot_items.cpp | 34 +++--- 5 files changed, 337 insertions(+), 177 deletions(-) diff --git a/implot.cpp b/implot.cpp index f258b51..8b8d8a2 100644 --- a/implot.cpp +++ b/implot.cpp @@ -95,29 +95,31 @@ ImPlotInputMap::ImPlotInputMap() { ImPlotStyle::ImPlotStyle() { - LineWeight = 1; - Marker = ImPlotMarker_None; - MarkerSize = 4; - MarkerWeight = 1; - FillAlpha = 1; - ErrorBarSize = 5; - ErrorBarWeight = 1.5f; - DigitalBitHeight = 8; - DigitalBitGap = 4; + LineWeight = 1; + Marker = ImPlotMarker_None; + MarkerSize = 4; + MarkerWeight = 1; + FillAlpha = 1; + ErrorBarSize = 5; + ErrorBarWeight = 1.5f; + DigitalBitHeight = 8; + DigitalBitGap = 4; - PlotBorderSize = 1; - MinorAlpha = 0.25f; - MajorTickLen = ImVec2(10,10); - MinorTickLen = ImVec2(5,5); - MajorTickSize = ImVec2(1,1); - MinorTickSize = ImVec2(1,1); - MajorGridSize = ImVec2(1,1); - MinorGridSize = ImVec2(1,1); - PlotPadding = ImVec2(8,8); - LabelPadding = ImVec2(5,5); - LegendPadding = ImVec2(10,10); - InfoPadding = ImVec2(10,10); - PlotMinSize = ImVec2(300,225); + PlotBorderSize = 1; + MinorAlpha = 0.25f; + MajorTickLen = ImVec2(10,10); + MinorTickLen = ImVec2(5,5); + MajorTickSize = ImVec2(1,1); + MinorTickSize = ImVec2(1,1); + MajorGridSize = ImVec2(1,1); + MinorGridSize = ImVec2(1,1); + PlotPadding = ImVec2(8,8); + LabelPadding = ImVec2(5,5); + LegendPadding = ImVec2(10,10); + InfoPadding = ImVec2(10,10); + AnnotationPadding = ImVec2(2,2); + AnnotationOffset = ImVec2(10,10); + PlotMinSize = ImVec2(300,225); ImPlot::StyleColorsAuto(this); @@ -215,29 +217,31 @@ struct ImPlotStyleVarInfo { static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight - { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight + { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, InfoPadding) }, // ImPlotStyleVar_InfoPadding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotPadding) }, // ImPlotStyleVar_PlotPadding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendPadding) }, // ImPlotStyleVar_LegendPadding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, InfoPadding) }, // ImPlotStyleVar_InfoPadding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationOffset) }, // ImPlotStyleVar_AnnotationOffset + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize }; static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { @@ -364,6 +368,8 @@ void Reset(ImPlotContext* ctx) { for (int i = 0; i < 3; ++i) { ctx->YTicks[i].Reset(); } + // reset labels + ctx->Annotations.Reset(); // reset extents/fit ctx->FitThisFrame = false; ctx->FitX = false; @@ -378,8 +384,9 @@ void Reset(ImPlotContext* ctx) { ctx->DigitalPlotItemCnt = 0; ctx->DigitalPlotOffset = 0; // nullify plot - ctx->CurrentPlot = NULL; - ctx->CurrentItem = NULL; + ctx->CurrentPlot = NULL; + ctx->CurrentItem = NULL; + ctx->PreviousItem = NULL; } //----------------------------------------------------------------------------- @@ -516,20 +523,20 @@ const char* GetLegendLabel(int i) { void LabelTickDefault(ImPlotTick& tick, ImGuiTextBuffer& buffer) { char temp[32]; if (tick.ShowLabel) { - tick.BufferOffset = buffer.size(); + 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.BufferOffset); + tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset); } } void LabelTickScientific(ImPlotTick& tick, ImGuiTextBuffer& buffer) { char temp[32]; if (tick.ShowLabel) { - tick.BufferOffset = buffer.size(); + 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.BufferOffset); + tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset); } } @@ -540,11 +547,11 @@ void AddTicksDefault(const ImPlotRange& range, int nMajor, int nMinor, ImPlotTic const double graphmax = ceil(range.Max / interval) * interval; for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { if (range.Contains(major)) - ticks.AddTick(major, true, true, LabelTickDefault); + ticks.Append(major, true, true, LabelTickDefault); for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (range.Contains(minor)) - ticks.AddTick(minor, false, true, LabelTickDefault); + ticks.Append(minor, false, true, LabelTickDefault); } } } @@ -566,7 +573,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.AddTick(major1, true, true, LabelTickScientific); + ticks.Append(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)); @@ -574,7 +581,7 @@ 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.AddTick(minor, false, false, LabelTickScientific); + ticks.Append(minor, false, false, LabelTickScientific); } } @@ -585,14 +592,14 @@ void AddTicksCustom(const double* values, const char* const labels[], int n, ImP 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.TextOffset = ticks.TextBuffer.size(); + ticks.TextBuffer.append(labels[i], labels[i] + strlen(labels[i]) + 1); tick.LabelSize = ImGui::CalcTextSize(labels[i]); } else { - LabelTickDefault(tick, ticks.Labels); + LabelTickDefault(tick, ticks.TextBuffer); } - ticks.AddTick(tick); + ticks.Append(tick); } } @@ -959,10 +966,10 @@ void PrintTime(const ImPlotTime& t, ImPlotTimeFmt fmt) { inline void LabelTickTime(ImPlotTick& tick, ImGuiTextBuffer& buffer, const ImPlotTime& t, ImPlotTimeFmt fmt, bool hour24) { char temp[32]; if (tick.ShowLabel) { - tick.BufferOffset = buffer.size(); + tick.TextOffset = buffer.size(); hour24 ? FormatTime24(t, temp, 32, fmt) : FormatTime12(t, temp, 32, fmt); buffer.append(temp, temp + strlen(temp) + 1); - tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.BufferOffset); + tick.LabelSize = ImGui::CalcTextSize(buffer.Buf.Data + tick.TextOffset); } } @@ -1053,17 +1060,17 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, bool hour24, ImPlo // minor level 0 tick ImPlotTick tick_min(t1.ToDouble(),true,true); tick_min.Level = 0; - LabelTickTime(tick_min,ticks.Labels,t1,fmt0,hour24); - ticks.AddTick(tick_min); + LabelTickTime(tick_min,ticks.TextBuffer,t1,fmt0,hour24); + ticks.Append(tick_min); // major level 1 tick ImPlotTick tick_maj(t1.ToDouble(),true,true); tick_maj.Level = 1; - LabelTickTime(tick_maj,ticks.Labels,t1, last_major == NULL ? fmtf : fmt1,hour24); - const char* this_major = ticks.Labels.Buf.Data + tick_maj.BufferOffset; + LabelTickTime(tick_maj,ticks.TextBuffer,t1, last_major == NULL ? fmtf : fmt1,hour24); + const char* this_major = ticks.TextBuffer.Buf.Data + tick_maj.TextOffset; if (last_major && TimeLabelSame(last_major,this_major)) tick_maj.ShowLabel = false; last_major = this_major; - ticks.AddTick(tick_maj); + ticks.Append(tick_maj); } // add minor ticks up until next major if (minor_per_major > 1 && (t_min <= t2 && t1 <= t_max)) { @@ -1073,14 +1080,14 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, bool hour24, ImPlo if (t12 >= t_min && t12 <= t_max) { ImPlotTick tick(t12.ToDouble(),false,px_to_t2 >= fmt0_width); tick.Level = 0; - LabelTickTime(tick,ticks.Labels,t12,fmt0,hour24); - ticks.AddTick(tick); + LabelTickTime(tick,ticks.TextBuffer,t12,fmt0,hour24); + ticks.Append(tick); if (last_major == NULL && px_to_t2 >= fmt0_width && px_to_t2 >= (fmt1_width + fmtf_width) / 2) { ImPlotTick tick_maj(t12.ToDouble(),true,true); tick_maj.Level = 1; - LabelTickTime(tick_maj,ticks.Labels,t12,fmtf,hour24); - last_major = ticks.Labels.Buf.Data + tick_maj.BufferOffset; - ticks.AddTick(tick_maj); + LabelTickTime(tick_maj,ticks.TextBuffer,t12,fmtf,hour24); + last_major = ticks.TextBuffer.Buf.Data + tick_maj.TextOffset; + ticks.Append(tick_maj); } } t12 = AddTime(t12, unit0, step); @@ -1106,8 +1113,8 @@ void AddTicksTime(const ImPlotRange& range, float plot_width, bool hour24, ImPlo if (t >= t_min && t <= t_max) { ImPlotTick tick(t.ToDouble(), true, true); tick.Level = 0; - LabelTickTime(tick, ticks.Labels, t, TimeFormatLevel0[ImPlotTimeUnit_Yr], hour24); - ticks.AddTick(tick); + LabelTickTime(tick, ticks.TextBuffer, t, TimeFormatLevel0[ImPlotTimeUnit_Yr], hour24); + ticks.Append(tick); } } } @@ -1707,7 +1714,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons 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->Level * (txt_height + gp.Style.LabelPadding.y)), - xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetLabel(t)); + xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetText(t)); } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { @@ -1717,7 +1724,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons 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.YTicks[i].GetLabel(t)); + DrawList.AddText(start, yt->Major ? gp.Col_Y[i].MajTxt : gp.Col_Y[i].MinTxt, gp.YTicks[i].GetText(t)); } } } @@ -1930,8 +1937,8 @@ void ShowPlotContextMenu(ImPlotState& plot) { ImGui::LabelText("Plots", "%d", gp.Plots.GetSize()); 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.XTicks.TextBuffer.Buf.Data, gp.XTicks.TextBuffer.Buf.Data + gp.XTicks.TextBuffer.size()); + ImGui::TextUnformatted(gp.YTicks[0].TextBuffer.Buf.Data, gp.YTicks[0].TextBuffer.Buf.Data + gp.YTicks[0].TextBuffer.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()); @@ -2008,6 +2015,47 @@ void EndPlot() { } ImGui::PopClipRect(); + // render annotations + PushPlotClipRect(); + for (int i = 0; i < gp.Annotations.Size; ++i) { + const char* txt = gp.Annotations.GetText(i); + ImPlotAnnotation& an = gp.Annotations.Annotations[i]; + const ImVec2 txt_size = ImGui::CalcTextSize(txt); + const ImVec2 size = txt_size + gp.Style.AnnotationPadding * 2; + ImVec2 pos = an.Pos; + if (an.Offset.x == 0) + pos.x -= size.x / 2; + else if (an.Offset.x > 0) + pos.x += an.Offset.x; + else + pos.x -= size.x - an.Offset.x; + if (an.Offset.y == 0) + pos.y -= size.y / 2; + else if (an.Offset.y > 0) + pos.y += an.Offset.y; + else + pos.y -= size.y - an.Offset.y; + if (an.Clamp) + pos = ClampLabelPos(pos, size, gp.BB_Plot.Min, gp.BB_Plot.Max); + ImRect rect(pos,pos+size); + if (an.Offset.x != 0 || an.Offset.y != 0) { + ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; + int min_corner = 0; + float min_len = FLT_MAX; + for (int c = 0; c < 4; ++c) { + float len = ImLengthSqr(an.Pos - corners[c]); + if (len < min_len) { + min_corner = c; + min_len = len; + } + } + DrawList.AddLine(an.Pos, corners[min_corner], an.ColorBg); + } + DrawList.AddRectFilled(rect.Min, rect.Max, an.ColorBg); + DrawList.AddText(pos + gp.Style.AnnotationPadding, an.ColorFg, txt); + } + PopPlotClipRect(); + // render y-axis drag/drop hover if ((gp.Y[1].Present || gp.Y[2].Present) && ImGui::IsDragDropPayloadBeingAccepted()) { for (int i = 0; i < IMPLOT_Y_AXES; ++i) { @@ -2483,6 +2531,43 @@ ImPlotLimits GetPlotQuery(int y_axis_in) { return result; } +void AnnotateEx(double x, double y, bool clamp, const ImVec4& col, const ImVec2& off, const char* fmt, va_list args) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Annotate() needs to be called between BeginPlot() and EndPlot()!"); + ImVec2 pos = PlotToPixels(x,y); + ImU32 bg = ImGui::GetColorU32(col); + ImU32 fg = col.w == 0 ? GetStyleColorU32(ImPlotCol_InlayText) : CalcTextColor(col); + gp.Annotations.AppendV(pos, off, bg, fg, clamp, fmt, args); +} + +void Annotate(double x, double y, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AnnotateEx(x,y,false,ImVec4(0,0,0,0),GImPlot->Style.AnnotationOffset,fmt,args); + va_end(args); +} + +void Annotate(double x, double y, const ImVec4& col, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AnnotateEx(x,y,false,col,GImPlot->Style.AnnotationOffset,fmt,args); + va_end(args); +} + +void AnnotateClamped(double x, double y, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AnnotateEx(x,y,true,ImVec4(0,0,0,0),GImPlot->Style.AnnotationOffset,fmt,args); + va_end(args); +} + +void AnnotateClamped(double x, double y, const ImVec4& col, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AnnotateEx(x,y,true,col,GImPlot->Style.AnnotationOffset,fmt,args); + va_end(args); +} + bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col, float thickness) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); @@ -2509,22 +2594,12 @@ bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col ImGui::GetCurrentWindow()->DC.CursorPos = new_cursor_pos; ImGui::InvisibleButton(id, ImVec2(grab_size, yb-yt)); ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; - if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { gp.Hov_Plot = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (show_label) { - PushPlotClipRect(); double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); - char buf[32]; - snprintf(buf, 32, "%s = %.*f", id, Precision(range_x), *value); - ImVec2 size = ImGui::CalcTextSize(buf); - const int pad = 2; - ImVec2 label_pos = ImVec2(x - size.x/2 - pad, yb - size.y - 2*pad); - ImVec2 label_size = size + ImVec2(pad*2,pad*2); - label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); - DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); - DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); - PopPlotClipRect(); + gp.Annotations.Append(ImVec2(x,yb),ImVec2(0,0),col32,CalcTextColor(color),true,"%s = %.*f", id, Precision(range_x), *value); } } bool dragging = false; @@ -2568,27 +2643,8 @@ bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col gp.Hov_Plot = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); if (show_label) { - PushPlotClipRect(); double range_y = gp.YTicks[yax].Size > 1 ? (gp.YTicks[yax].Ticks[1].PlotPos - gp.YTicks[yax].Ticks[0].PlotPos) : gp.CurrentPlot->YAxis[yax].Range.Size(); - char buf[32]; - snprintf(buf, 32, "%s = %.*f", id, Precision(range_y), *value); - ImVec2 size = ImGui::CalcTextSize(buf); - const int pad = 2; - if (yax == 0) { - ImVec2 label_pos = ImVec2(xl,y-pad-size.y/2); - ImVec2 label_size = size + ImVec2(pad*2,pad*2); - label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); - DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); - DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); - } - else { - ImVec2 label_pos = ImVec2(xr-size.x-2*pad,y-pad-size.y/2); - ImVec2 label_size = size + ImVec2(pad*2,pad*2); - label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); - DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); - DrawList.AddText(label_pos + ImVec2(pad,pad),CalcTextColor(color),buf); - } - PopPlotClipRect(); + gp.Annotations.Append(ImVec2(yax == 0 ? xl : xr,y), ImVec2(0,0), col32, CalcTextColor(color), true, "%s = %.*f", id, Precision(range_y), *value); } } bool dragging = false; @@ -2624,18 +2680,10 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe gp.Hov_Plot = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); if (show_label) { - PushPlotClipRect(); double range_x = gp.XTicks.Size > 1 ? (gp.XTicks.Ticks[1].PlotPos - gp.XTicks.Ticks[0].PlotPos) : gp.CurrentPlot->XAxis.Range.Size(); double range_y = gp.YTicks[yax].Size > 1 ? (gp.YTicks[yax].Ticks[1].PlotPos - gp.YTicks[yax].Ticks[0].PlotPos) : gp.CurrentPlot->YAxis[yax].Range.Size(); - char buf[64]; - const float yb = gp.BB_Plot.Max.y; - snprintf(buf, 64, "%s = %.*f,%.*f", id, Precision(range_x), *x, Precision(range_y), *y); ImVec2 label_pos = pos + ImVec2(16 * GImGui->Style.MouseCursorScale, 8 * GImGui->Style.MouseCursorScale); - ImVec2 label_size = ImGui::CalcTextSize(buf) + ImVec2(4,4); - label_pos = ClampLabelPos(label_pos, label_size, gp.BB_Plot.Min, gp.BB_Plot.Max); - DrawList.AddRectFilled(label_pos, label_pos + label_size, col32); - DrawList.AddText(label_pos + ImVec2(2,2), CalcTextColor(color), buf); - PopPlotClipRect(); + gp.Annotations.Append(label_pos, ImVec2(0.0001f,0.00001f), col32, CalcTextColor(color), true, "%s = %.*f,%.*f", id, Precision(range_x), *x, Precision(range_y), *y); } } bool dragging = false; @@ -3133,7 +3181,7 @@ void ShowColormapScale(double scale_min, double scale_max, float height) { 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.Ticks[i].Major ? 10.0f : 5.0f), ypos), col_tick, 1.0f); - DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks.Ticks[i].LabelSize.y * 0.5f), GetStyleColorU32(ImPlotCol_TitleText), ticks.GetLabel(i)); + DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks.Ticks[i].LabelSize.y * 0.5f), GetStyleColorU32(ImPlotCol_TitleText), ticks.GetText(i)); } ImGui::PopClipRect(); @@ -3221,12 +3269,14 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f"); + ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); ImGui::Text("Plot Padding"); ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("InfoPadding", (float*)&style.InfoPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); + ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); + ImGui::SliderFloat2("AnnotationOffset", (float*)&style.AnnotationOffset, -20.0f, 20.0f, "%.0f"); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Colors")) { diff --git a/implot.h b/implot.h index 2d79953..3d6c4bc 100644 --- a/implot.h +++ b/implot.h @@ -125,29 +125,31 @@ enum ImPlotCol_ { // Plot styling variables. enum ImPlotStyleVar_ { // item styling variables - ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels - ImPlotStyleVar_Marker, // int, marker specification - ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") - ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels - ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills - ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels - ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels - ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels - ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels + ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels + ImPlotStyleVar_Marker, // int, marker specification + ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") + ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels + ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills + ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels + ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels + ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels + ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels // plot styling variables - ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area - ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines - ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes - ImPlotStyleVar_MinorTickLen, // ImVec2, minor tick lengths for X and Y axes - ImPlotStyleVar_MajorTickSize, // ImVec2, line thickness of major ticks - ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks - ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines - ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor grid lines - ImPlotStyleVar_PlotPadding, // ImVec2, padding between widget frame and plot area and/or labels - ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge - ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from top-left of plot - ImPlotStyleVar_InfoPadding, // ImVec2, padding between plot edge and interior info text - ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk + ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area + ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines + ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes + ImPlotStyleVar_MinorTickLen, // ImVec2, minor tick lengths for X and Y axes + ImPlotStyleVar_MajorTickSize, // ImVec2, line thickness of major ticks + ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks + ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines + ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor grid lines + ImPlotStyleVar_PlotPadding, // ImVec2, padding between widget frame and plot area and/or labels + ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge + ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from top-left of plot + ImPlotStyleVar_InfoPadding, // ImVec2, padding between plot edge and interior info text + ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels + ImPlotStyleVar_AnnotationOffset, // ImVec2, annotation label offset in pixels (0,0 will center the label) + ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk ImPlotStyleVar_COUNT }; @@ -238,6 +240,8 @@ struct ImPlotStyle { ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge ImVec2 LegendPadding; // = 10,10 legend padding from top-left of plot ImVec2 InfoPadding; // = 10,10 padding between plot edge and interior info text + ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels + ImVec2 AnnotationOffset; // = 10,10 annotation label offset in pixels (0,0 will center the label) ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk // colors ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors @@ -398,7 +402,7 @@ template IMPLOT_API void PlotDigital(const char* label_id, const T* IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, const ImVec2& uv0=ImVec2(0,0), const ImVec2& uv1=ImVec2(1,1), const ImVec4& tint_col=ImVec4(1,1,1,1)); // Plots a centered text label at point x,y with optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). -IMPLOT_API void PlotText(const char* text, double x, double y, bool vertical=false, const ImVec2& pixel_offset=ImVec2(0,0)); +IMPLOT_API void PlotText(const char* text, double x, double y, bool vertical=false, const ImVec2& pix_offset=ImVec2(0,0)); //----------------------------------------------------------------------------- // Plot Utils @@ -453,10 +457,22 @@ IMPLOT_API ImPlotPoint GetPlotMousePos(int y_axis = IMPLOT_AUTO); // Returns the current plot axis range. A negative y_axis uses the current value of SetPlotYAxis (0 initially). IMPLOT_API ImPlotLimits GetPlotLimits(int y_axis = IMPLOT_AUTO); +// Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query. +IMPLOT_API bool IsPlotQueried(); +// Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query. +IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); + //----------------------------------------------------------------------------- // Plot Tools //----------------------------------------------------------------------------- +// Shows an annotation callout at a chosen point. Annotations can be offset or centered with ImPlotStyleVar_AnnotationOffset. +IMPLOT_API void Annotate(double x, double y, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void Annotate(double x, double y, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(4); +// Same as above, but the annotation will always be clamped to stay inside the plot area. +IMPLOT_API void AnnotateClamped(double x, double y, const char* fmt, ...) IM_FMTARGS(3); +IMPLOT_API void AnnotateClamped(double x, double y, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(4); + // Shows a draggable vertical guide line at an x-value. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragLineX(const char* id, double* x_value, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float thickness = 1); // Shows a draggable horizontal guide line at a y-value. #col defaults to ImGuiCol_Text. @@ -464,11 +480,6 @@ IMPLOT_API bool DragLineY(const char* id, double* y_value, bool show_label = tru // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 4); -// Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query. -IMPLOT_API bool IsPlotQueried(); -// Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query. -IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); - //----------------------------------------------------------------------------- // Legend Utils and Tools //----------------------------------------------------------------------------- @@ -533,10 +544,13 @@ IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size // Set the error bar style for the next item only. IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); +// Gets the last item primary color (i.e. its legend icon color) +IMPLOT_API ImVec4 GetLastItemColor(); + // Returns the null terminated string name for an ImPlotCol. -IMPLOT_API const char* GetStyleColorName(ImPlotCol color); +IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); // Returns the null terminated string name for an ImPlotMarker. -IMPLOT_API const char* GetMarkerName(ImPlotMarker marker); +IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); //----------------------------------------------------------------------------- // Colormaps diff --git a/implot_demo.cpp b/implot_demo.cpp index 83369a2..697c0dc 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -659,6 +659,7 @@ void ShowDemoWindow(bool* p_open) { double t_now = (double)time(0); double y_now = HugeTimeData::GetY(t_now); ImPlot::PlotScatter("Now",&t_now,&y_now,1); + ImPlot::Annotate(t_now,y_now,ImPlot::GetLastItemColor(),"Now"); ImPlot::EndPlot(); } } @@ -857,6 +858,39 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } } + if (ImGui::CollapsingHeader("Annotations")) { + static bool clamp = false; + ImGui::Checkbox("Clamp",&clamp); + ImPlot::SetNextPlotLimits(0,2,0,1); + if (ImPlot::BeginPlot("##Annotations")) { + + static float p[] = {0.25f, 0.25f, 0.75f, 0.75f, 0.25f}; + ImPlot::PlotScatter("##Points",&p[0],&p[1],4); + ImVec4 col = GetLastItemColor(); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(-15,15)); + clamp ? ImPlot::AnnotateClamped(0.25,0.25,col,"BL") : ImPlot::Annotate(0.25,0.25,col,"BL"); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(15,15)); + clamp ? ImPlot::AnnotateClamped(0.75,0.25,col,"BR") : ImPlot::Annotate(0.75,0.25,col,"BR"); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(15,-15)); + clamp ? ImPlot::AnnotateClamped(0.75,0.75,col,"TR") : ImPlot::Annotate(0.75,0.75,col,"TR"); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(-15,-15)); + clamp ? ImPlot::AnnotateClamped(0.25,0.75,col,"TL") : ImPlot::Annotate(0.25,0.75,col,"TL"); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(0,0)); + clamp ? ImPlot::AnnotateClamped(0.5,0.5,col,"Center") : ImPlot::Annotate(0.5,0.5,col,"Center"); + ImPlot::PopStyleVar(5); + + float bx[] = {1.2f,1.5f,1.8f}; + float by[] = {0.25f, 0.5f, 0.75f}; + ImPlot::PlotBars("##Bars",bx,by,4,0.2); + ImPlot::PushStyleVar(ImPlotStyleVar_AnnotationOffset, ImVec2(0,-5)); + for (int i = 0; i < 3; ++i) { + ImPlot::Annotate(bx[i],by[i],"B[%d]=%.2f",i,by[i]); + } + ImPlot::PopStyleVar(); + + ImPlot::EndPlot(); + } + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Drag and Drop")) { const int K_CHANNELS = 9; diff --git a/implot_internal.h b/implot_internal.h index 8dd7e63..1113068 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -77,9 +77,9 @@ extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer #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 -// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) +// 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) +// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 //----------------------------------------------------------------------------- @@ -135,7 +135,7 @@ struct ImBufferWriter Pos = 0; } - void Write(const char* fmt, ...) IM_FMTARGS(2) { + void Write(const char* fmt, ...) { va_list argp; va_start(argp, fmt); const int written = ::vsnprintf(&Buffer[Pos], Size - Pos - 1, fmt, argp); @@ -247,19 +247,68 @@ struct ImPlotColormapMod { struct ImPlotPointError { double X, Y, Neg, Pos; - ImPlotPointError(double x, double y, double neg, double pos) { X = x; Y = y; Neg = neg; Pos = pos; } }; +// Interior plot label/annotation +struct ImPlotAnnotation { + ImVec2 Pos; + ImVec2 Offset; + ImU32 ColorBg; + ImU32 ColorFg; + int TextOffset; + bool Clamp; +}; + +// Collection of plot labels +struct ImPlotAnnotationCollection { + + ImVector Annotations; + ImGuiTextBuffer TextBuffer; + int Size; + + ImPlotAnnotationCollection() { Reset(); } + + void AppendV(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, va_list args) IM_FMTLIST(7) { + ImPlotAnnotation an; + an.Pos = pos; an.Offset = off; + an.ColorBg = bg; an.ColorFg = fg; + an.TextOffset = TextBuffer.size(); + an.Clamp = clamp; + Annotations.push_back(an); + TextBuffer.appendfv(fmt, args); + const char nul[] = ""; + TextBuffer.append(nul,nul+1); + Size++; + } + + void Append(const ImVec2& pos, const ImVec2& off, ImU32 bg, ImU32 fg, bool clamp, const char* fmt, ...) IM_FMTARGS(7) { + va_list args; + va_start(args, fmt); + AppendV(pos, off, bg, fg, clamp, fmt, args); + va_end(args); + } + + const char* GetText(int idx) { + return TextBuffer.Buf.Data + Annotations[idx].TextOffset; + } + + void Reset() { + Annotations.shrink(0); + TextBuffer.Buf.shrink(0); + Size = 0; + } +}; + // Tick mark info struct ImPlotTick { double PlotPos; float PixelPos; ImVec2 LabelSize; - int BufferOffset; + int TextOffset; bool Major; bool ShowLabel; int Level; @@ -268,7 +317,7 @@ struct ImPlotTick PlotPos = value; Major = major; ShowLabel = show_label; - BufferOffset = -1; + TextOffset = -1; Level = 0; } }; @@ -276,14 +325,16 @@ struct ImPlotTick // Collection of ticks struct ImPlotTickCollection { ImVector Ticks; - ImGuiTextBuffer Labels; + ImGuiTextBuffer TextBuffer; float TotalWidth; float TotalHeight; float MaxWidth; float MaxHeight; int Size; - void AddTick(const ImPlotTick& tick) { + ImPlotTickCollection() { Reset(); } + + void Append(const ImPlotTick& tick) { if (tick.ShowLabel) { TotalWidth += tick.ShowLabel ? tick.LabelSize.x : 0; TotalHeight += tick.ShowLabel ? tick.LabelSize.y : 0; @@ -294,24 +345,23 @@ struct ImPlotTickCollection { Size++; } - void AddTick(double value, bool major, bool show_label, void (*labeler)(ImPlotTick& tick, ImGuiTextBuffer& buf)) { + void Append(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); + labeler(tick, TextBuffer); + Append(tick); } - const char* GetLabel(int idx) { - return Labels.Buf.Data + Ticks[idx].BufferOffset; + const char* GetText(int idx) { + return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } void Reset() { Ticks.shrink(0); - Labels.Buf.shrink(0); + TextBuffer.Buf.shrink(0); TotalWidth = TotalHeight = MaxWidth = MaxHeight = 0; Size = 0; } - }; // Axis state information that must persist after EndPlot @@ -544,6 +594,7 @@ struct ImPlotContext { ImPool Plots; ImPlotState* CurrentPlot; ImPlotItem* CurrentItem; + ImPlotItem* PreviousItem; // Legend ImVector LegendIndices; @@ -565,6 +616,9 @@ struct ImPlotContext { ImPlotTickCollection YTicks[IMPLOT_Y_AXES]; float YAxisReference[IMPLOT_Y_AXES]; + // Annotation and User Labels + ImPlotAnnotationCollection Annotations; + // Transformations and Data Extents ImPlotScale Scales[IMPLOT_Y_AXES]; ImRect PixelRange[IMPLOT_Y_AXES]; diff --git a/implot_items.cpp b/implot_items.cpp index 9b7c931..dfb6aa3 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -125,6 +125,13 @@ void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { gp.NextItemData.ErrorBarWeight = weight; } +ImVec4 GetLastItemColor() { + ImPlotContext& gp = *GImPlot; + if (gp.PreviousItem) + return gp.PreviousItem->Color; + return ImVec4(); +} + void HideNextItem(bool hidden, ImGuiCond cond) { ImPlotContext& gp = *GImPlot; gp.NextItemData.HasHidden = true; @@ -151,29 +158,29 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); bool just_created; ImPlotItem* item = RegisterOrGetItem(label_id, &just_created); - + // set current item + gp.CurrentItem = item; + ImPlotNextItemData& s = gp.NextItemData; + // override item color + if (recolor_from != -1) { + if (!IsColorAuto(s.Colors[recolor_from])) + item->Color = s.Colors[recolor_from]; + else if (!IsColorAuto(gp.Style.Colors[recolor_from])) + item->Color = gp.Style.Colors[recolor_from]; + } // hide/show item if (gp.NextItemData.HasHidden) { if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always) item->Show = !gp.NextItemData.Hidden; } - if (!item->Show) { // reset next item data gp.NextItemData = ImPlotNextItemData(); + gp.PreviousItem = item; + gp.CurrentItem = NULL; return false; } else { - // set current item - gp.CurrentItem = item; - ImPlotNextItemData& s = gp.NextItemData; - // override item color - if (recolor_from != -1) { - if (!IsColorAuto(s.Colors[recolor_from])) - item->Color = s.Colors[recolor_from]; - else if (!IsColorAuto(gp.Style.Colors[recolor_from])) - item->Color = gp.Style.Colors[recolor_from]; - } // stage next item colors s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; @@ -218,7 +225,8 @@ void EndItem() { // reset next item data gp.NextItemData = ImPlotNextItemData(); // set current item - gp.CurrentItem = NULL; + gp.PreviousItem = gp.CurrentItem; + gp.CurrentItem = NULL; } //-----------------------------------------------------------------------------