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

Configurable Legend Locations (#135)

* add support for ISO 8601 timestamps

* clean up work on ISO 8601

* legend location proto

* location docs

* add horizontal legends, and ability to position mouse location

* add ShowAltLegend

* add ShowAltLegend

* default sizing for ShowAltLegend

* finish up legend locations
This commit is contained in:
Evan Pezent 2020-10-18 23:26:34 -05:00 committed by GitHub
parent f9a15a7147
commit 587c8b6221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 529 additions and 301 deletions

View File

@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files.
You can read releases logs https://github.com/epezent/implot/releases for more details. You can read releases logs https://github.com/epezent/implot/releases for more details.
- 2020/10/16 (0.8) - ImPlotStyleVar_InfoPadding was changed to ImPlotStyleVar_MousePosPadding
- 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0. - 2020/09/10 (0.8) - The single array versions of PlotLine, PlotScatter, PlotStems, and PlotShaded were given additional arguments for x-scale and x0.
- 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG) - 2020/09/07 (0.8) - Plotting functions which accept a custom getter function pointer have been post-fixed with a G (e.g. PlotLineG)
- 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset - 2020/09/06 (0.7) - Several flags under ImPlotFlags and ImPlotAxisFlags were inverted (e.g. ImPlotFlags_Legend -> ImPlotFlags_NoLegend) so that the default flagset
@ -95,30 +96,33 @@ ImPlotInputMap::ImPlotInputMap() {
ImPlotStyle::ImPlotStyle() { ImPlotStyle::ImPlotStyle() {
LineWeight = 1; LineWeight = 1;
Marker = ImPlotMarker_None; Marker = ImPlotMarker_None;
MarkerSize = 4; MarkerSize = 4;
MarkerWeight = 1; MarkerWeight = 1;
FillAlpha = 1; FillAlpha = 1;
ErrorBarSize = 5; ErrorBarSize = 5;
ErrorBarWeight = 1.5f; ErrorBarWeight = 1.5f;
DigitalBitHeight = 8; DigitalBitHeight = 8;
DigitalBitGap = 4; DigitalBitGap = 4;
PlotBorderSize = 1; PlotBorderSize = 1;
MinorAlpha = 0.25f; MinorAlpha = 0.25f;
MajorTickLen = ImVec2(10,10); MajorTickLen = ImVec2(10,10);
MinorTickLen = ImVec2(5,5); MinorTickLen = ImVec2(5,5);
MajorTickSize = ImVec2(1,1); MajorTickSize = ImVec2(1,1);
MinorTickSize = ImVec2(1,1); MinorTickSize = ImVec2(1,1);
MajorGridSize = ImVec2(1,1); MajorGridSize = ImVec2(1,1);
MinorGridSize = ImVec2(1,1); MinorGridSize = ImVec2(1,1);
PlotPadding = ImVec2(8,8); PlotPadding = ImVec2(10,10);
LabelPadding = ImVec2(5,5); LabelPadding = ImVec2(5,5);
LegendPadding = ImVec2(10,10); LegendPadding = ImVec2(10,10);
InfoPadding = ImVec2(10,10); LegendInnerPadding = ImVec2(5,5);
AnnotationPadding = ImVec2(2,2); LegendSpacing = ImVec2(0,0);
PlotMinSize = ImVec2(300,225); MousePosPadding = ImVec2(10,10);
AnnotationPadding = ImVec2(2,2);
PlotDefaultSize = ImVec2(400,300);
PlotMinSize = ImVec2(300,225);
ImPlot::StyleColorsAuto(this); ImPlot::StyleColorsAuto(this);
@ -128,6 +132,20 @@ ImPlotStyle::ImPlotStyle() {
UseISO8601 = false; UseISO8601 = false;
} }
ImPlotItem* ImPlotLegend::GetItem(int i) {
return Plot->Items.GetByIndex(Indices[i]);
}
const char* ImPlotLegend::GetLabel(int i) {
ImPlotItem* item = GetItem(i);
IM_ASSERT(item->NameOffset != -1 && item->NameOffset < Labels.Buf.Size);
return Labels.Buf.Data + item->NameOffset;
}
//-----------------------------------------------------------------------------
// Style
//-----------------------------------------------------------------------------
namespace ImPlot { namespace ImPlot {
const char* GetStyleColorName(ImPlotCol col) { const char* GetStyleColorName(ImPlotCol col) {
@ -217,30 +235,34 @@ struct ImPlotStyleVarInfo {
static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = static const ImPlotStyleVarInfo GPlotStyleVarInfo[] =
{ {
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight
{ ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { 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, MarkerSize) }, // ImPlotStyleVar_MarkerSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight { 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, FillAlpha) }, // ImPlotStyleVar_FillAlpha
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { 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, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight { 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, DigitalBitGap) }, // ImPlotStyleVar_DigitalBitGap
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotBorderSize) }, // ImPlotStyleVar_PlotBorderSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorAlpha) }, // ImPlotStyleVar_MinorAlpha { 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, MajorTickLen) }, // ImPlotStyleVar_MajorTickLen
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickLen) }, // ImPlotStyleVar_MinorTickLen { 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, MajorTickSize) }, // ImPlotStyleVar_MajorTickSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorTickSize) }, // ImPlotStyleVar_MinorTickSize { 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, MajorGridSize) }, // ImPlotStyleVar_MajorGridSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MinorGridSize) }, // ImPlotStyleVar_MinorGridSize { 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, PlotPadding) }, // ImPlotStyleVar_PlotPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LabelPadding) }, // ImPlotStyleVar_LabelPaddine { 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, LegendPadding) }, // ImPlotStyleVar_LegendPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, InfoPadding) }, // ImPlotStyleVar_InfoPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendInnerPadding) }, // ImPlotStyleVar_LegendInnerPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, LegendSpacing) }, // ImPlotStyleVar_LegendSpacing
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, MousePosPadding) }, // ImPlotStyleVar_MousePosPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, AnnotationPadding) }, // ImPlotStyleVar_AnnotationPadding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotDefaultSize) }, // ImPlotStyleVar_PlotDefaultSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlotStyle, PlotMinSize) } // ImPlotStyleVar_PlotMinSize
}; };
static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) { static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) {
@ -359,14 +381,10 @@ void Reset(ImPlotContext* ctx) {
ctx->NextItemData = ImPlotNextItemData(); ctx->NextItemData = ImPlotNextItemData();
// reset items count // reset items count
ctx->VisibleItemCount = 0; ctx->VisibleItemCount = 0;
// reset legend items
ctx->LegendIndices.shrink(0);
ctx->LegendLabels.Buf.shrink(0);
// reset ticks/labels // reset ticks/labels
ctx->XTicks.Reset(); ctx->XTicks.Reset();
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i)
ctx->YTicks[i].Reset(); ctx->YTicks[i].Reset();
}
// reset labels // reset labels
ctx->Annotations.Reset(); ctx->Annotations.Reset();
// reset extents/fit // reset extents/fit
@ -408,7 +426,7 @@ void BustPlotCache() {
void FitPoint(const ImPlotPoint& p) { void FitPoint(const ImPlotPoint& p) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
const int y_axis = gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = gp.CurrentPlot->CurrentYAxis;
ImPlotRange& ex_x = gp.ExtentsX; ImPlotRange& ex_x = gp.ExtentsX;
ImPlotRange& ex_y = gp.ExtentsY[y_axis]; ImPlotRange& ex_y = gp.ExtentsY[y_axis];
const bool log_x = ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale); const bool log_x = ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale);
@ -453,10 +471,10 @@ void UpdateTransformCache() {
gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / gp.CurrentPlot->XAxis.Range.Size(); gp.Mx = (gp.PixelRange[0].Max.x - gp.PixelRange[0].Min.x) / gp.CurrentPlot->XAxis.Range.Size();
} }
ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in) { ImPlotPoint PixelsToPlot(float x, float y, ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImPlotPoint plt; ImPlotPoint plt;
plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min; plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min;
plt.y = (y - gp.PixelRange[y_axis].Min.y) / gp.My[y_axis] + gp.CurrentPlot->YAxis[y_axis].Range.Min; plt.y = (y - gp.PixelRange[y_axis].Min.y) / gp.My[y_axis] + gp.CurrentPlot->YAxis[y_axis].Range.Min;
@ -471,15 +489,15 @@ ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in) {
return plt; return plt;
} }
ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis) { ImPlotPoint PixelsToPlot(const ImVec2& pix, ImPlotYAxis y_axis) {
return PixelsToPlot(pix.x, pix.y, y_axis); return PixelsToPlot(pix.x, pix.y, y_axis);
} }
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead. // This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
ImVec2 PlotToPixels(double x, double y, int y_axis_in) { ImVec2 PlotToPixels(double x, double y, ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImVec2 pix; ImVec2 pix;
if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) { if (ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) {
double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX;
@ -495,7 +513,7 @@ ImVec2 PlotToPixels(double x, double y, int y_axis_in) {
} }
// This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead. // This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead.
ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) { ImVec2 PlotToPixels(const ImPlotPoint& plt, ImPlotYAxis y_axis) {
return PlotToPixels(plt.x, plt.y, y_axis); return PlotToPixels(plt.x, plt.y, y_axis);
} }
@ -503,16 +521,98 @@ ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) {
// Legend Utils // Legend Utils
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int GetLegendCount() { ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation loc, const ImVec2& pad) {
ImPlotContext& gp = *GImPlot; ImVec2 pos;
return gp.LegendIndices.size(); if (ImHasFlag(loc, ImPlotLocation_West) && !ImHasFlag(loc, ImPlotLocation_East))
pos.x = outer_rect.Min.x + pad.x;
else if (!ImHasFlag(loc, ImPlotLocation_West) && ImHasFlag(loc, ImPlotLocation_East))
pos.x = outer_rect.Max.x - pad.x - inner_size.x;
else
pos.x = outer_rect.GetCenter().x - inner_size.x * 0.5f;
// legend reference point y
if (ImHasFlag(loc, ImPlotLocation_North) && !ImHasFlag(loc, ImPlotLocation_South))
pos.y = outer_rect.Min.y + pad.y;
else if (!ImHasFlag(loc, ImPlotLocation_North) && ImHasFlag(loc, ImPlotLocation_South))
pos.y = outer_rect.Max.y - pad.y - inner_size.y;
else
pos.y = outer_rect.GetCenter().y - inner_size.y * 0.5f;
pos.x = IM_ROUND(pos.x);
pos.y = IM_ROUND(pos.y);
return pos;
} }
const char* GetLegendLabel(int i) { ImVec2 CalcLegendSize(ImPlotLegend& legend, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orn) {
ImPlotContext& gp = *GImPlot; // vars
ImPlotItem* item = gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]); const int nItems = legend.Count();
IM_ASSERT(item->NameOffset != -1 && item->NameOffset < gp.LegendLabels.Buf.Size); const float txt_ht = ImGui::GetTextLineHeight();
return gp.LegendLabels.Buf.Data + item->NameOffset; const float icon_size = txt_ht;
// get label max width
float max_label_width = 0;
float sum_label_width = 0;
for (int i = 0; i < nItems; ++i) {
const char* label = legend.GetLabel(i);
const float label_width = ImGui::CalcTextSize(label, NULL, true).x;
max_label_width = label_width > max_label_width ? label_width : max_label_width;
sum_label_width += label_width;
}
// calc legend size
const ImVec2 legend_size = orn == ImPlotOrientation_Vertical ?
ImVec2(pad.x * 2 + icon_size + max_label_width, pad.y * 2 + nItems * txt_ht + (nItems - 1) * spacing.y) :
ImVec2(pad.x * 2 + icon_size * nItems + sum_label_width + (nItems - 1) * spacing.x, pad.y * 2 + txt_ht);
return legend_size;
}
void ShowLegendEntries(ImPlotLegend& legend, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orn, ImDrawList& DrawList) {
ImGuiIO& IO = ImGui::GetIO();
// vars
const float txt_ht = ImGui::GetTextLineHeight();
const float icon_size = txt_ht;
const float icon_shrink = 2;
ImVec4 col_txt = GetStyleColorVec4(ImPlotCol_LegendText);
ImU32 col_txt_dis = ImGui::GetColorU32(col_txt * ImVec4(1,1,1,0.25f));
// render each legend item
float sum_label_width = 0;
for (int i = 0; i < legend.Count(); ++i) {
ImPlotItem* item = legend.GetItem(i);
const char* label = legend.GetLabel(i);
const float label_width = ImGui::CalcTextSize(label, NULL, true).x;
const ImVec2 top_left = orn == ImPlotOrientation_Vertical ?
legend_bb.Min + pad + ImVec2(0, i * (txt_ht + spacing.y)) :
legend_bb.Min + pad + ImVec2(i * (icon_size + spacing.x) + sum_label_width, 0);
sum_label_width += label_width;
ImRect icon_bb;
icon_bb.Min = top_left + ImVec2(icon_shrink,icon_shrink);
icon_bb.Max = top_left + ImVec2(icon_size - icon_shrink, icon_size - icon_shrink);
ImRect label_bb;
label_bb.Min = top_left;
label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size);
ImU32 col_hl_txt;
if (interactable && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) {
item->LegendHovered = true;
col_hl_txt = ImGui::GetColorU32(ImLerp(col_txt, item->Color, 0.25f));
}
else {
// item->LegendHovered = false;
col_hl_txt = ImGui::GetColorU32(col_txt);
}
ImU32 iconColor;
ImVec4 item_color = item->Color;
item_color.w = 1;
if (interactable && icon_bb.Contains(IO.MousePos)) {
ImVec4 colAlpha = item_color;
colAlpha.w = 0.5f;
iconColor = item->Show ? ImGui::GetColorU32(colAlpha) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);
if (IO.MouseClicked[0])
item->Show = !item->Show;
}
else {
iconColor = item->Show ? ImGui::GetColorU32(item_color) : col_txt_dis;
}
DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, iconColor, 1);
const char* text_display_end = ImGui::FindRenderedTextEnd(label, NULL);
if (label != text_display_end)
DrawList.AddText(top_left + ImVec2(icon_size, 0), item->Show ? col_hl_txt : col_txt_dis, label, text_display_end);
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -881,7 +981,7 @@ int FormatDate(const ImPlotTime& t, char* buffer, int size, ImPlotDateFmt fmt, b
case ImPlotDateFmt_DayMo: return snprintf(buffer, size, "--%02d-%02d", mon, day); case ImPlotDateFmt_DayMo: return snprintf(buffer, size, "--%02d-%02d", mon, day);
case ImPlotDateFmt_DayMoYr: return snprintf(buffer, size, "%d-%02d-%02d", year, mon, day); case ImPlotDateFmt_DayMoYr: return snprintf(buffer, size, "%d-%02d-%02d", year, mon, day);
case ImPlotDateFmt_MoYr: return snprintf(buffer, size, "%d-%02d", year, mon); case ImPlotDateFmt_MoYr: return snprintf(buffer, size, "%d-%02d", year, mon);
case ImPlotDateFmt_Mo: return snprintf(buffer, size, "--%02d-01", mon); case ImPlotDateFmt_Mo: return snprintf(buffer, size, "--%02d", mon);
case ImPlotDateFmt_Yr: return snprintf(buffer, size, "%d", year); case ImPlotDateFmt_Yr: return snprintf(buffer, size, "%d", year);
default: return 0; default: return 0;
} }
@ -1092,7 +1192,7 @@ int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, do
return snprintf(buff, size, "%.3E", value); return snprintf(buff, size, "%.3E", value);
} }
else if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) { else if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) {
ImPlotTimeUnit unit = (axis.Direction == ImPlotDirection_Horizontal) ImPlotTimeUnit unit = (axis.Direction == ImPlotOrientation_Horizontal)
? GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetWidth() / 100)) ? GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetWidth() / 100))
: GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetHeight() / 100)); : GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetHeight() / 100));
return FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); return FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit));
@ -1174,7 +1274,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// capture scroll with a child region // capture scroll with a child region
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) { if (!ImHasFlag(plot.Flags, ImPlotFlags_NoChild)) {
ImGui::BeginChild(title, ImVec2(size.x == 0 ? IMPLOT_DEFAULT_W : size.x, size.y == 0 ? IMPLOT_DEFAULT_H : size.y), false, ImGuiWindowFlags_NoScrollbar); ImGui::BeginChild(title, ImVec2(size.x == 0 ? gp.Style.PlotDefaultSize.x : size.x, size.y == 0 ? gp.Style.PlotDefaultSize.y : size.y), false, ImGuiWindowFlags_NoScrollbar);
Window = ImGui::GetCurrentWindow(); Window = ImGui::GetCurrentWindow();
Window->ScrollMax.y = 1.0f; Window->ScrollMax.y = 1.0f;
gp.ChildWindowMade = true; gp.ChildWindowMade = true;
@ -1244,7 +1344,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// BB, PADDING, HOVER ----------------------------------------------------------- // BB, PADDING, HOVER -----------------------------------------------------------
// frame // frame
ImVec2 frame_size = ImGui::CalcItemSize(size, IMPLOT_DEFAULT_W, IMPLOT_DEFAULT_H); ImVec2 frame_size = ImGui::CalcItemSize(size, gp.Style.PlotDefaultSize.x, gp.Style.PlotDefaultSize.y);
if (frame_size.x < gp.Style.PlotMinSize.x && size.x < 0.0f) if (frame_size.x < gp.Style.PlotMinSize.x && size.x < 0.0f)
frame_size.x = gp.Style.PlotMinSize.x; frame_size.x = gp.Style.PlotMinSize.x;
if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f)
@ -1261,8 +1361,35 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
ImGui::SetItemAllowOverlap(); ImGui::SetItemAllowOverlap();
ImGui::RenderFrame(gp.BB_Frame.Min, gp.BB_Frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); ImGui::RenderFrame(gp.BB_Frame.Min, gp.BB_Frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding);
// canvas bb // canvas/axes bb
gp.BB_Canvas = ImRect(gp.BB_Frame.Min + gp.Style.PlotPadding, gp.BB_Frame.Max - gp.Style.PlotPadding); gp.BB_Canvas = ImRect(gp.BB_Frame.Min + gp.Style.PlotPadding, gp.BB_Frame.Max - gp.Style.PlotPadding);
gp.BB_Axes = gp.BB_Frame;
// outside legend adjustments
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Legend.Count() > 0 && plot.LegendOutside) {
const ImVec2 legend_size = CalcLegendSize(plot.Legend, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation);
const bool west = ImHasFlag(plot.LegendLocation, ImPlotLocation_West) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_East);
const bool east = ImHasFlag(plot.LegendLocation, ImPlotLocation_East) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_West);
const bool north = ImHasFlag(plot.LegendLocation, ImPlotLocation_North) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_South);
const bool south = ImHasFlag(plot.LegendLocation, ImPlotLocation_South) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_North);
const bool horz = plot.LegendOrientation == ImPlotOrientation_Horizontal;
if ((west && !horz) || (west && horz && !north && !south)) {
gp.BB_Canvas.Min.x += (legend_size.x + gp.Style.LegendPadding.x);
gp.BB_Axes.Min.x += (legend_size.x + gp.Style.PlotPadding.x);
}
if ((east && !horz) || (east && horz && !north && !south)) {
gp.BB_Canvas.Max.x -= (legend_size.x + gp.Style.LegendPadding.x);
gp.BB_Axes.Max.x -= (legend_size.x + gp.Style.PlotPadding.x);
}
if ((north && horz) || (north && !horz && !west && !east)) {
gp.BB_Canvas.Min.y += (legend_size.y + gp.Style.LegendPadding.y);
gp.BB_Axes.Min.y += (legend_size.y + gp.Style.PlotPadding.y);
}
if ((south && horz) || (south && !horz && !west && !east)) {
gp.BB_Canvas.Max.y -= (legend_size.y + gp.Style.LegendPadding.y);
gp.BB_Axes.Max.y -= (legend_size.y + gp.Style.PlotPadding.y);
}
}
gp.RenderX = (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) || gp.RenderX = (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines) ||
!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks) || !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks) ||
@ -1320,8 +1447,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
gp.Hov_Plot = gp.BB_Plot.Contains(IO.MousePos) && gp.Hov_Frame; gp.Hov_Plot = gp.BB_Plot.Contains(IO.MousePos) && gp.Hov_Frame;
// x axis region bb and hover // x axis region bb and hover
const ImRect xAxisRegion_bb(gp.BB_Plot.GetBL(), ImVec2(gp.BB_Plot.Max.x, gp.BB_Frame.Max.y)); gp.BB_X = ImRect(gp.BB_Plot.GetBL(), ImVec2(gp.BB_Plot.Max.x, gp.BB_Axes.Max.y));
plot.XAxis.HoveredExt = xAxisRegion_bb.Contains(IO.MousePos); plot.XAxis.HoveredExt = gp.BB_X.Contains(IO.MousePos);
plot.XAxis.HoveredTot = plot.XAxis.HoveredExt || gp.Hov_Plot; plot.XAxis.HoveredTot = plot.XAxis.HoveredExt || gp.Hov_Plot;
// axis label reference // axis label reference
@ -1330,34 +1457,20 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
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); 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 // y axis regions bb and hover
ImRect yAxisRegion_bb[IMPLOT_Y_AXES]; gp.BB_Y[0] = ImRect(ImVec2(gp.BB_Axes.Min.x, gp.BB_Plot.Min.y), ImVec2(gp.BB_Plot.Min.x, gp.BB_Plot.Max.y));
yAxisRegion_bb[0] = ImRect(ImVec2(gp.BB_Frame.Min.x, gp.BB_Plot.Min.y), ImVec2(gp.BB_Plot.Min.x, gp.BB_Plot.Max.y)); gp.BB_Y[1] = gp.Y[2].Present
yAxisRegion_bb[1] = gp.Y[2].Present
? ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.YAxisReference[2], gp.BB_Plot.Max.y)) ? ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.YAxisReference[2], gp.BB_Plot.Max.y))
: ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.BB_Frame.Max.x, gp.BB_Plot.Max.y)); : ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.BB_Axes.Max.x, gp.BB_Plot.Max.y));
yAxisRegion_bb[2] = ImRect(ImVec2(gp.YAxisReference[2], gp.BB_Plot.Min.y), ImVec2(gp.BB_Frame.Max.x, gp.BB_Plot.Max.y)); gp.BB_Y[2] = ImRect(ImVec2(gp.YAxisReference[2], gp.BB_Plot.Min.y), ImVec2(gp.BB_Axes.Max.x, gp.BB_Plot.Max.y));
for (int i = 0; i < IMPLOT_Y_AXES; ++i) { for (int i = 0; i < IMPLOT_Y_AXES; ++i) {
plot.YAxis[i].HoveredExt = gp.Y[i].Present && yAxisRegion_bb[i].Contains(IO.MousePos); plot.YAxis[i].HoveredExt = gp.Y[i].Present && gp.BB_Y[i].Contains(IO.MousePos);
plot.YAxis[i].HoveredTot = plot.YAxis[i].HoveredExt || gp.Hov_Plot; plot.YAxis[i].HoveredTot = plot.YAxis[i].HoveredExt || gp.Hov_Plot;
} }
#if 0
ImGui::GetForegroundDrawList()->AddRect(gp.BB_Canvas.Min, gp.BB_Canvas.Max, IM_COL32_WHITE);
ImGui::GetForegroundDrawList()->AddRectFilled(xAxisRegion_bb.Min, xAxisRegion_bb.Max, IM_COL32(255,0,0,plot.XAxis.HoveredTot ? 128 : 64));
ImGui::GetForegroundDrawList()->AddRectFilled(yAxisRegion_bb[0].Min, yAxisRegion_bb[0].Max, IM_COL32(255,255,0,plot.YAxis[0].HoveredTot ? 128 : 64));
if (gp.Y[1].Present)
ImGui::GetForegroundDrawList()->AddRectFilled(yAxisRegion_bb[1].Min, yAxisRegion_bb[1].Max, IM_COL32(0,255,0,plot.YAxis[1].HoveredTot ? 128 : 64));
if (gp.Y[2].Present)
ImGui::GetForegroundDrawList()->AddRectFilled(yAxisRegion_bb[2].Min, yAxisRegion_bb[2].Max, IM_COL32(0,0,255,plot.YAxis[2].HoveredTot ? 128 : 64));
#endif
const bool any_hov_y_axis_region = plot.YAxis[0].HoveredTot || plot.YAxis[1].HoveredTot || plot.YAxis[2].HoveredTot; const bool any_hov_y_axis_region = plot.YAxis[0].HoveredTot || plot.YAxis[1].HoveredTot || plot.YAxis[2].HoveredTot;
// legend hovered from last frame
const bool hov_legend = !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
bool hov_query = false; bool hov_query = false;
if (gp.Hov_Frame && gp.Hov_Plot && plot.Queried && !plot.Querying) { if (gp.Hov_Frame && gp.Hov_Plot && plot.Queried && !plot.Querying) {
ImRect bb_query = plot.QueryRect; ImRect bb_query = plot.QueryRect;
@ -1377,7 +1490,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
plot.QueryRect.Min += IO.MouseDelta; plot.QueryRect.Min += IO.MouseDelta;
plot.QueryRect.Max += IO.MouseDelta; plot.QueryRect.Max += IO.MouseDelta;
} }
if (gp.Hov_Frame && gp.Hov_Plot && hov_query && !plot.DraggingQuery && !plot.Selecting && !hov_legend) { if (gp.Hov_Frame && gp.Hov_Plot && hov_query && !plot.DraggingQuery && !plot.Selecting && !plot.LegendHovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging;
if (IO.MouseDown[gp.InputMap.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { if (IO.MouseDown[gp.InputMap.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) {
@ -1445,7 +1558,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
} }
} }
// start drag // start drag
if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !hov_legend && !hov_query && !plot.DraggingQuery) { if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !plot.LegendHovered && !hov_query && !plot.DraggingQuery) {
if (plot.XAxis.HoveredTot) { if (plot.XAxis.HoveredTot) {
plot.XAxis.Dragging = true; plot.XAxis.Dragging = true;
} }
@ -1576,7 +1689,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
// FIT ----------------------------------------------------------- // FIT -----------------------------------------------------------
// fit from double click // fit from double click
if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && gp.Hov_Frame && (plot.XAxis.HoveredTot || any_hov_y_axis_region) && !hov_legend && !hov_query ) { if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && gp.Hov_Frame && (plot.XAxis.HoveredTot || any_hov_y_axis_region) && !plot.LegendHovered && !hov_query ) {
gp.FitThisFrame = true; gp.FitThisFrame = true;
gp.FitX = plot.XAxis.HoveredTot; gp.FitX = plot.XAxis.HoveredTot;
for (int i = 0; i < IMPLOT_Y_AXES; i++) for (int i = 0; i < IMPLOT_Y_AXES; i++)
@ -1708,6 +1821,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons
} }
} }
ImGui::PopClipRect(); ImGui::PopClipRect();
// clear legend
plot.Legend.Reset();
// push plot ID into stack // push plot ID into stack
ImGui::PushID(ID); ImGui::PushID(ID);
return true; return true;
@ -1915,11 +2030,15 @@ void ShowPlotContextMenu(ImPlotPlot& plot) {
ImGui::LabelText("Plots", "%d", gp.Plots.GetSize()); ImGui::LabelText("Plots", "%d", gp.Plots.GetSize());
ImGui::LabelText("Color Mods", "%d", gp.ColorModifiers.size()); ImGui::LabelText("Color Mods", "%d", gp.ColorModifiers.size());
ImGui::LabelText("Style Mods", "%d", gp.StyleModifiers.size()); ImGui::LabelText("Style Mods", "%d", gp.StyleModifiers.size());
ImGui::TextUnformatted(gp.XTicks.TextBuffer.Buf.Data, gp.XTicks.TextBuffer.Buf.Data + gp.XTicks.TextBuffer.size()); bool f = false;
ImGui::TextUnformatted(gp.YTicks[0].TextBuffer.Buf.Data, gp.YTicks[0].TextBuffer.Buf.Data + gp.YTicks[0].TextBuffer.size()); ImGui::Selectable("BB_Frame",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Frame.Min, gp.BB_Frame.Max, IM_COL32(255,255,0,255));
// ImGui::TextUnformatted(gp.YTicks[1].Labels.Buf.Data, gp.YTicks[1].Labels.Buf.Data + gp.YTicks[1].Labels.size()); ImGui::Selectable("BB_Canvas",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Canvas.Min, gp.BB_Canvas.Max, IM_COL32(255,255,0,255));
// ImGui::TextUnformatted(gp.YTicks[2].Labels.Buf.Data, gp.YTicks[2].Labels.Buf.Data + gp.YTicks[2].Labels.size()); ImGui::Selectable("BB_Plot",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Plot.Min, gp.BB_Plot.Max, IM_COL32(255,255,0,255));
ImGui::Selectable("BB_Axes",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Axes.Min, gp.BB_Axes.Max, IM_COL32(255,255,0,255));
ImGui::Selectable("BB_X",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_X.Min, gp.BB_X.Max, IM_COL32(255,255,0,255));
ImGui::Selectable("BB_Y[0]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[0].Min, gp.BB_Y[0].Max, IM_COL32(255,255,0,255));
ImGui::Selectable("BB_Y[1]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[1].Min, gp.BB_Y[1].Max, IM_COL32(255,255,0,255));
ImGui::Selectable("BB_Y[2]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[2].Min, gp.BB_Y[2].Max, IM_COL32(255,255,0,255));
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -2096,75 +2215,9 @@ void EndPlot() {
} }
} }
// render legend
const float txt_ht = ImGui::GetTextLineHeight();
const ImVec2 legend_offset = gp.Style.LegendPadding;
const ImVec2 legend_spacing(5, 5);
const float legend_icon_size = txt_ht;
ImRect legend_content_bb;
int nItems = GetLegendCount();
bool hov_legend = false;
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && nItems > 0) {
// get max width
float max_label_width = 0;
for (int i = 0; i < nItems; ++i) {
const char* label = GetLegendLabel(i);
ImVec2 labelWidth = ImGui::CalcTextSize(label, NULL, true);
max_label_width = labelWidth.x > max_label_width ? labelWidth.x : max_label_width;
}
legend_content_bb = ImRect(gp.BB_Plot.Min + legend_offset, gp.BB_Plot.Min + legend_offset + ImVec2(max_label_width, nItems * txt_ht));
plot.BB_Legend = ImRect(legend_content_bb.Min, legend_content_bb.Max + legend_spacing * 2 + ImVec2(legend_icon_size, 0));
hov_legend = !ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) ? gp.Hov_Frame && plot.BB_Legend.Contains(IO.MousePos) : false;
// render legend box
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
ImVec4 col_txt = GetStyleColorVec4(ImPlotCol_LegendText);
ImU32 col_txt_dis = ImGui::GetColorU32(col_txt * ImVec4(1,1,1,0.25f));
DrawList.AddRectFilled(plot.BB_Legend.Min, plot.BB_Legend.Max, col_bg);
DrawList.AddRect(plot.BB_Legend.Min, plot.BB_Legend.Max, col_bd);
// render each legend item
for (int i = 0; i < nItems; ++i) {
ImPlotItem* item = GetItem(i);
ImRect icon_bb;
icon_bb.Min = legend_content_bb.Min + legend_spacing + ImVec2(0, i * txt_ht) + ImVec2(2, 2);
icon_bb.Max = legend_content_bb.Min + legend_spacing + ImVec2(0, i * txt_ht) + ImVec2(legend_icon_size - 2, legend_icon_size - 2);
ImRect label_bb;
label_bb.Min = legend_content_bb.Min + legend_spacing + ImVec2(0, i * txt_ht) + ImVec2(2, 2);
label_bb.Max = legend_content_bb.Min + legend_spacing + ImVec2(0, i * txt_ht) + ImVec2(legend_content_bb.Max.x, legend_icon_size - 2);
ImU32 col_hl_txt;
if (hov_legend && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) {
item->LegendHovered = true;
col_hl_txt = ImGui::GetColorU32(ImLerp(col_txt, item->Color, 0.25f));
}
else
{
item->LegendHovered = false;
col_hl_txt = ImGui::GetColorU32(col_txt);
}
ImU32 iconColor;
ImVec4 item_color = item->Color;
item_color.w = 1;
if (hov_legend && icon_bb.Contains(IO.MousePos)) {
ImVec4 colAlpha = item_color;
colAlpha.w = 0.5f;
iconColor = item->Show ? ImGui::GetColorU32(colAlpha)
: ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);
if (IO.MouseClicked[0])
item->Show = !item->Show;
} else {
iconColor = item->Show ? ImGui::GetColorU32(item_color) : col_txt_dis;
}
DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, iconColor, 1);
const char* label = GetLegendLabel(i);
const char* text_display_end = ImGui::FindRenderedTextEnd(label, NULL);
if (label != text_display_end)
DrawList.AddText(legend_content_bb.Min + legend_spacing + ImVec2(legend_icon_size, i * txt_ht), item->Show ? col_hl_txt : col_txt_dis, label, text_display_end);
}
}
// render crosshairs // render crosshairs
if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && gp.Hov_Plot && gp.Hov_Frame && if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && gp.Hov_Plot && gp.Hov_Frame &&
!(plot.XAxis.Dragging || any_y_dragging) && !plot.Selecting && !plot.Querying && !hov_legend) { !(plot.XAxis.Dragging || any_y_dragging) && !plot.Selecting && !plot.Querying && !plot.LegendHovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImGui::SetMouseCursor(ImGuiMouseCursor_None);
ImVec2 xy = IO.MousePos; ImVec2 xy = IO.MousePos;
ImVec2 h1(gp.BB_Plot.Min.x, xy.y); ImVec2 h1(gp.BB_Plot.Min.x, xy.y);
@ -2229,13 +2282,43 @@ void EndPlot() {
writer.Write(",(%.*f)", Precision(range_y), gp.MousePos[2].y); writer.Write(",(%.*f)", Precision(range_y), gp.MousePos[2].y);
} }
} }
ImVec2 size = ImGui::CalcTextSize(buffer); const ImVec2 size = ImGui::CalcTextSize(buffer);
ImVec2 pos = gp.BB_Plot.Max - size - gp.Style.InfoPadding; const ImVec2 pos = GetLocationPos(gp.BB_Plot, size, plot.MousePosLocation, gp.Style.MousePosPadding);
DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), buffer); DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), buffer);
} }
PopPlotClipRect(); PopPlotClipRect();
// reset legend hovers
plot.LegendHovered = false;
for (int i = 0; i < plot.Items.GetSize(); ++i)
plot.Items.GetByIndex(i)->LegendHovered = false;
// render legend
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.Legend.Count() > 0) {
const ImVec2 legend_size = CalcLegendSize(plot.Legend, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation);
const ImVec2 legend_pos = GetLocationPos(plot.LegendOutside ? gp.BB_Frame : gp.BB_Plot,
legend_size,
plot.LegendLocation,
plot.LegendOutside ? gp.Style.PlotPadding : gp.Style.LegendPadding);
const ImRect legend_bb(legend_pos, legend_pos + legend_size);
// test hover
plot.LegendHovered = gp.Hov_Frame && legend_bb.Contains(IO.MousePos);
if (plot.LegendOutside)
ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true);
else
PushPlotClipRect();
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg);
DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd);
ShowLegendEntries(plot.Legend, legend_bb, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList);
ImGui::PopClipRect();
}
if (plot.LegendFlipSide) {
plot.LegendOutside = !plot.LegendOutside;
plot.LegendFlipSide = false;
}
// render border // render border
if (gp.Style.PlotBorderSize > 0) if (gp.Style.PlotBorderSize > 0)
DrawList.AddRect(gp.BB_Plot.Min, gp.BB_Plot.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawCornerFlags_All, gp.Style.PlotBorderSize); DrawList.AddRect(gp.BB_Plot.Min, gp.BB_Plot.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawCornerFlags_All, gp.Style.PlotBorderSize);
@ -2271,14 +2354,14 @@ void EndPlot() {
// CONTEXT MENUS ----------------------------------------------------------- // CONTEXT MENUS -----------------------------------------------------------
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend) if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered)
ImGui::OpenPopup("##PlotContext"); ImGui::OpenPopup("##PlotContext");
if (ImGui::BeginPopup("##PlotContext")) { if (ImGui::BeginPopup("##PlotContext")) {
ShowPlotContextMenu(plot); ShowPlotContextMenu(plot);
ImGui::EndPopup(); ImGui::EndPopup();
} }
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.XAxis.HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend) if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.XAxis.HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered)
ImGui::OpenPopup("##XContext"); ImGui::OpenPopup("##XContext");
if (ImGui::BeginPopup("##XContext")) { if (ImGui::BeginPopup("##XContext")) {
ImGui::Text("X-Axis"); ImGui::Separator(); ImGui::Text("X-Axis"); ImGui::Separator();
@ -2288,7 +2371,7 @@ void EndPlot() {
for (int i = 0; i < IMPLOT_Y_AXES; ++i) { for (int i = 0; i < IMPLOT_Y_AXES; ++i) {
ImGui::PushID(i); ImGui::PushID(i);
if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.YAxis[i].HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !hov_legend) if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.YAxis[i].HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered)
ImGui::OpenPopup("##YContext"); ImGui::OpenPopup("##YContext");
if (ImGui::BeginPopup("##YContext")) { if (ImGui::BeginPopup("##YContext")) {
if (i == 0) { if (i == 0) {
@ -2347,7 +2430,7 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) {
gp.NextPlotData.X.Max = x_max; gp.NextPlotData.X.Max = x_max;
} }
void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis) { void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, ImPlotYAxis y_axis) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimitsY() needs to be called before BeginPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimitsY() 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"); IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES");
@ -2393,7 +2476,7 @@ void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char* cons
SetNextPlotTicksX(&buffer[0], n_ticks, labels, show_default); SetNextPlotTicksX(&buffer[0], n_ticks, labels, show_default);
} }
void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[], bool show_default, int y_axis) { void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[], bool show_default, ImPlotYAxis y_axis) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksY() needs to be called before BeginPlot()!"); 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"); IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES");
@ -2401,14 +2484,14 @@ void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labe
AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis]); AddTicksCustom(values, labels, n_ticks, gp.YTicks[y_axis]);
} }
void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[], bool show_default, int y_axis) { void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char* const labels[], bool show_default, ImPlotYAxis y_axis) {
IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1");
static ImVector<double> buffer; static ImVector<double> buffer;
FillRange(buffer, n_ticks, y_min, y_max); FillRange(buffer, n_ticks, y_min, y_max);
SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis); SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis);
} }
void SetPlotYAxis(int y_axis) { void SetPlotYAxis(ImPlotYAxis y_axis) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!");
IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < IMPLOT_Y_AXES, "y_axis needs to be between 0 and IMPLOT_Y_AXES");
@ -2453,28 +2536,28 @@ bool IsPlotXAxisHovered() {
return gp.CurrentPlot->XAxis.HoveredExt; return gp.CurrentPlot->XAxis.HoveredExt;
} }
bool IsPlotYAxisHovered(int y_axis_in) { bool IsPlotYAxisHovered(ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotYAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotYAxisHovered() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
return gp.CurrentPlot->YAxis[y_axis].HoveredExt; return gp.CurrentPlot->YAxis[y_axis].HoveredExt;
} }
ImPlotPoint GetPlotMousePos(int y_axis_in) { ImPlotPoint GetPlotMousePos(ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
return gp.MousePos[y_axis]; return gp.MousePos[y_axis];
} }
ImPlotLimits GetPlotLimits(int y_axis_in) { ImPlotLimits GetPlotLimits(ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!");
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
ImPlotPlot& plot = *gp.CurrentPlot; ImPlotPlot& plot = *gp.CurrentPlot;
ImPlotLimits limits; ImPlotLimits limits;
@ -2489,12 +2572,12 @@ bool IsPlotQueried() {
return gp.CurrentPlot->Queried; return gp.CurrentPlot->Queried;
} }
ImPlotLimits GetPlotQuery(int y_axis_in) { ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis_in) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES");
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!");
ImPlotPlot& plot = *gp.CurrentPlot; ImPlotPlot& plot = *gp.CurrentPlot;
const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis;
UpdateTransformCache(); UpdateTransformCache();
ImPlotPoint p1 = PixelsToPlot(plot.QueryRect.Min + gp.BB_Plot.Min, y_axis); ImPlotPoint p1 = PixelsToPlot(plot.QueryRect.Min + gp.BB_Plot.Min, y_axis);
@ -2694,6 +2777,21 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe
return dragging; return dragging;
} }
void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) {
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetLegendLocation() needs to be called between BeginPlot() and EndPlot()!");
gp.CurrentPlot->LegendLocation = location;
gp.CurrentPlot->LegendOrientation = orientation;
if (gp.CurrentPlot->LegendOutside != outside)
gp.CurrentPlot->LegendFlipSide = true;
}
void SetMousePosLocation(ImPlotLocation location) {
ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetMousePosLocation() needs to be called between BeginPlot() and EndPlot()!");
gp.CurrentPlot->MousePosLocation = location;
}
bool IsLegendEntryHovered(const char* label_id) { bool IsLegendEntryHovered(const char* label_id) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotItemHighlight() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotItemHighlight() needs to be called between BeginPlot() and EndPlot()!");
@ -2790,6 +2888,42 @@ void EndLegendPopup() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
void ShowAltLegend(const char* title_id, ImPlotOrientation orientation, const ImVec2 size, bool interactable) {
ImPlotContext& gp = *GImPlot;
ImGuiContext &G = *GImGui;
ImGuiWindow * Window = G.CurrentWindow;
if (Window->SkipItems)
return;
ImDrawList &DrawList = *Window->DrawList;
ImPlotPlot* plot = GetPlot(title_id);
ImVec2 legend_size;
ImVec2 default_size = gp.Style.LegendPadding * 2;
if (plot != NULL) {
legend_size = CalcLegendSize(plot->Legend, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, orientation);
default_size = legend_size + gp.Style.LegendPadding * 2;
}
ImVec2 frame_size = ImGui::CalcItemSize(size, default_size.x, default_size.y);
ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size);
ImGui::ItemSize(bb_frame);
if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame))
return;
ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);
DrawList.PushClipRect(bb_frame.Min, bb_frame.Max, true);
if (plot != NULL) {
const ImVec2 legend_pos = GetLocationPos(bb_frame, legend_size, 0, gp.Style.LegendPadding);
const ImRect legend_bb(legend_pos, legend_pos + legend_size);
interactable = interactable && bb_frame.Contains(ImGui::GetIO().MousePos);
// render legend box
ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg);
ImU32 col_bd = GetStyleColorU32(ImPlotCol_LegendBorder);
DrawList.AddRectFilled(legend_bb.Min, legend_bb.Max, col_bg);
DrawList.AddRect(legend_bb.Min, legend_bb.Max, col_bd);
// render entries
ShowLegendEntries(plot->Legend, legend_bb, interactable, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, orientation, DrawList);
}
DrawList.PopClipRect();
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// STYLING // STYLING
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -3154,7 +3288,7 @@ void ShowColormapScale(double scale_min, double scale_max, float height) {
ImGui::ItemSize(bb_frame); ImGui::ItemSize(bb_frame);
if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame))
return; return;
ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg)); ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding);
ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding, bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x, height - gp.Style.PlotPadding.y)); ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding, bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x, height - gp.Style.PlotPadding.y));
int num_cols = GetColormapSize(); int num_cols = GetColormapSize();
@ -3280,12 +3414,15 @@ void ShowStyleEditor(ImPlotStyle* ref) {
ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f"); ImGui::SliderFloat2("MinorTickSize", (float*)&style.MinorTickSize, 0.0f, 2.0f, "%.1f");
ImGui::SliderFloat2("MajorGridSize", (float*)&style.MajorGridSize, 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("MinorGridSize", (float*)&style.MinorGridSize, 0.0f, 2.0f, "%.1f");
ImGui::SliderFloat2("PlotDefaultSize", (float*)&style.PlotDefaultSize, 0.0f, 1000, "%.0f");
ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f"); ImGui::SliderFloat2("PlotMinSize", (float*)&style.PlotMinSize, 0.0f, 300, "%.0f");
ImGui::Text("Plot Padding"); ImGui::Text("Plot Padding");
ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("PlotPadding", (float*)&style.PlotPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("LabelPadding", (float*)&style.LabelPadding, 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("LegendPadding", (float*)&style.LegendPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("InfoPadding", (float*)&style.InfoPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("LegendInnerPadding", (float*)&style.LegendInnerPadding, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat2("LegendSpacing", (float*)&style.LegendSpacing, 0.0f, 5.0f, "%.0f");
ImGui::SliderFloat2("MousePosPadding", (float*)&style.MousePosPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f"); ImGui::SliderFloat2("AnnotationPadding", (float*)&style.AnnotationPadding, 0.0f, 5.0f, "%.0f");
ImGui::EndTabItem(); ImGui::EndTabItem();
} }

138
implot.h
View File

@ -58,6 +58,9 @@ typedef int ImPlotCol; // -> enum ImPlotCol_
typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_ typedef int ImPlotStyleVar; // -> enum ImPlotStyleVar_
typedef int ImPlotMarker; // -> enum ImPlotMarker_ typedef int ImPlotMarker; // -> enum ImPlotMarker_
typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotColormap; // -> enum ImPlotColormap_
typedef int ImPlotLocation; // -> enum ImPlotLocation_
typedef int ImPlotOrientation; // -> enum ImPlotOrientation_
typedef int ImPlotYAxis; // -> enum ImPlotYAxis_;
// Options for plots. // Options for plots.
enum ImPlotFlags_ { enum ImPlotFlags_ {
@ -65,7 +68,7 @@ enum ImPlotFlags_ {
ImPlotFlags_NoLegend = 1 << 0, // the top-left legend will not be displayed ImPlotFlags_NoLegend = 1 << 0, // the top-left legend will not be displayed
ImPlotFlags_NoMenus = 1 << 1, // the user will not be able to open context menus with double-right click ImPlotFlags_NoMenus = 1 << 1, // the user will not be able to open context menus with double-right click
ImPlotFlags_NoBoxSelect = 1 << 2, // the user will not be able to box-select with right-mouse ImPlotFlags_NoBoxSelect = 1 << 2, // the user will not be able to box-select with right-mouse
ImPlotFlags_NoMousePos = 1 << 3, // the mouse position, in plot coordinates, will not be displayed in the bottom-right ImPlotFlags_NoMousePos = 1 << 3, // the mouse position, in plot coordinates, will not be displayed inside of the plot
ImPlotFlags_NoHighlight = 1 << 4, // plot items will not be highlighted when their legend entry is hovered ImPlotFlags_NoHighlight = 1 << 4, // plot items will not be highlighted when their legend entry is hovered
ImPlotFlags_NoChild = 1 << 5, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) ImPlotFlags_NoChild = 1 << 5, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications)
ImPlotFlags_YAxis2 = 1 << 6, // enable a 2nd y-axis on the right side ImPlotFlags_YAxis2 = 1 << 6, // enable a 2nd y-axis on the right side
@ -125,30 +128,33 @@ enum ImPlotCol_ {
// Plot styling variables. // Plot styling variables.
enum ImPlotStyleVar_ { enum ImPlotStyleVar_ {
// item styling variables // item styling variables
ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels ImPlotStyleVar_LineWeight, // float, plot item line weight in pixels
ImPlotStyleVar_Marker, // int, marker specification ImPlotStyleVar_Marker, // int, marker specification
ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius")
ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels ImPlotStyleVar_MarkerWeight, // float, plot outline weight of markers in pixels
ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills
ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels
ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels
ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels
ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels ImPlotStyleVar_DigitalBitGap, // float, digital channels bit padding gap in pixels
// plot styling variables // plot styling variables
ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area ImPlotStyleVar_PlotBorderSize, // float, thickness of border around plot area
ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines ImPlotStyleVar_MinorAlpha, // float, alpha multiplier applied to minor axis grid lines
ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes ImPlotStyleVar_MajorTickLen, // ImVec2, major tick lengths for X and Y axes
ImPlotStyleVar_MinorTickLen, // ImVec2, minor 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_MajorTickSize, // ImVec2, line thickness of major ticks
ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks ImPlotStyleVar_MinorTickSize, // ImVec2, line thickness of minor ticks
ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines ImPlotStyleVar_MajorGridSize, // ImVec2, line thickness of major grid lines
ImPlotStyleVar_MinorGridSize, // ImVec2, line thickness of minor 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_PlotPadding, // ImVec2, padding between widget frame and plot area, labels, or outside legends (i.e. main padding)
ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge ImPlotStyleVar_LabelPadding, // ImVec2, padding between axes labels, tick labels, and plot edge
ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from top-left of plot ImPlotStyleVar_LegendPadding, // ImVec2, legend padding from plot edges
ImPlotStyleVar_InfoPadding, // ImVec2, padding between plot edge and interior info text ImPlotStyleVar_LegendInnerPadding, // ImVec2, legend inner padding from legend edges
ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels ImPlotStyleVar_LegendSpacing, // ImVec2, spacing between legend entries
ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk ImPlotStyleVar_MousePosPadding, // ImVec2, padding between plot edge and interior info text
ImPlotStyleVar_AnnotationPadding, // ImVec2, text padding around annotation labels
ImPlotStyleVar_PlotDefaultSize, // ImVec2, default size used when ImVec2(0,0) is passed to BeginPlot
ImPlotStyleVar_PlotMinSize, // ImVec2, minimum size plot frame can be when shrunk
ImPlotStyleVar_COUNT ImPlotStyleVar_COUNT
}; };
@ -184,6 +190,32 @@ enum ImPlotColormap_ {
ImPlotColormap_COUNT ImPlotColormap_COUNT
}; };
// Used to position items on a plot (e.g. legends, labels, etc.)
enum ImPlotLocation_ {
ImPlotLocation_Center = 0, // center-center
ImPlotLocation_North = 1 << 0, // top-center
ImPlotLocation_South = 1 << 1, // bottom-center
ImPlotLocation_West = 1 << 2, // center-left
ImPlotLocation_East = 1 << 3, // center-right
ImPlotLocation_NorthWest = ImPlotLocation_North | ImPlotLocation_West, // top-left
ImPlotLocation_NorthEast = ImPlotLocation_North | ImPlotLocation_East, // top-right
ImPlotLocation_SouthWest = ImPlotLocation_South | ImPlotLocation_West, // bottom-left
ImPlotLocation_SouthEast = ImPlotLocation_South | ImPlotLocation_East // bottom-right
};
// Used to orient items on a plot (e.g. legends, labels, etc.)
enum ImPlotOrientation_ {
ImPlotOrientation_Horizontal, // left/right
ImPlotOrientation_Vertical // up/down
};
// Enums for different y-axes.
enum ImPlotYAxis_ {
ImPlotYAxis_1 = 0, // left (default)
ImPlotYAxis_2 = 1, // first on right side
ImPlotYAxis_3 = 2 // second on right side
};
// Double precision version of ImVec2 used by ImPlot. Extensible by end users. // Double precision version of ImVec2 used by ImPlot. Extensible by end users.
struct ImPlotPoint { struct ImPlotPoint {
double x, y; double x, y;
@ -235,11 +267,14 @@ struct ImPlotStyle {
ImVec2 MinorTickSize; // = 1,1 line thickness of minor ticks ImVec2 MinorTickSize; // = 1,1 line thickness of minor ticks
ImVec2 MajorGridSize; // = 1,1 line thickness of major grid lines ImVec2 MajorGridSize; // = 1,1 line thickness of major grid lines
ImVec2 MinorGridSize; // = 1,1 line thickness of minor grid lines ImVec2 MinorGridSize; // = 1,1 line thickness of minor grid lines
ImVec2 PlotPadding; // = 8,8 padding between widget frame and plot area and/or labels ImVec2 PlotPadding; // = 10,10 padding between widget frame and plot area, labels, or outside legends (i.e. main padding)
ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge 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 LegendPadding; // = 10,10 legend padding from plot edges
ImVec2 InfoPadding; // = 10,10 padding between plot edge and interior info text ImVec2 LegendInnerPadding; // = 5,5 legend inner padding from legend edges
ImVec2 LegendSpacing; // = 0,0 spacing between legend entries
ImVec2 MousePosPadding; // = 10,10 padding between plot edge and interior mouse location text
ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels
ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot
ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk
// colors // colors
ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors
@ -247,7 +282,7 @@ struct ImPlotStyle {
bool AntiAliasedLines; // = false, enable global anti-aliasing on plot lines (overrides ImPlotFlags_AntiAliased) bool AntiAliasedLines; // = false, enable global anti-aliasing on plot lines (overrides ImPlotFlags_AntiAliased)
bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled
bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.) bool UseISO8601; // = false, dates will be formatted according to ISO 8601 where applicable (e.g. YYYY-MM-DD, YYYY-MM, --MM-DD, etc.)
bool Use24HourClock; // = false, times will be formatted using 24 hour clock bool Use24HourClock; // = false, times will be formatted using a 24 hour clock
IMPLOT_API ImPlotStyle(); IMPLOT_API ImPlotStyle();
}; };
@ -403,6 +438,9 @@ IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, con
// Plots a centered text label at point x,y with optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). // 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& pix_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));
// Plots an dummy item (i.e. adds a legend entry colored by ImPlotCol_Line)
IMPLOT_API void PlotDummy(const char* label_id);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Plot Utils // Plot Utils
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -414,7 +452,7 @@ 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. // 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); 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. // 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, int y_axis = 0); IMPLOT_API void SetNextPlotLimitsY(double ymin, double ymax, ImGuiCond cond = ImGuiCond_Once, ImPlotYAxis y_axis = 0);
// Links the next plot limits to external values. Set to NULL for no linkage. The pointer data must remain valid until the matching call EndPlot. // Links the next plot limits to external values. Set to NULL for no linkage. The pointer data must remain valid until the matching call 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); 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). // Fits the next plot axes to all plotted data if they are unlocked (equivalent to double-clicks).
@ -425,22 +463,22 @@ IMPLOT_API void SetNextPlotTicksX(const double* values, int n_ticks, const char*
IMPLOT_API void SetNextPlotTicksX(double x_min, double x_max, 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 Y axis ticks and optionally the labels for the next plot. // Set the Y axis ticks and optionally the labels for the next plot.
IMPLOT_API void SetNextPlotTicksY(const double* values, int n_ticks, const char* const labels[] = NULL, bool show_default = false, int y_axis = 0); 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, int 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);
// The following functions MUST be called between Begin/EndPlot! // The following functions MUST be called between Begin/EndPlot!
// Select which Y axis will be used for subsequent plot elements. The default is '0', or the first (left) Y axis. Enable 2nd and 3rd axes with ImPlotFlags_YAxisX. // Select which Y axis will be used for subsequent plot elements. The default is ImPlotYAxis_1, or the first (left) Y axis. Enable 2nd and 3rd axes with ImPlotFlags_YAxisX.
IMPLOT_API void SetPlotYAxis(int y_axis); IMPLOT_API void SetPlotYAxis(ImPlotYAxis y_axis);
// Hides or shows the next plot item (i.e. as if it were toggled from the legend). Use ImGuiCond_Always if you need to change this every frame. // Hides or shows the next plot item (i.e. as if it were toggled from the legend). Use ImGuiCond_Always if you need to forcefully set this every frame.
IMPLOT_API void HideNextItem(bool hidden = true, ImGuiCond cond = ImGuiCond_Once); IMPLOT_API void HideNextItem(bool hidden = true, ImGuiCond cond = ImGuiCond_Once);
// Convert pixels to a position in the current plot's coordinate system. A negative y_axis uses the current value of SetPlotYAxis (0 initially). // Convert pixels to a position in the current plot's coordinate system. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially).
IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotPoint PixelsToPlot(const ImVec2& pix, ImPlotYAxis y_axis = IMPLOT_AUTO);
IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, int y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotPoint PixelsToPlot(float x, float y, ImPlotYAxis y_axis = IMPLOT_AUTO);
// Convert a position in the current plot's coordinate system to pixels. A negative y_axis uses the current value of SetPlotYAxis (0 initially). // Convert a position in the current plot's coordinate system to pixels. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially).
IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis = IMPLOT_AUTO); IMPLOT_API ImVec2 PlotToPixels(const ImPlotPoint& plt, ImPlotYAxis y_axis = IMPLOT_AUTO);
IMPLOT_API ImVec2 PlotToPixels(double x, double y, int y_axis = IMPLOT_AUTO); IMPLOT_API ImVec2 PlotToPixels(double x, double y, ImPlotYAxis y_axis = IMPLOT_AUTO);
// Get the current Plot position (top-left) in pixels. // Get the current Plot position (top-left) in pixels.
IMPLOT_API ImVec2 GetPlotPos(); IMPLOT_API ImVec2 GetPlotPos();
// Get the curent Plot size in pixels. // Get the curent Plot size in pixels.
@ -450,21 +488,23 @@ IMPLOT_API bool IsPlotHovered();
// Returns true if the XAxis plot area in the current plot is hovered. // Returns true if the XAxis plot area in the current plot is hovered.
IMPLOT_API bool IsPlotXAxisHovered(); IMPLOT_API bool IsPlotXAxisHovered();
// Returns true if the YAxis[n] plot area in the current plot is hovered. // Returns true if the YAxis[n] plot area in the current plot is hovered.
IMPLOT_API bool IsPlotYAxisHovered(int y_axis = 0); IMPLOT_API bool IsPlotYAxisHovered(ImPlotYAxis y_axis = 0);
// Returns the mouse position in x,y coordinates of the current plot. A negative y_axis uses the current value of SetPlotYAxis (0 initially). // Returns the mouse position in x,y coordinates of the current plot. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially).
IMPLOT_API ImPlotPoint GetPlotMousePos(int y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotPoint GetPlotMousePos(ImPlotYAxis y_axis = IMPLOT_AUTO);
// Returns the current plot axis range. A negative y_axis uses the current value of SetPlotYAxis (0 initially). // Returns the current plot axis range. A negative y_axis uses the current value of SetPlotYAxis (ImPlotYAxis_1 initially).
IMPLOT_API ImPlotLimits GetPlotLimits(int y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotLimits GetPlotLimits(ImPlotYAxis y_axis = IMPLOT_AUTO);
// Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query. // Returns true if the current plot is being queried. Query must be enabled with ImPlotFlags_Query.
IMPLOT_API bool IsPlotQueried(); IMPLOT_API bool IsPlotQueried();
// Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query. // Returns the current plot query bounds. Query must be enabled with ImPlotFlags_Query.
IMPLOT_API ImPlotLimits GetPlotQuery(int y_axis = IMPLOT_AUTO); IMPLOT_API ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis = IMPLOT_AUTO);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Plot Tools // Plot Tools
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// The following functions MUST be called between Begin/EndPlot!
// Shows an annotation callout at a chosen point. // Shows an annotation callout at a chosen point.
IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const char* fmt, ...) IM_FMTARGS(4); IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const char* fmt, ...) IM_FMTARGS(4);
IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(5); IMPLOT_API void Annotate(double x, double y, const ImVec2& pix_offset, const ImVec4& color, const char* fmt, ...) IM_FMTARGS(5);
@ -488,6 +528,12 @@ IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label
// Legend Utils and Tools // Legend Utils and Tools
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// The following functions MUST be called between Begin/EndPlot!
// Set the location of the current plot's legend.
IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation = ImPlotOrientation_Vertical, bool outside = false);
// Set the locaton of the current plot's mouse position text (default = South|East).
IMPLOT_API void SetMousePosLocation(ImPlotLocation location);
// Returns true if a plot item legend entry is hovered. // Returns true if a plot item legend entry is hovered.
IMPLOT_API bool IsLegendEntryHovered(const char* label_id); IMPLOT_API bool IsLegendEntryHovered(const char* label_id);
// Begin a drag and drop source from a legend entry. The only supported flag is SourceNoPreviewTooltip // Begin a drag and drop source from a legend entry. The only supported flag is SourceNoPreviewTooltip

View File

@ -350,11 +350,13 @@ void ShowDemoWindow(bool* p_open) {
ImVec2(-1,0), 0, 0, horz ? ImPlotAxisFlags_Invert : 0)) ImVec2(-1,0), 0, 0, horz ? ImPlotAxisFlags_Invert : 0))
{ {
if (horz) { if (horz) {
ImPlot::SetLegendLocation(ImPlotLocation_West, ImPlotOrientation_Vertical);
ImPlot::PlotBarsH("Midterm Exam", midtm, 10, 0.2, -0.2); ImPlot::PlotBarsH("Midterm Exam", midtm, 10, 0.2, -0.2);
ImPlot::PlotBarsH("Final Exam", final, 10, 0.2, 0); ImPlot::PlotBarsH("Final Exam", final, 10, 0.2, 0);
ImPlot::PlotBarsH("Course Grade", grade, 10, 0.2, 0.2); ImPlot::PlotBarsH("Course Grade", grade, 10, 0.2, 0.2);
} }
else { else {
ImPlot::SetLegendLocation(ImPlotLocation_South, ImPlotOrientation_Horizontal);
ImPlot::PlotBars("Midterm Exam", midtm, 10, 0.2, -0.2); ImPlot::PlotBars("Midterm Exam", midtm, 10, 0.2, -0.2);
ImPlot::PlotBars("Final Exam", final, 10, 0.2, 0); ImPlot::PlotBars("Final Exam", final, 10, 0.2, 0);
ImPlot::PlotBars("Course Grade", grade, 10, 0.2, 0.2); ImPlot::PlotBars("Course Grade", grade, 10, 0.2, 0.2);
@ -686,11 +688,11 @@ void ShowDemoWindow(bool* p_open) {
ImPlot::PlotLine("f(x) = x", xs, xs, 1001); ImPlot::PlotLine("f(x) = x", xs, xs, 1001);
ImPlot::PlotLine("f(x) = sin(x)*3+1", xs, ys1, 1001); ImPlot::PlotLine("f(x) = sin(x)*3+1", xs, ys1, 1001);
if (y2_axis) { if (y2_axis) {
ImPlot::SetPlotYAxis(1); ImPlot::SetPlotYAxis(ImPlotYAxis_2);
ImPlot::PlotLine("f(x) = cos(x)*.2+.5 (Y2)", xs, ys2, 1001); ImPlot::PlotLine("f(x) = cos(x)*.2+.5 (Y2)", xs, ys2, 1001);
} }
if (y3_axis) { if (y3_axis) {
ImPlot::SetPlotYAxis(2); ImPlot::SetPlotYAxis(ImPlotYAxis_3);
ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 (Y3)", xs2, ys3, 1001); ImPlot::PlotLine("f(x) = sin(x+.5)*100+200 (Y3)", xs2, ys3, 1001);
} }
ImPlot::EndPlot(); ImPlot::EndPlot();
@ -797,6 +799,41 @@ void ShowDemoWindow(bool* p_open) {
} }
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Legend")) {
static bool n = false; static bool s = false; static bool w = false; static bool e = true;
static bool h = false; static bool o = true;
ImGui::Checkbox("North", &n); ImGui::SameLine();
ImGui::Checkbox("South", &s); ImGui::SameLine();
ImGui::Checkbox("West", &w); ImGui::SameLine();
ImGui::Checkbox("East", &e); ImGui::SameLine();
ImGui::Checkbox("Horizontal", &h); ImGui::SameLine();
ImGui::Checkbox("Outside", &o);
ImPlotLocation loc = 0;
loc = n ? loc | ImPlotLocation_North : loc;
loc = s ? loc | ImPlotLocation_South : loc;
loc = w ? loc | ImPlotLocation_West : loc;
loc = e ? loc | ImPlotLocation_East : loc;
ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f");
ImGui::SliderFloat2("LegendInnerPadding", (float*)&GetStyle().LegendInnerPadding, 0.0f, 10.0f, "%.0f");
ImGui::SliderFloat2("LegendSpacing", (float*)&GetStyle().LegendSpacing, 0.0f, 5.0f, "%.0f");
if (ImPlot::BeginPlot("##Legend","x","y",ImVec2(-1,0))) {
ImPlot::SetLegendLocation(loc, h ? ImPlotOrientation_Horizontal : ImPlotOrientation_Vertical, o);
static MyImPlot::WaveData data1(0.001, 0.2, 2, 0.75);
static MyImPlot::WaveData data2(0.001, 0.2, 4, 0.25);
static MyImPlot::WaveData data3(0.001, 0.2, 6, 0.5);
ImPlot::PlotLineG("Item 1", MyImPlot::SineWave, &data1, 1000); // "Item 1" added to legend
ImPlot::PlotLineG("Item 2##IDText", MyImPlot::SawWave, &data2, 1000); // "Item 2" added to legend, text after ## used for ID only
ImPlot::PlotLineG("##NotDisplayed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend
ImPlot::PlotLineG("Item 3", MyImPlot::SineWave, &data1, 1000); // "Item 3" added to legend
ImPlot::PlotLineG("Item 3", MyImPlot::SawWave, &data2, 1000); // combined with previous "Item 3"
ImPlot::EndPlot();
}
}
//-------------------------------------------------------------------------
if (ImGui::CollapsingHeader("Drag Lines and Points")) { if (ImGui::CollapsingHeader("Drag Lines and Points")) {
ImGui::BulletText("Click and drag the horizontal and vertical lines."); ImGui::BulletText("Click and drag the horizontal and vertical lines.");
static double x1 = 0.2; static double x1 = 0.2;
@ -817,7 +854,7 @@ void ShowDemoWindow(bool* p_open) {
ys[i] = (y1+y2)/2+abs(y2-y1)/2*sin(f*i/10); ys[i] = (y1+y2)/2+abs(y2-y1)/2*sin(f*i/10);
} }
ImPlot::PlotLine("Interactive Data", xs, ys, 1000); ImPlot::PlotLine("Interactive Data", xs, ys, 1000);
ImPlot::SetPlotYAxis(1); ImPlot::SetPlotYAxis(ImPlotYAxis_2);
ImPlot::DragLineY("f",&f,show_labels,ImVec4(1,0.5f,1,1)); ImPlot::DragLineY("f",&f,show_labels,ImVec4(1,0.5f,1,1));
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
@ -1402,7 +1439,7 @@ void StyleSeaborn() {
style.PlotPadding = ImVec2(12,12); style.PlotPadding = ImVec2(12,12);
style.LabelPadding = ImVec2(5,5); style.LabelPadding = ImVec2(5,5);
style.LegendPadding = ImVec2(5,5); style.LegendPadding = ImVec2(5,5);
style.InfoPadding = ImVec2(5,5); style.MousePosPadding = ImVec2(5,5);
style.PlotMinSize = ImVec2(300,225); style.PlotMinSize = ImVec2(300,225);
} }
@ -1639,4 +1676,4 @@ void ShowBenchmarkTool() {
} }
} }
} }

View File

@ -51,6 +51,7 @@ struct ImPlotAxis;
struct ImPlotAxisState; struct ImPlotAxisState;
struct ImPlotAxisColor; struct ImPlotAxisColor;
struct ImPlotItem; struct ImPlotItem;
struct ImPlotLegend;
struct ImPlotPlot; struct ImPlotPlot;
struct ImPlotNextPlotData; struct ImPlotNextPlotData;
@ -67,20 +68,16 @@ extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer
// Constants can be changed unless stated otherwise. We may move some of these // Constants can be changed unless stated otherwise. We may move some of these
// to ImPlotStyleVar_ over time. // to ImPlotStyleVar_ over time.
// Default plot frame width when requested width is auto (i.e. 0). This is not the plot area width!
#define IMPLOT_DEFAULT_W 400
// Default plot frame height when requested height is auto (i.e. 0). This is not the plot area height!
#define IMPLOT_DEFAULT_H 300
// The maximum number of supported y-axes (DO NOT CHANGE THIS) // The maximum number of supported y-axes (DO NOT CHANGE THIS)
#define IMPLOT_Y_AXES 3 #define IMPLOT_Y_AXES 3
// The number of times to subdivided grid divisions (best if a multiple of 1, 2, and 5) // The number of times to subdivided grid divisions (best if a multiple of 1, 2, and 5)
#define IMPLOT_SUB_DIV 10 #define IMPLOT_SUB_DIV 10
// Zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click) // Zoom rate for scroll (e.g. 0.1f = 10% plot range every scroll click)
#define IMPLOT_ZOOM_RATE 0.1f #define IMPLOT_ZOOM_RATE 0.1f
// Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS) // Mimimum allowable timestamp value 01/01/1970 @ 12:00am (UTC) (DO NOT DECREASE THIS)
#define IMPLOT_MIN_TIME 0 #define IMPLOT_MIN_TIME 0
// Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS)
#define IMPLOT_MAX_TIME 32503680000 #define IMPLOT_MAX_TIME 32503680000
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Generic Helpers // [SECTION] Generic Helpers
@ -158,17 +155,10 @@ struct ImPlotPointArray {
// [SECTION] ImPlot Enums // [SECTION] ImPlot Enums
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
typedef int ImPlotDirection; // -> enum ImPlotDirection_ typedef int ImPlotScale; // -> enum ImPlotScale_
typedef int ImPlotScale; // -> enum ImPlotScale_ typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_
typedef int ImPlotTimeUnit; // -> enum ImPlotTimeUnit_ typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_
typedef int ImPlotDateFmt; // -> enum ImPlotDateFmt_ typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_
typedef int ImPlotTimeFmt; // -> enum ImPlotTimeFmt_
// Axis direction
enum ImPlotDirection_ {
ImPlotDirection_Horizontal, // left/right
ImPlotDirection_Vertical // up/down
};
// XY axes scaling combinations // XY axes scaling combinations
enum ImPlotScale_ { enum ImPlotScale_ {
@ -195,7 +185,7 @@ enum ImPlotDateFmt_ { // default [ ISO 8601 ]
ImPlotDateFmt_DayMo, // 10/3 [ --10-03 ] ImPlotDateFmt_DayMo, // 10/3 [ --10-03 ]
ImPlotDateFmt_DayMoYr, // 10/3/91 [ 1991-10-03 ] ImPlotDateFmt_DayMoYr, // 10/3/91 [ 1991-10-03 ]
ImPlotDateFmt_MoYr, // Oct 1991 [ 1991-10 ] ImPlotDateFmt_MoYr, // Oct 1991 [ 1991-10 ]
ImPlotDateFmt_Mo, // Oct [ --10-01 ] ImPlotDateFmt_Mo, // Oct [ --10 ]
ImPlotDateFmt_Yr // 1991 [ 1991 ] ImPlotDateFmt_Yr // 1991 [ 1991 ]
}; };
@ -392,7 +382,7 @@ struct ImPlotAxis
ImPlotAxisFlags Flags; ImPlotAxisFlags Flags;
ImPlotAxisFlags PreviousFlags; ImPlotAxisFlags PreviousFlags;
ImPlotRange Range; ImPlotRange Range;
ImPlotDirection Direction; ImPlotOrientation Direction;
bool Dragging; bool Dragging;
bool HoveredExt; bool HoveredExt;
bool HoveredTot; bool HoveredTot;
@ -524,6 +514,20 @@ struct ImPlotItem
~ImPlotItem() { ID = 0; } ~ImPlotItem() { ID = 0; }
}; };
// Holds Legend state labels and item references
struct ImPlotLegend
{
ImPlotPlot* Plot;
ImVector<int> Indices;
ImGuiTextBuffer Labels;
ImPlotLegend(ImPlotPlot* plot) { Plot = plot; }
void Reset() { Indices.shrink(0); Labels.Buf.shrink(0); }
int Count() const { return Indices.size(); }
ImPlotItem* GetItem(int i);
const char* GetLabel(int i);
};
// Holds Plot state information that must persist after EndPlot // Holds Plot state information that must persist after EndPlot
struct ImPlotPlot struct ImPlotPlot
{ {
@ -531,27 +535,37 @@ struct ImPlotPlot
ImPlotFlags PreviousFlags; ImPlotFlags PreviousFlags;
ImPlotAxis XAxis; ImPlotAxis XAxis;
ImPlotAxis YAxis[IMPLOT_Y_AXES]; ImPlotAxis YAxis[IMPLOT_Y_AXES];
ImPlotLegend Legend;
ImPool<ImPlotItem> Items; ImPool<ImPlotItem> Items;
ImVec2 SelectStart; ImVec2 SelectStart;
ImVec2 QueryStart; ImVec2 QueryStart;
ImRect QueryRect; ImRect QueryRect;
ImRect BB_Legend;
bool Selecting; bool Selecting;
bool Querying; bool Querying;
bool Queried; bool Queried;
bool DraggingQuery; bool DraggingQuery;
bool LegendHovered;
bool LegendOutside;
bool LegendFlipSide;
int ColormapIdx; int ColormapIdx;
int CurrentYAxis; int CurrentYAxis;
ImPlotLocation MousePosLocation;
ImPlotLocation LegendLocation;
ImPlotOrientation LegendOrientation;
ImPlotPlot() { ImPlotPlot() : Legend(this) {
Flags = PreviousFlags = ImPlotFlags_None; Flags = PreviousFlags = ImPlotFlags_None;
XAxis.Direction = ImPlotDirection_Horizontal; XAxis.Direction = ImPlotOrientation_Horizontal;
for (int i = 0; i < IMPLOT_Y_AXES; ++i) for (int i = 0; i < IMPLOT_Y_AXES; ++i)
YAxis[i].Direction = ImPlotDirection_Vertical; YAxis[i].Direction = ImPlotOrientation_Vertical;
SelectStart = QueryStart = ImVec2(0,0); SelectStart = QueryStart = ImVec2(0,0);
Selecting = Querying = Queried = DraggingQuery = false; Selecting = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSide = false;
ColormapIdx = CurrentYAxis = 0; ColormapIdx = CurrentYAxis = 0;
LegendLocation = ImPlotLocation_North | ImPlotLocation_West;
LegendOrientation = ImPlotOrientation_Vertical;
MousePosLocation = ImPlotLocation_South | ImPlotLocation_East;
} }
}; };
// Temporary data storage for upcoming plot // Temporary data storage for upcoming plot
@ -619,17 +633,16 @@ struct ImPlotContext {
// Plot States // Plot States
ImPool<ImPlotPlot> Plots; ImPool<ImPlotPlot> Plots;
ImPlotPlot* CurrentPlot; ImPlotPlot* CurrentPlot;
ImPlotItem* CurrentItem; ImPlotItem* CurrentItem;
ImPlotItem* PreviousItem; ImPlotItem* PreviousItem;
// Legend
ImVector<int> LegendIndices;
ImGuiTextBuffer LegendLabels;
// Bounding Boxes // Bounding Boxes
ImRect BB_Frame; ImRect BB_Frame;
ImRect BB_Canvas; ImRect BB_Canvas;
ImRect BB_Plot; ImRect BB_Plot;
ImRect BB_Axes;
ImRect BB_X;
ImRect BB_Y[IMPLOT_Y_AXES];
// Axis States // Axis States
ImPlotAxisColor Col_X; ImPlotAxisColor Col_X;
@ -743,15 +756,11 @@ IMPLOT_API bool BeginItem(const char* label_id, ImPlotCol recolor_from = -1);
// Ends an item (call only if BeginItem returns true). Pops PlotClipRect. // Ends an item (call only if BeginItem returns true). Pops PlotClipRect.
IMPLOT_API void EndItem(); IMPLOT_API void EndItem();
// Register or get an existing item from the current plot // Register or get an existing item from the current plot.
IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created = NULL); IMPLOT_API ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created = NULL);
// Get the ith plot item from the current plot // Get a plot item from the current plot.
IMPLOT_API ImPlotItem* GetItem(int i);
// Get a plot item from the current plot
IMPLOT_API ImPlotItem* GetItem(const char* label_id); IMPLOT_API ImPlotItem* GetItem(const char* label_id);
// Gets a plot item from a specific plot // Gets the current item.
IMPLOT_API ImPlotItem* GetItem(const char* plot_title, const char* item_label_id);
// Gets the current item
IMPLOT_API ImPlotItem* GetCurrentItem(); IMPLOT_API ImPlotItem* GetCurrentItem();
// Busts the cache for every item for every plot in the current context. // Busts the cache for every item for every plot in the current context.
IMPLOT_API void BustItemCache(); IMPLOT_API void BustItemCache();
@ -791,10 +800,14 @@ IMPLOT_API void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed =
// [SECTION] Legend Utils // [SECTION] Legend Utils
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Returns the number of entries in the current legend // Gets the position of an inner rect that is located inside of an outer rect according to an ImPlotLocation and padding amount.
IMPLOT_API int GetLegendCount(); IMPLOT_API ImVec2 GetLocationPos(const ImRect& outer_rect, const ImVec2& inner_size, ImPlotLocation location, const ImVec2& pad = ImVec2(0,0));
// Gets the ith entry string for the current legend // Calculates the bounding box size of a legend
IMPLOT_API const char* GetLegendLabel(int i); IMPLOT_API ImVec2 CalcLegendSize(ImPlotLegend& legend, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orientation);
// Renders legend entries into a bounding box
IMPLOT_API void ShowLegendEntries(ImPlotLegend& legend, const ImRect& legend_bb, bool interactable, const ImVec2& pad, const ImVec2& spacing, ImPlotOrientation orientation, ImDrawList& DrawList);
// Shows an alternate legend for the plot identified by #title_id, outside of the plot frame (can be called before or after of Begin/EndPlot but must occur in the same ImGui window!).
IMPLOT_API void ShowAltLegend(const char* title_id, ImPlotOrientation orientation = ImPlotOrientation_Vertical, const ImVec2 size = ImVec2(0,0), bool interactable = true);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Tick Utils // [SECTION] Tick Utils

View File

@ -60,9 +60,9 @@ ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created) {
int idx = gp.CurrentPlot->Items.GetIndex(item); int idx = gp.CurrentPlot->Items.GetIndex(item);
item->ID = id; item->ID = id;
if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) { if (ImGui::FindRenderedTextEnd(label_id, NULL) != label_id) {
gp.LegendIndices.push_back(idx); gp.CurrentPlot->Legend.Indices.push_back(idx);
item->NameOffset = gp.LegendLabels.size(); item->NameOffset = gp.CurrentPlot->Legend.Labels.size();
gp.LegendLabels.append(label_id, label_id + strlen(label_id) + 1); gp.CurrentPlot->Legend.Labels.append(label_id, label_id + strlen(label_id) + 1);
} }
else { else {
item->Show = true; item->Show = true;
@ -72,26 +72,12 @@ ImPlotItem* RegisterOrGetItem(const char* label_id, bool* just_created) {
return item; return item;
} }
ImPlotItem* GetItem(int i) {
ImPlotContext& gp = *GImPlot;
return gp.CurrentPlot->Items.GetByIndex(gp.LegendIndices[i]);
}
ImPlotItem* GetItem(const char* label_id) { ImPlotItem* GetItem(const char* label_id) {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
ImGuiID id = ImGui::GetID(label_id); ImGuiID id = ImGui::GetID(label_id);
return gp.CurrentPlot->Items.GetByKey(id); return gp.CurrentPlot->Items.GetByKey(id);
} }
ImPlotItem* GetItem(const char* plot_title, const char* item_label_id) {
ImPlotPlot* plot = GetPlot(plot_title);
if (plot) {
ImGuiID id = ImGui::GetID(item_label_id);
return plot->Items.GetByKey(id);
}
return NULL;
}
ImPlotItem* GetCurrentItem() { ImPlotItem* GetCurrentItem() {
ImPlotContext& gp = *GImPlot; ImPlotContext& gp = *GImPlot;
return gp.CurrentItem; return gp.CurrentItem;
@ -1756,4 +1742,13 @@ void PlotText(const char* text, double x, double y, bool vertical, const ImVec2&
PopPlotClipRect(); PopPlotClipRect();
} }
//-----------------------------------------------------------------------------
// PLOT DUMMY
//-----------------------------------------------------------------------------
void PlotDummy(const char* label_id) {
if (BeginItem(label_id, ImPlotCol_Line))
EndItem();
}
} // namespace ImPlot } // namespace ImPlot