diff --git a/implot.h b/implot.h index 87825ed2..341b3456 100644 --- a/implot.h +++ b/implot.h @@ -95,6 +95,7 @@ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags typedef int ImPlotBubblesFlags; // -> ImPlotBubblesFlags +typedef int ImPlotPolygonFlags; // -> ImPlotPolygonFlags_ typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ @@ -271,6 +272,12 @@ enum ImPlotBubblesFlags_ { ImPlotBubblesFlags_None = 0, // default }; +// Flags for PlotPolygon. Used by setting ImPlotSpec::Flags. +enum ImPlotPolygonFlags_ { + ImPlotPolygonFlags_None = 0, // default (closed, convex polygon) + ImPlotPolygonFlags_Concave = 1 << 10, // use concave polygon filling (slower but supports concave shapes) +}; + // Flags for PlotStairs. Used by setting ImPlotSpec::Flags. enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default @@ -960,6 +967,9 @@ IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* da IMPLOT_TMP void PlotBubbles(const char* label_id, const T* values, const T* szs, int count, double xscale=1, double xstart=0, const ImPlotSpec& spec=ImPlotSpec()); IMPLOT_TMP void PlotBubbles(const char* label_id, const T* xs, const T* ys, const T* szs, int count, const ImPlotSpec& spec=ImPlotSpec()); +// Plots a polygon. Points are specified in counter-clockwise order. If concave, make sure to set the Concave flag. +IMPLOT_TMP void PlotPolygon(const char* label_id, const T* xs, const T* ys, int count, const ImPlotSpec& spec=ImPlotSpec()); + // 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, const ImPlotSpec& spec=ImPlotSpec()); IMPLOT_TMP void PlotStairs(const char* label_id, const T* xs, const T* ys, int count, const ImPlotSpec& spec=ImPlotSpec()); @@ -1193,7 +1203,7 @@ IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val); IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val); // Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleVar(int count = 1); - + // Gets the last item primary color (i.e. its legend icon color) IMPLOT_API ImVec4 GetLastItemColor(); @@ -1201,10 +1211,10 @@ IMPLOT_API ImVec4 GetLastItemColor(); IMPLOT_API const char* GetStyleColorName(ImPlotCol idx); // Returns the null terminated string name for an ImPlotMarker. IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); - + // Returns the next marker and advances the marker for the current plot. You need to call this between Begin/EndPlot! IMPLOT_API ImPlotMarker NextMarker(); - + //----------------------------------------------------------------------------- // [SECTION] Colormaps //----------------------------------------------------------------------------- @@ -1274,7 +1284,7 @@ IMPLOT_API void BustColorCache(const char* plot_title_id = nullptr); //----------------------------------------------------------------------------- // [SECTION] Input Mapping //----------------------------------------------------------------------------- - + // Provides access to input mapping structure for permanent modifications to controls for pan, select, etc. IMPLOT_API ImPlotInputMap& GetInputMap(); diff --git a/implot_demo.cpp b/implot_demo.cpp index c13cd828..3efd664e 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -437,7 +437,7 @@ void Demo_ScatterPlots() { //----------------------------------------------------------------------------- void Demo_BubblePlots() { - IMGUI_DEMO_MARKER("Demo_BubblePlots"); + IMGUI_DEMO_MARKER("Plots/Bubble Plots"); srand(0); static float xs[20], ys1[20], ys2[20], szs1[20], szs2[20]; for (int i = 0; i < 20; ++i) { @@ -460,6 +460,49 @@ void Demo_BubblePlots() { //----------------------------------------------------------------------------- +void Demo_PolygonPlots() { + IMGUI_DEMO_MARKER("Plots/Polygon Plots"); + // Triangle (convex) + static float tri_xs[3] = {0.5f, 1.0f, 0.0f}; + static float tri_ys[3] = {1.0f, 0.0f, 0.0f}; + + // Pentagon (convex) + static float pent_xs[5], pent_ys[5]; + for (int i = 0; i < 5; ++i) { + float angle = (float)i * 2.0f * 3.14159f / 5.0f - 3.14159f / 2.0f; + pent_xs[i] = 3.0f + 0.8f * cosf(angle); + pent_ys[i] = 0.5f + 0.8f * sinf(angle); + } + + // Star (concave), counter-clockwise + static float star_xs[10], star_ys[10]; + for (int i = 0; i < 10; ++i) { + float angle = (float)i * 2.0f * 3.14159f / 10.0f - 3.14159f / 2.0f; + float radius = (i % 2 == 0) ? 0.8f : 0.3f; + star_xs[i] = 5.5f + radius * cosf(angle); + star_ys[i] = 0.5f + radius * sinf(angle); + } + + if (ImPlot::BeginPlot("Polygon Plot", ImVec2(-1,0), ImPlotFlags_Equal)) { + ImPlot::PlotPolygon("Triangle", tri_xs, tri_ys, 3, { + ImPlotProp_FillAlpha, 0.5f, + }); + ImPlot::PlotPolygon("Pentagon", pent_xs, pent_ys, 5, { + ImPlotProp_FillAlpha, 0.5f, + ImPlotProp_FillColor, ImVec4(0,1,0,1), + }); + ImPlot::PlotPolygon("Star (Concave)", star_xs, star_ys, 10, { + ImPlotProp_FillAlpha, 0.5f, + ImPlotProp_FillColor, ImVec4(1,1,0,1), + ImPlotProp_Flags, ImPlotPolygonFlags_Concave, + }); + + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + void Demo_StairstepPlots() { IMGUI_DEMO_MARKER("Plots/Stairstep Plots"); static float ys1[21], ys2[21]; @@ -2440,6 +2483,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); DemoHeader("Bubble Plots", Demo_BubblePlots); + DemoHeader("Polygon Plots", Demo_PolygonPlots); 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 1af0cd2c..18b3cc95 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1701,7 +1701,6 @@ struct RendererCircleLine : RendererBase { mutable ImVec2 UV1; }; - 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_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)}; @@ -1941,6 +1940,65 @@ void PlotBubbles(const char* label_id, const T* xs, const T* ys, const T* szs, i CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO +//----------------------------------------------------------------------------- +// [SECTION] PlotPolygon +//----------------------------------------------------------------------------- + +template +void PlotPolygonEx(const char* label_id, const Getter& getter, const ImPlotSpec& spec) { + if (BeginItemEx(label_id, Fitter1(getter), spec, spec.FillColor, spec.Marker)) { + if (getter.Count < 2) { + EndItem(); + return; + } + const ImPlotNextItemData& s = GetItemData(); + const bool is_concave = ImHasFlag(spec.Flags, ImPlotPolygonFlags_Concave); + + ImDrawList& draw_list = *GetPlotDrawList(); + const ImPlotAxis& x_axis = GetCurrentPlot()->Axes[GetCurrentPlot()->CurrentX]; + const ImPlotAxis& y_axis = GetCurrentPlot()->Axes[GetCurrentPlot()->CurrentY]; + Transformer2 transformer(x_axis, y_axis); + + // Flip points to make sure they are in counter-clockwise order for correct filling when one axis is inverted + bool x_inv = ImHasFlag(x_axis.Flags, ImPlotAxisFlags_Invert); + bool y_inv = ImHasFlag(y_axis.Flags, ImPlotAxisFlags_Invert); + bool flip = !((x_inv ? 1 : 0) ^ (y_inv ? 1 : 0)); + + // Transform all points to screen space + ImVec2* points = (ImVec2*)IM_ALLOC(getter.Count * sizeof(ImVec2)); + for (int i = 0; i < getter.Count; ++i) { + ImPlotPoint p = flip ? getter[getter.Count - 1 - i] : getter[i]; + points[i] = transformer(p); + } + + if (s.RenderFill && getter.Count >= 3) { + const ImU32 col_fill = ImGui::GetColorU32(s.Spec.FillColor); + if (is_concave) + draw_list.AddConcavePolyFilled(points, getter.Count, col_fill); + else + draw_list.AddConvexPolyFilled(points, getter.Count, col_fill); + } + if (s.RenderLine && getter.Count >= 2) { + const ImU32 col_line = ImGui::GetColorU32(s.Spec.LineColor); + draw_list.AddPolyline(points, getter.Count, col_line, ImDrawFlags_Closed, s.Spec.LineWeight); + } + IM_FREE(points); + + EndItem(); + } +} + +template +void PlotPolygon(const char* label_id, const T* xs, const T* ys, int count, const ImPlotSpec& spec) { + GetterXY,IndexerIdx> getter(IndexerIdx(xs,count,spec.Offset,Stride(spec)),IndexerIdx(ys,count,spec.Offset,Stride(spec)),count); + return PlotPolygonEx(label_id, getter, spec); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API void PlotPolygon(const char* label_id, const T* xs, const T* ys, int count, const ImPlotSpec& spec); +CALL_INSTANTIATE_FOR_NUMERIC_TYPES() +#undef INSTANTIATE_MACRO + //----------------------------------------------------------------------------- // [SECTION] PlotStairs //-----------------------------------------------------------------------------