diff --git a/implot.cpp b/implot.cpp index 3bdfebe..282744a 100644 --- a/implot.cpp +++ b/implot.cpp @@ -49,6 +49,7 @@ ImPlotStyle::ImPlotStyle() { MarkerWeight = 1; ErrorBarSize = 5; ErrorBarWeight = 1.5; + DigitalBitHeight = 7; Colors[ImPlotCol_Line] = IM_COL_AUTO; Colors[ImPlotCol_Fill] = IM_COL_AUTO; @@ -61,7 +62,7 @@ ImPlotStyle::ImPlotStyle() { Colors[ImPlotCol_XAxis] = IM_COL_AUTO; Colors[ImPlotCol_YAxis] = IM_COL_AUTO; Colors[ImPlotCol_Selection] = ImVec4(1,1,0,1); - Colors[ImPlotCol_Query] = ImVec4(0,1,0,1); + Colors[ImPlotCol_Query] = ImVec4(0,1,0,1); } ImPlotRange::ImPlotRange() { @@ -327,6 +328,8 @@ struct ImPlotContext { ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImNextPlotData NextPlotData; + // Digital plot item count + int DigitalPlotItemCnt; }; /// Global plot context @@ -1051,6 +1054,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons gp.Extents.Max.y = -INFINITY; // clear item names gp.LegendLabels.Buf.resize(0); + // reset digital plot items count + gp.DigitalPlotItemCnt = 0; return true; } @@ -1458,12 +1463,13 @@ struct ImPlotStyleVarInfo { static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight - { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) } // ImPlotStyleVar_ErrorBarWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, LineWeight) }, // ImPlotStyleVar_LineWeight + { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight + { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) } // ImPlotStyleVar_DigitalBitHeight }; static const ImPlotStyleVarInfo* GetPlotStyleVarInfo(ImPlotStyleVar idx) @@ -2202,4 +2208,91 @@ void PlotLabel(const char* text, float x, float y, bool vertical, const ImVec2& PopPlotClipRect(); } -} // namespace ImGui \ No newline at end of file +template +inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int offset) +{ + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() Needs to be called between BeginPlot() and EndPlot()!"); + + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) + return; + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + + const bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; + + if (gp.Style.Colors[ImPlotCol_Line].w != -1) + item->Color = gp.Style.Colors[ImPlotCol_Line]; + + // find data extents + if (gp.FitThisFrame) { + for (int i = 0; i < count; ++i) { + ImVec2 p = getter(i); + FitPoint(p); + } + } + + ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); + bool cull = HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_CullData); + + const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; + + // render digital signals as "pixel bases" rectangles + if (count > 1 && rend_line) { + // + const float mx = (gp.PixelRange.Max.x - gp.PixelRange.Min.x) / (gp.CurrentPlot->XAxis.Max - gp.CurrentPlot->XAxis.Min); + int pixY_0 = line_weight; + int pixY_1 = gp.Style.DigitalBitHeight; + int pixY_Offset = 20;//20 pixel from bottom due to mouse cursor label + int pixY_chOffset = pixY_1 + 3; //3 pixels between channels + ImVec2 pMin, pMax; + float y0 = (gp.PixelRange.Min.y) + ((-pixY_chOffset * gp.DigitalPlotItemCnt) - pixY_0 - pixY_Offset); + float y1 = (gp.PixelRange.Min.y) + ((-pixY_chOffset * gp.DigitalPlotItemCnt) - pixY_1 - pixY_Offset); + const int segments = count - 1; + int i1 = offset; + for (int s = 0; s < segments; ++s) { + const int i2 = (i1 + 1) % count; + ImVec2 itemData1 = getter(i1); + ImVec2 itemData2 = getter(i2); + i1 = i2; + pMin.x = gp.PixelRange.Min.x + mx * (itemData1.x - gp.CurrentPlot->XAxis.Min); + pMin.y = (gp.PixelRange.Min.y) + ((-pixY_chOffset * gp.DigitalPlotItemCnt) - pixY_Offset); + pMax.x = gp.PixelRange.Min.x + mx * (itemData2.x - gp.CurrentPlot->XAxis.Min); + pMax.y = ((int) itemData1.y == 0) ? y0 : y1; + //plot only one rectangle for same digital state + while (((s+2) < segments) && ((int) itemData1.y == (int) itemData2.y)) { + const int i2 = (i1 + 1) % count; + itemData2 = getter(i2); + pMax.x = gp.PixelRange.Min.x + mx * (itemData2.x - gp.CurrentPlot->XAxis.Min); + i1 = i2; + s++; + } + //do not extend plot outside plot range + if (pMin.x < gp.PixelRange.Min.x) pMin.x = gp.PixelRange.Min.x; + if (pMax.x < gp.PixelRange.Min.x) pMax.x = gp.PixelRange.Min.x; + if (pMin.x > gp.PixelRange.Max.x) pMin.x = gp.PixelRange.Max.x; + if (pMax.x > gp.PixelRange.Max.x) pMax.x = gp.PixelRange.Max.x; + //plot a rectangle that extends up to x2 with y1 height + if ((pMax.x > pMin.x) && (!cull || gp.BB_Grid.Contains(pMin) || gp.BB_Grid.Contains(pMax))) { + auto colAlpha = item->Color; + colAlpha.w = item->Highlight ? 1.0 : 0.9; + DrawList.AddRectFilled(pMin, pMax, GetColorU32(colAlpha)); + } + } + gp.DigitalPlotItemCnt++; + } + + ImGui::PopClipRect(); +} + +void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) { + Getter2D getter(xs,ys,stride); + return PlotDigitalEx(label_id, getter, count, offset); +} + +void PlotDigital(const char* label_id, ImVec2 (*getter_func)(void* data, int idx), void* data, int count, int offset) { + GetterFuncPtrImVec2 getter(getter_func,data); + return PlotDigitalEx(label_id, getter, count, offset); +} + +} // namespace ImGui diff --git a/implot.h b/implot.h index b5ceeb5..6eb67c9 100644 --- a/implot.h +++ b/implot.h @@ -82,12 +82,13 @@ enum ImPlotCol_ { }; enum ImPlotStyleVar_ { - ImPlotStyleVar_LineWeight, // float, line weight in pixels - ImPlotStyleVar_Marker, // int, marker specification - ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") - ImPlotStyleVar_MarkerWeight, // float, outline weight of markers in pixels - ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels - ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels + ImPlotStyleVar_LineWeight, // float, line weight in pixels + ImPlotStyleVar_Marker, // int, marker specification + ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") + ImPlotStyleVar_MarkerWeight, // float, outline weight of markers in pixels + ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels + ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels + ImPlotStyleVar_DigitalBitHeight, // int, digital channels bit height (at 1) ImPlotStyleVar_COUNT }; @@ -121,6 +122,7 @@ struct ImPlotStyle { float MarkerWeight; // = 1, outline weight of markers in pixels float ErrorBarSize; // = 5, error bar whisker width in pixels float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels + int DigitalBitHeight; // = 7, digital channels bit height (at 1) ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors ImPlotStyle(); }; @@ -173,6 +175,9 @@ void PlotErrorBars(const char* label_id, ImVec4 (*getter)(void* data, int idx), void PlotPieChart(const char** label_ids, float* values, int count, const ImVec2& center, float radius, bool show_percents = true, float angle0 = 90); // Plots a text label at point x,y. void PlotLabel(const char* text, float x, float y, bool vertical = false, const ImVec2& pixel_offset = ImVec2(0,0)); +// Plots digital channels. +void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset = 0, int stride = sizeof(float) + sizeof(bool)); +void PlotDigital(const char* label_id, ImVec2 (*getter)(void* data, int idx), void* data, int count, int offset = 0); //----------------------------------------------------------------------------- // Plot Queries @@ -248,4 +253,4 @@ void PopPlotClipRect(); // Shows the ImPlot demo. Add implot_demo.cpp to your sources! void ShowImPlotDemoWindow(bool* p_open = NULL); -} // namespace ImGui \ No newline at end of file +} // namespace ImGui diff --git a/implot_demo.cpp b/implot_demo.cpp index 6ea85b3..2b91691 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -46,6 +46,12 @@ struct ScrollingData { Offset = (Offset + 1) % MaxSize; } } + void Erase() { + if (Data.size() > 0) { + Data.clear(); + Offset = 0; + } + } }; struct RollingData { @@ -81,7 +87,7 @@ namespace ImGui { void ShowImPlotDemoWindow(bool* p_open) { - ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos; + //ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos; ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(520, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot Demo", p_open); @@ -513,6 +519,146 @@ void ShowImPlotDemoWindow(bool* p_open) { ImGui::EndDragDropTarget(); } } + + if (ImGui::CollapsingHeader("Digital")) { + + static int bitHeight = 7; + ImGui::SetNextItemWidth(100); + ImGui::DragInt("Bit Hieght", &bitHeight, 0.2, 5, 50); + /// Set the X axis range of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axis will be locked. + //void SetNextPlotRangeX(float x_min, float x_max, ImGuiCond cond = ImGuiCond_Once); + static bool paused = false; + #define K_PLOT_DIGITAL_CH_COUNT 8 + #define K_PLOT_ANALOG_CH_COUNT 8 + static ScrollingData dataDigital[K_PLOT_DIGITAL_CH_COUNT]; + static ScrollingData dataAnalog[K_PLOT_ANALOG_CH_COUNT]; + static bool showDigital[K_PLOT_DIGITAL_CH_COUNT]; + static bool showAnalog[K_PLOT_ANALOG_CH_COUNT]; + + ImGui::BulletText("Drag data items from the left column onto the plot."); + ImGui::BeginGroup(); + if (ImGui::Button("Clear", {100, 0})) { + for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) + showDigital[i] = false; + for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) + showAnalog[i] = false; + } + if (ImGui::Button(paused ? "Resume" : "Pause", {100,0})) + paused = !paused; + ImGui::Separator(); + for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) { + char label[32]; + sprintf(label, "digital_data_%d", i); + ImGui::Checkbox(label, &showDigital[i]); + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + ImGui::SetDragDropPayload("DND_DIGITAL_PLOT", &i, sizeof(int)); + ImGui::TextUnformatted(label); + ImGui::EndDragDropSource(); + } + } + for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) { + char label[32]; + sprintf(label, "analog_data_%d", i); + ImGui::Checkbox(label, &showAnalog[i]); + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + ImGui::SetDragDropPayload("DND_ANALOG_PLOT", &i, sizeof(int)); + ImGui::TextUnformatted(label); + ImGui::EndDragDropSource(); + } + } + ImGui::EndGroup(); + ImGui::SameLine(); + static float t = 0; + if (true) { + t += ImGui::GetIO().DeltaTime; + + //digital signal values + int i = 0; + if (showDigital[i]) + dataDigital[i].AddPoint(t, sin(t) > 0.45); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, sin(t) < 0.45); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, sin(t) > 0.83); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, sin(t) < 0.17); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, cos(t) > 0.45); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, cos(t) < 0.45); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, cos(t) > 0.83); + i++; + if (showDigital[i]) + dataDigital[i].AddPoint(t, cos(t) < 0.17); + + //Analog signal values + i = 0; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, sin(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, cos(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, sin(t) * cos(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, sin(t) - cos(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, cos(t) - sin(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, sin(t) + cos(t)); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, (sin(t) + cos(t)) / (sin(t) * cos(t))); + i++; + if (showAnalog[i]) + dataAnalog[i].AddPoint(t, (cos(t) + cos(t)) / (cos(t) * cos(t))); + i++; + + } + ImGui::SetNextPlotRangeX(t - 60.0f, t, paused ? ImGuiCond_Once : ImGuiCond_Always); + if (ImGui::BeginPlot("##DND", NULL, NULL, ImVec2(-1,300), ImPlotFlags_Default)) { + for (int i = 0; i < K_PLOT_DIGITAL_CH_COUNT; ++i) { + if (showDigital[i]) { + char label[32]; + sprintf(label, "digital_data_%d", i); + ImGui::PushPlotStyleVar(ImPlotStyleVar_DigitalBitHeight, bitHeight); + ImGui::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); + ImGui::PopPlotStyleVar(); + } + } + for (int i = 0; i < K_PLOT_ANALOG_CH_COUNT; ++i) { + if (showAnalog[i]) { + char label[32]; + sprintf(label, "analog_data_%d", i); + if (dataAnalog[i].Data.size() > 0) + ImGui::Plot(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); + } + } + ImGui::EndPlot(); + } + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DIGITAL_PLOT")) { + int i = *(int*)payload->Data; + showDigital[i] = true; + } + else if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_ANALOG_PLOT")) { + int i = *(int*)payload->Data; + showAnalog[i] = true; + } + ImGui::EndDragDropTarget(); + } + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Custom Styles")) { static ImVec4 my_palette[3] = { @@ -579,4 +725,4 @@ void ShowImPlotDemoWindow(bool* p_open) { } -} // namespace ImGui \ No newline at end of file +} // namespace ImGui