diff --git a/TODO.md b/TODO.md index 03b7303c..65e7bd40 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,7 @@ The list below represents a combination of high-priority work, nice-to-have feat ## Plot Items -- add `PlotBubbles` (see MATLAB bubble chart) +- add legend for `PlotBubbles` (see MATLAB bubble chart) - add non-zero references for `PlotBars` etc. - fix appearance of `PlotBars` spacing diff --git a/implot.h b/implot.h index a7961c4f..dd347658 100644 --- a/implot.h +++ b/implot.h @@ -89,6 +89,7 @@ typedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags +typedef int ImPlotBubblesFlags; // -> ImPlotBubblesFlags typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ @@ -242,6 +243,12 @@ enum ImPlotScatterFlags_ { ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped }; +// Flags for PlotBubbles +enum ImPlotBubblesFlags_ { + ImPlotBubblesFlags_None = 0, // default + ImPlotBubblesFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped +}; + // Flags for PlotStairs enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default @@ -481,6 +488,18 @@ struct ImPlotPoint { }; IM_MSVC_RUNTIME_CHECKS_RESTORE +// Double precision point with three coordinates used by ImPlot. +IM_MSVC_RUNTIME_CHECKS_OFF +struct ImPlotPoint3D { + double x, y, z; + constexpr ImPlotPoint3D() : x(0.0), y(0.0), z(0.0) { } + constexpr ImPlotPoint3D(double _x, double _y, double _z) : x(_x), y(_y), z(_z) { } + double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2); return ((double*)(void*)(char*)this)[idx]; } + double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2); return ((const double*)(const void*)(const char*)this)[idx]; } + +}; +IM_MSVC_RUNTIME_CHECKS_RESTORE + // Range defined by a min/max value. struct ImPlotRange { double Min, Max; @@ -588,6 +607,7 @@ typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_da // Callback signature for data getter. typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); +typedef ImPlotPoint3D (*ImPlotGetter3D)(int idx, void* user_data); // Callback signature for axis transform. typedef double (*ImPlotTransform)(double value, void* user_data); @@ -864,6 +884,11 @@ IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, do IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); +// Plots a bubble graph. Default marker is ImPlotMarker_Circle. +IMPLOT_TMP void PlotBubbles(const char* label_id, const T* values, const T* szs, int count, double xscale=1, double xstart=0, ImPlotBubblesFlags flags=0, float min_pxsize=3, float max_pxsize=50, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotBubbles(const char* label_id, const T* xs, const T* ys, const T* szs, int count, ImPlotBubblesFlags flags=0, float min_pxsize=3, float max_pxsize=50, int offset=0, int stride=sizeof(T)); +IMPLOT_API void PlotBubblesG(const char* label_id, ImPlotGetter3D getter, void* data, int count, ImPlotBubblesFlags flags=0, float min_pxsize=3, float max_pxsize=50); + // Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] IMPLOT_TMP void PlotStairs(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, ImPlotStairsFlags flags=0, int offset=0, int stride=sizeof(T)); diff --git a/implot_demo.cpp b/implot_demo.cpp index d4536a4a..894d9373 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -416,6 +416,29 @@ void Demo_ScatterPlots() { //----------------------------------------------------------------------------- +void Demo_BubblePlots() { + srand(0); + static float xs[20], ys1[20], ys2[20], szs1[20], szs2[20]; + for (int i = 0; i < 20; ++i) { + xs[i] = i * 0.01f; + ys1[i] = (float)rand() / (float)RAND_MAX; + ys2[i] = 0.5f + 0.3f * (2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f); + + szs1[i] = 10.0f + 1000.0f * ((float)rand() / (float)RAND_MAX); + szs2[i] = 5.0f + 200.0f * ((float)rand() / (float)RAND_MAX); + } + + + if (ImPlot::BeginPlot("Bubble Plot")) { + ImPlot::PlotBubbles("Data 1", xs, ys1, szs1, 20); + ImPlot::SetNextMarkerStyle(IMPLOT_AUTO, IMPLOT_AUTO, IMPLOT_AUTO_COL, 1, IMPLOT_AUTO_COL); + ImPlot::PlotBubbles("Data 2", xs, ys2, szs2, 20, ImPlotBubblesFlags_None, 5, 20); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + void Demo_StairstepPlots() { static float ys1[21], ys2[21]; for (int i = 0; i < 21; ++i) { @@ -2231,6 +2254,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Filled Line Plots", Demo_FilledLinePlots); DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); + DemoHeader("Bubble Plots", Demo_BubblePlots); DemoHeader("Realtime Plots", Demo_RealtimePlots); DemoHeader("Stairstep Plots", Demo_StairstepPlots); DemoHeader("Bar Plots", Demo_BarPlots); diff --git a/implot_items.cpp b/implot_items.cpp index 741eaaf2..9320da03 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -569,6 +569,18 @@ struct GetterXY { const int Count; }; +template +struct GetterXYZ { + GetterXYZ(_IndexerX x, _IndexerY y, _IndexerZ z, int count) : IndxerX(x), IndxerY(y), IndxerZ(z), Count(count) { } + template IMPLOT_INLINE ImPlotPoint3D operator()(I idx) const { + return ImPlotPoint3D(IndxerX(idx),IndxerY(idx),IndxerZ(idx)); + } + const _IndexerX IndxerX; + const _IndexerY IndxerY; + const _IndexerZ IndxerZ; + const int Count; +}; + /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : @@ -584,6 +596,20 @@ struct GetterFuncPtr { const int Count; }; +struct GetterFuncPtr3D { + GetterFuncPtr3D(ImPlotGetter3D getter, void* data, int count) : + Getter(getter), + Data(data), + Count(count) + { } + template IMPLOT_INLINE ImPlotPoint3D operator()(I idx) const { + return Getter(idx, Data); + } + ImPlotGetter3D Getter; + void* const Data; + const int Count; +}; + template struct GetterOverrideX { GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } @@ -664,6 +690,19 @@ struct Fitter1 { const _Getter1& Getter; }; +template +struct FitterBubbles1 { + FitterBubbles1(const _Getter1& getter) : Getter(getter) { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + for (int i = 0; i < Getter.Count; ++i) { + ImPlotPoint3D p = Getter(i); + x_axis.ExtendFitWith(y_axis, p.x, p.y); + y_axis.ExtendFitWith(x_axis, p.y, p.x); + } + } + const _Getter1& Getter; +}; + template struct FitterX { FitterX(const _Getter1& getter) : Getter(getter) { } @@ -1496,7 +1535,123 @@ struct RendererMarkersLine : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererVariableSizedMarkersFill : RendererBase { + RendererVariableSizedMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float min_size, float max_size, float min_pxsize, float max_pxsize, ImU32 col) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Col(col), + MinSize(min_size), + MaxSize(max_size), + MinPxSize(min_pxsize), + MaxPxSize(max_pxsize) + { } + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint3D p3D = Getter(prim); + float size = MinPxSize + (MaxPxSize - MinPxSize) * (((float) p3D.z) - MinSize)/(MaxSize - MinSize); + ImVec2 p = this->Transformer(ImPlotPoint(p3D.x,p3D.y)); + if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) { + for (int i = 0; i < Count; i++) { + draw_list._VtxWritePtr[0].pos.x = p.x + Marker[i].x * size; + draw_list._VtxWritePtr[0].pos.y = p.y + Marker[i].y * size; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = Col; + draw_list._VtxWritePtr++; + } + for (int i = 2; i < Count; i++) { + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i - 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i); + draw_list._IdxWritePtr += 3; + } + draw_list._VtxCurrentIdx += (ImDrawIdx)Count; + return true; + } + return false; + } + const _Getter& Getter; + const ImVec2* Marker; + const int Count; + const ImU32 Col; + mutable ImVec2 UV; + const float MinSize; + const float MaxSize; + const float MinPxSize; + const float MaxPxSize; +}; + +template +struct RendererVariableSizedMarkersLine : RendererBase { + RendererVariableSizedMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float min_size, float max_size, float min_pxsize, float max_pxsize, float weight, ImU32 col) : + RendererBase(getter.Count, count/2*6, count/2*4), + Getter(getter), + Marker(marker), + Count(count), + HalfWeight(ImMax(1.0f,weight)*0.5f), + Col(col), + MinSize(min_size), + MaxSize(max_size), + MinPxSize(min_pxsize), + MaxPxSize(max_pxsize) + { } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImPlotPoint3D p3D = Getter(prim); + float size = MinPxSize + (MaxPxSize - MinPxSize) * (((float) p3D.z) - MinSize)/(MaxSize - MinSize); + ImVec2 p = this->Transformer(ImPlotPoint(p3D.x,p3D.y)); + if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) { + for (int i = 0; i < Count; i = i + 2) { + ImVec2 p1(p.x + Marker[i].x * size, p.y + Marker[i].y * size); + ImVec2 p2(p.x + Marker[i+1].x * size, p.y + Marker[i+1].y * size); + PrimLine(draw_list, p1, p2, HalfWeight, Col, UV0, UV1); + } + return true; + } + return false; + } + const _Getter& Getter; + const ImVec2* Marker; + const int Count; + mutable float HalfWeight; + const ImU32 Col; + mutable ImVec2 UV0; + mutable ImVec2 UV1; + const float MinSize; + const float MaxSize; + const float MinPxSize; + const float MaxPxSize; +}; + static const ImVec2 MARKER_FILL_CIRCLE[10] = {ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f),ImVec2(0.30901697f, 0.95105654f),ImVec2(-0.30901703f, 0.9510565f),ImVec2(-0.80901706f, 0.5877852f),ImVec2(-1.0f, 0.0f),ImVec2(-0.80901694f, -0.58778536f),ImVec2(-0.3090171f, -0.9510565f),ImVec2(0.30901712f, -0.9510565f),ImVec2(0.80901694f, -0.5877853f)}; +static const ImVec2 MARKER_HIGH_RES_FILL_CIRCLE[20] = { + ImVec2(1.0f, 0.0f), + ImVec2(0.9510565f, 0.30901697f), + ImVec2(0.809017f, 0.58778524f), + ImVec2(0.58778524f, 0.809017f), + ImVec2(0.30901697f, 0.9510565f), + ImVec2(0.0f, 1.0f), + ImVec2(-0.30901697f, 0.9510565f), + ImVec2(-0.58778524f, 0.809017f), + ImVec2(-0.809017f, 0.58778524f), + ImVec2(-0.9510565f, 0.30901697f), + ImVec2(-1.0f, 0.0f), + ImVec2(-0.9510565f, -0.30901697f), + ImVec2(-0.809017f, -0.58778524f), + ImVec2(-0.58778524f, -0.809017f), + ImVec2(-0.30901697f, -0.9510565f), + ImVec2(0.0f, -1.0f), + ImVec2(0.30901697f, -0.9510565f), + ImVec2(0.58778524f, -0.809017f), + ImVec2(0.809017f, -0.58778524f), + ImVec2(0.9510565f, -0.30901697f) +}; static const ImVec2 MARKER_FILL_SQUARE[4] = {ImVec2(SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2)}; static const ImVec2 MARKER_FILL_DIAMOND[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)}; static const ImVec2 MARKER_FILL_UP[3] = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f)}; @@ -1526,6 +1681,49 @@ static const ImVec2 MARKER_LINE_CIRCLE[20] = { ImVec2(0.80901694f, -0.5877853f), ImVec2(1.0f, 0.0f) }; +static const ImVec2 MARKER_HIGH_RES_LINE_CIRCLE[40] = { + ImVec2(1.00000000f, 0.00000000f), + ImVec2(0.95105652f, 0.30901699f), + ImVec2(0.95105652f, 0.30901699f), + ImVec2(0.80901699f, 0.58778525f), + ImVec2(0.80901699f, 0.58778525f), + ImVec2(0.58778525f, 0.80901699f), + ImVec2(0.58778525f, 0.80901699f), + ImVec2(0.30901699f, 0.95105652f), + ImVec2(0.30901699f, 0.95105652f), + ImVec2(0.00000000f, 1.00000000f), + ImVec2(0.00000000f, 1.00000000f), + ImVec2(-0.30901699f, 0.95105652f), + ImVec2(-0.30901699f, 0.95105652f), + ImVec2(-0.58778525f, 0.80901699f), + ImVec2(-0.58778525f, 0.80901699f), + ImVec2(-0.80901699f, 0.58778525f), + ImVec2(-0.80901699f, 0.58778525f), + ImVec2(-0.95105652f, 0.30901699f), + ImVec2(-0.95105652f, 0.30901699f), + ImVec2(-1.00000000f, 0.00000000f), + ImVec2(-1.00000000f, 0.00000000f), + ImVec2(-0.95105652f, -0.30901699f), + ImVec2(-0.95105652f, -0.30901699f), + ImVec2(-0.80901699f, -0.58778525f), + ImVec2(-0.80901699f, -0.58778525f), + ImVec2(-0.58778525f, -0.80901699f), + ImVec2(-0.58778525f, -0.80901699f), + ImVec2(-0.30901699f, -0.95105652f), + ImVec2(-0.30901699f, -0.95105652f), + ImVec2(0.00000000f, -1.00000000f), + ImVec2(0.00000000f, -1.00000000f), + ImVec2(0.30901699f, -0.95105652f), + ImVec2(0.30901699f, -0.95105652f), + ImVec2(0.58778525f, -0.80901699f), + ImVec2(0.58778525f, -0.80901699f), + ImVec2(0.80901699f, -0.58778525f), + ImVec2(0.80901699f, -0.58778525f), + ImVec2(0.95105652f, -0.30901699f), + ImVec2(0.95105652f, -0.30901699f), + ImVec2(1.00000000f, 0.00000000f) +}; + static const ImVec2 MARKER_LINE_SQUARE[8] = {ImVec2(SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,SQRT_1_2)}; static const ImVec2 MARKER_LINE_DIAMOND[8] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(-1, 0), ImVec2(0, 1), ImVec2(0, 1), ImVec2(1, 0)}; static const ImVec2 MARKER_LINE_UP[6] = {ImVec2(SQRT_3_2,0.5f), ImVec2(0,-1),ImVec2(0,-1),ImVec2(-SQRT_3_2,0.5f),ImVec2(-SQRT_3_2,0.5f),ImVec2(SQRT_3_2,0.5f)}; @@ -1565,6 +1763,35 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool } } +template +void RenderVariableSizedMarkers(const _Getter& getter, ImPlotMarker marker, float min_size, float max_size, float min_pxsize, float max_pxsize, bool rend_fill, ImU32 col_fill, bool rend_line, ImU32 col_line, float weight) { + if (rend_fill) { + switch (marker) { + case ImPlotMarker_Circle : RenderPrimitives1(getter,MARKER_HIGH_RES_FILL_CIRCLE,20,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Square : RenderPrimitives1(getter,MARKER_FILL_SQUARE, 4,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Diamond : RenderPrimitives1(getter,MARKER_FILL_DIAMOND,4,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Up : RenderPrimitives1(getter,MARKER_FILL_UP, 3,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Down : RenderPrimitives1(getter,MARKER_FILL_DOWN, 3,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Left : RenderPrimitives1(getter,MARKER_FILL_LEFT, 3,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + case ImPlotMarker_Right : RenderPrimitives1(getter,MARKER_FILL_RIGHT, 3,min_size,max_size,min_pxsize,max_pxsize,col_fill); break; + } + } + if (rend_line) { + switch (marker) { + case ImPlotMarker_Circle : RenderPrimitives1(getter,MARKER_HIGH_RES_LINE_CIRCLE, 40,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Square : RenderPrimitives1(getter,MARKER_LINE_SQUARE, 8,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Diamond : RenderPrimitives1(getter,MARKER_LINE_DIAMOND, 8,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Up : RenderPrimitives1(getter,MARKER_LINE_UP, 6,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Down : RenderPrimitives1(getter,MARKER_LINE_DOWN, 6,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Left : RenderPrimitives1(getter,MARKER_LINE_LEFT, 6,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Right : RenderPrimitives1(getter,MARKER_LINE_RIGHT, 6,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Asterisk : RenderPrimitives1(getter,MARKER_LINE_ASTERISK,6,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Plus : RenderPrimitives1(getter,MARKER_LINE_PLUS, 4,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + case ImPlotMarker_Cross : RenderPrimitives1(getter,MARKER_LINE_CROSS, 4,min_size,max_size,min_pxsize,max_pxsize,weight,col_line); break; + } + } +} + //----------------------------------------------------------------------------- // [SECTION] PlotLine //----------------------------------------------------------------------------- @@ -1690,6 +1917,74 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in return PlotScatterEx(label_id, getter, flags); } +//----------------------------------------------------------------------------- +// [SECTION] PlotBubbles +//----------------------------------------------------------------------------- + +template +void PlotBubblesEx(const char* label_id, const Getter& getter, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize) { + const ImPlotNextItemData& s0 = GetItemData(); + bool is_marker_col_auto_fill = s0.Colors[ImPlotCol_MarkerFill].w < 0; + bool is_maker_line_auto_fill = s0.Colors[ImPlotCol_MarkerOutline].w < 0 && s0.MarkerWeight < 0; + + if (BeginItemEx(label_id, FitterBubbles1(getter), flags, ImPlotCol_MarkerOutline)) { + if (getter.Count <= 0) { + EndItem(); + return; + } + const ImPlotNextItemData& s = GetItemData(); + ImPlotMarker marker = s.Marker == ImPlotMarker_None ? ImPlotMarker_Circle: s.Marker; + if (marker != ImPlotMarker_None) { + if (ImHasFlag(flags,ImPlotBubblesFlags_NoClip)) { + PopPlotClipRect(); + PushPlotClipRect(max_pxsize); + } + + float min_size = HUGE_VAL; + float max_size = -HUGE_VAL; + + for (int i = 0; i < getter.Count; i++) { + ImPlotPoint3D p = getter(i); + min_size = min_size < p.z ? min_size : p.z; + max_size = max_size > p.z ? max_size : p.z; + } + + ImVec4 vec_col_fill = s.Colors[ImPlotCol_MarkerFill]; + if (is_marker_col_auto_fill) { + vec_col_fill.w *= 0.5f; + } + const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); + const ImU32 col_fill = ImGui::GetColorU32(vec_col_fill); + RenderVariableSizedMarkers(getter, marker, min_size, max_size, min_pxsize, max_pxsize, s.RenderMarkerFill, col_fill, !is_maker_line_auto_fill, col_line, s.MarkerWeight); + } + EndItem(); + } +} + +template +void PlotBubbles(const char* label_id, const T* values, const T* szs, int count, double xscale, double x0, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize, int offset, int stride) { + GetterXYZ,IndexerIdx> getter(IndexerLin(xscale,x0), IndexerIdx(values,count,offset,stride), IndexerIdx(szs,count,offset,stride),count); + PlotBubblesEx(label_id, getter, flags, min_pxsize, max_pxsize); +} + +template +void PlotBubbles(const char* label_id, const T* xs, const T* ys, const T* szs, int count, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize, int offset, int stride) { + GetterXYZ,IndexerIdx,IndexerIdx> getter(IndexerIdx(xs,count,offset,stride),IndexerIdx(ys,count,offset,stride), IndexerIdx(szs,count,offset,stride),count); + return PlotBubblesEx(label_id, getter, flags, min_pxsize, max_pxsize); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API void PlotBubbles(const char* label_id, const T* values, const T* szs, int count, double xscale, double x0, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize, int offset, int stride); \ + template IMPLOT_API void PlotBubbles(const char* label_id, const T* xs, const T* ys, const T* szs, int count, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize, int offset, int stride); +CALL_INSTANTIATE_FOR_NUMERIC_TYPES() +#undef INSTANTIATE_MACRO + +// custom +void PlotBubblesG(const char* label_id, ImPlotGetter3D getter_func, void* data, int count, ImPlotBubblesFlags flags, float min_pxsize, float max_pxsize) { + GetterFuncPtr3D getter(getter_func, data, count); + return PlotBubblesEx(label_id, getter, flags, min_pxsize, max_pxsize); +} + //----------------------------------------------------------------------------- // [SECTION] PlotStairs //-----------------------------------------------------------------------------