From add4124cf09bd8c2471c2893ebee03e8372b6f25 Mon Sep 17 00:00:00 2001 From: Raphael Nogueira Rezende Laroca Pinto <61098580+katyushapolye@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:43:56 -0300 Subject: [PATCH 01/13] Added a basic QuiverPlot functionality. Similar to other graphing library, a quiver plot is commonly used to plot vector fields. It currently supports colour mapping for magnitude, normalization of the displayed quivers and custom ranges. I also added some support functionality for it. --- implot.cpp | 41 +++-- implot.h | 41 ++++- implot_demo.cpp | 83 +++++++++- implot_internal.h | 4 +- implot_items.cpp | 389 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 528 insertions(+), 30 deletions(-) diff --git a/implot.cpp b/implot.cpp index 5910e93d..5550d031 100644 --- a/implot.cpp +++ b/implot.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.18 WIP +// ImPlot v0.17 /* @@ -4102,34 +4102,31 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m bool modified = false; bool clicked = false, hovered = false, held = false; + ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); - const bool is_movable = *x_min != *x_max || *y_min != *y_max; - if (is_movable) { - ImGui::KeepAliveID(id); - if (input) { - // middle point - ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); - clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); - if (out_clicked) *out_clicked = clicked; - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - } + ImGui::KeepAliveID(id); + if (input) { + // middle point + clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); + if (out_clicked) *out_clicked = clicked; + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + } - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - if (held && ImGui::IsMouseDragging(0)) { - for (int i = 0; i < 4; ++i) { - ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); - *y[i] = pp.y; - *x[i] = pp.x; - } - modified = true; + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + if (held && ImGui::IsMouseDragging(0)) { + for (int i = 0; i < 4; ++i) { + ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); + *y[i] = pp.y; + *x[i] = pp.x; } + modified = true; } for (int i = 0; i < 4; ++i) { // points - ImRect b_rect(p[i].x - DRAG_GRAB_HALF_SIZE, p[i].y - DRAG_GRAB_HALF_SIZE, p[i].x + DRAG_GRAB_HALF_SIZE, p[i].y + DRAG_GRAB_HALF_SIZE); + b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); ImGuiID p_id = id + i + 1; ImGui::KeepAliveID(p_id); if (input) { diff --git a/implot.h b/implot.h index d4acc66d..3b507d4a 100644 --- a/implot.h +++ b/implot.h @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.18 WIP +// ImPlot v0.17 // Table of Contents: // @@ -62,9 +62,9 @@ #endif // ImPlot version string. -#define IMPLOT_VERSION "0.18 WIP" +#define IMPLOT_VERSION "0.17" // ImPlot version integer encoded as XYYZZ (X=major, YY=minor, ZZ=patch). -#define IMPLOT_VERSION_NUM 1800 +#define IMPLOT_VERSION_NUM 1700 // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. @@ -92,6 +92,7 @@ typedef int ImPlotColormapScaleFlags; // -> ImPlotColormapScaleFlags_ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags +typedef int ImPlotQuiverFlags; // -> ImPlotQuiverFlags_ typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ @@ -246,6 +247,14 @@ enum ImPlotScatterFlags_ { ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped }; +// Flgs for PlotQuiver +enum ImPlotQuiverFlags_ { + ImPlotQuiverFlags_None = 0, // default + ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped + ImPlotQuiverFlags_Normalize = 1 << 11, // all arrows will be normalized to the same length + ImPlotQuiverFlags_Colored = 1 << 12 // arrow colors will be mapped to their magnitudes +}; + // Flags for PlotStairs enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default @@ -350,6 +359,7 @@ enum ImPlotCol_ { ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color) ImPlotCol_MarkerFill, // marker fill color (defaults to the current line color) ImPlotCol_ErrorBar, // error bar color (defaults to ImGuiCol_Text) + ImPlotCol_QuiverFill, // plot styling colors ImPlotCol_FrameBg, // plot frame background color (defaults to ImGuiCol_FrameBg) ImPlotCol_PlotBg, // plot area background color (defaults to ImGuiCol_WindowBg) @@ -485,6 +495,26 @@ struct ImPlotPoint { }; IM_MSVC_RUNTIME_CHECKS_RESTORE +IM_MSVC_RUNTIME_CHECKS_OFF +// Implementation of a 2D plot vector with double precision. +struct ImPlotQuiver { + double x, y; + double u, v; + double mag2; + IMPLOT_API constexpr ImPlotQuiver() : x(0.0), y(0.0), u(0.0), v(0.0), mag2(0.0) { } + IMPLOT_API constexpr ImPlotQuiver(double _x, double _y, double _u, double _v) : x(_x), y(_y), u(_u), v(_v), mag2((_u*_u + _v*_v)) { } + IMPLOT_API constexpr ImPlotQuiver(double _x, double _y, double _u, double _v, double _mag2) : x(_x), y(_y), u(_u), v(_v), mag2(_mag2) { } // ADD THIS + IMPLOT_API constexpr ImPlotQuiver(const ImVec2& p, const ImVec2& q) : x((double)p.x), y((double)p.y), u((double)q.x), v((double)q.y), mag2((q.x*q.x + q.y*q.y)) { } + IMPLOT_API constexpr ImPlotQuiver(const ImVec4& r) : x((double)r.x), y((double)r.y), u((double)r.z), v((double)r.w), mag2((r.z*r.z + r.w*r.w)) { } + IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3 || idx == 4); return ((double*)(void*)(char*)this)[idx]; } + IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3 || idx == 4); return ((const double*)(const void*)(const char*)this)[idx]; } +#ifdef IMPLOT_POINT_CLASS_EXTRA + IMPLOT_POINT_CLASS_EXTRA + +#endif +}; +IM_MSVC_RUNTIME_CHECKS_RESTORE + // Range defined by a min/max value. struct ImPlotRange { double Min, Max; @@ -868,6 +898,9 @@ 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 standard 2D Vector Field arrow plot. It has a direction and magnitude. +IMPLOT_TMP void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count,const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags = 0, int offset= 0, int stride=sizeof(T)); + // 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)); @@ -1130,6 +1163,8 @@ IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weig IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO); // Set the marker style for the next item only. IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); +// Set the quiver style for the next item only. +IMPLOT_API void SetNextQuiverStyle(float size, const ImVec4& col = IMPLOT_AUTO_COL); // Set the error bar style for the next item only. IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); diff --git a/implot_demo.cpp b/implot_demo.cpp index 2c079ad3..7fae8151 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.18 WIP +// ImPlot v0.17 // We define this so that the demo does not accidentally use deprecated API #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS @@ -415,6 +415,84 @@ void Demo_ScatterPlots() { ImPlot::EndPlot(); } } +//----------------------------------------------------------------------------- + +void Demo_QuiverPlots(){ + static float xs[100], ys[100], us[100], vs[100]; + static bool animate = false; + static float animation_speed = 1.0f; + + float time = animate ? ImGui::GetTime() * animation_speed : 0.0f; + + for (int i = 0; i < 10; ++i) { + for(int j = 0; j < 10; ++j){ + int idx = i*10 + j; + xs[idx] = ((float)j * 0.1f) - 0.5; + ys[idx] = ((float)i * 0.1f) - 0.5; + + if (animate) { + float k = 2.0f * 3.14159f; + float decay = expf(-0.1f * time); + us[idx] = decay * (sinf(k * xs[idx]) * cosf(k * ys[idx]) * cosf(time) + - cosf(k * xs[idx]) * sinf(k * ys[idx]) * sinf(time)); + vs[idx] = decay * (-cosf(k * xs[idx]) * sinf(k * ys[idx]) * cosf(time) + - sinf(k * xs[idx]) * cosf(k * ys[idx]) * sinf(time)); + } else { + // Taylor-Green vortex + float k = 2.0f * 3.14159f; + us[idx] = sinf(k * xs[idx]) * cosf(k * ys[idx]); + vs[idx] = -cosf(k * xs[idx]) * sinf(k * ys[idx]); + } + } + } + + static float scale_min = 0.00f; + static float scale_max = 1.0f; + static float baseSize = 12.0f; + static ImPlotColormap map = ImPlotColormap_Viridis; + + if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { + map = (map + 1) % ImPlot::GetColormapCount(); + BustColorCache("##Heatmap1"); + BustColorCache("##Heatmap2"); + } + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); + + ImGui::SetNextItemWidth(225); + ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); + if (scale_max <= scale_min + 0.01f) { + scale_max = scale_min + 0.01f; + } + + ImGui::SetNextItemWidth(225); + ImGui::DragFloat("Base Size",&baseSize,0.1f,0,100); + + // Animation controls + ImGui::Checkbox("Time Dependant Function", &animate); + if (animate) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(150); + ImGui::DragFloat("Speed", &animation_speed, 0.1f, 0.1f, 10.0f); + } + + static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; + ImGui::CheckboxFlags("Normalized", (unsigned int*)&qv_flags, ImPlotQuiverFlags_Normalize); + ImGui::CheckboxFlags("Color Coded", (unsigned int*)&qv_flags, ImPlotQuiverFlags_Colored); + + ImPlot::PushColormap(map); + if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { + ImPlot::SetupAxisTicks(ImAxis_X1,-0.5, 0.5, 11); + ImPlot::SetupAxisTicks(ImAxis_Y1,-0.5, 0.5, 11); + ImPlot::SetNextQuiverStyle(baseSize,ImPlot::GetColormapColor(1)); + ImPlot::SetupAxes("x","y"); + ImPlot::PlotQuiver("Magnitude", xs, ys,us,vs,100,scale_min,scale_max, qv_flags); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##Magnitude Scale", scale_min, scale_max); + ImPlot::PopColormap(); +} //----------------------------------------------------------------------------- @@ -670,7 +748,7 @@ void Demo_Heatmaps() { ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); - + static ImPlotHeatmapFlags hm_flags = 0; ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); @@ -2248,6 +2326,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Filled Line Plots", Demo_FilledLinePlots); DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); + DemoHeader("Quiver Plots", Demo_QuiverPlots); DemoHeader("Realtime Plots", Demo_RealtimePlots); DemoHeader("Stairstep Plots", Demo_StairstepPlots); DemoHeader("Bar Plots", Demo_BarPlots); diff --git a/implot_internal.h b/implot_internal.h index b9d453ab..88494a0a 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.18 WIP +// ImPlot v0.17 // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! @@ -1199,6 +1199,7 @@ struct ImPlotNextItemData { ImPlotMarker Marker; float MarkerSize; float MarkerWeight; + float QuiverSize; float FillAlpha; float ErrorBarSize; float ErrorBarWeight; @@ -1218,6 +1219,7 @@ struct ImPlotNextItemData { LineWeight = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO; Marker = IMPLOT_AUTO; HasHidden = Hidden = false; + } }; diff --git a/implot_items.cpp b/implot_items.cpp index 3b00ae4c..0d2ad14c 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.18 WIP +// ImPlot v0.17 #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS @@ -344,6 +344,13 @@ void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, flo gp.NextItemData.MarkerWeight = weight; } +void SetNextQuiverStyle(float size, const ImVec4& col) { + ImPlotContext& gp = *GImPlot; + gp.NextItemData.QuiverSize = size; + gp.NextItemData.Colors[ImPlotCol_Fill] = col; + +} + void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; @@ -570,6 +577,26 @@ struct GetterXY { const int Count; }; +// Functor for returning a 2D vector in 2D space (For Quiver plots) +template +struct GetterXYUV { + GetterXYUV(_IndexerX x, _IndexerY y, _IndexerU u, _IndexerV v, int count) + : IndxerX(x), IndxerY(y), IndxerU(u), IndxerV(v), Count(count) { } + + template IMPLOT_INLINE ImPlotQuiver operator()(I idx) const { + double u_val = IndxerU(idx); + double v_val = IndxerV(idx); + double mag = sqrt(u_val*u_val + v_val*v_val); + return ImPlotQuiver(IndxerX(idx), IndxerY(idx), u_val, v_val, mag); + } + + const _IndexerX IndxerX; + const _IndexerY IndxerY; + const _IndexerU IndxerU; + const _IndexerV IndxerV; + const int Count; +}; + /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : @@ -708,6 +735,20 @@ struct Fitter2 { const _Getter2& Getter2; }; + +template +struct FitterVector { + FitterVector(const _Getter1& getter) : Getter(getter) { } + void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { + for (int i = 0; i < Getter.Count; ++i) { + ImPlotQuiver 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 FitterBarV { FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : @@ -1504,7 +1545,6 @@ static const ImVec2 MARKER_FILL_UP[3] = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1 static const ImVec2 MARKER_FILL_DOWN[3] = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f)}; static const ImVec2 MARKER_FILL_LEFT[3] = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)}; static const ImVec2 MARKER_FILL_RIGHT[3] = {ImVec2(1,0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)}; - static const ImVec2 MARKER_LINE_CIRCLE[20] = { ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f), @@ -1566,6 +1606,309 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool } } + +//----------------------------------------------------------------------------- +// [SECTION] Vectors +//----------------------------------------------------------------------------- + +template +struct RendererVectorFillColorCodedNormalized : RendererBase { + RendererVectorFillColorCodedNormalized(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size), + Col(col), + MinMag(min_mag), + MaxMag(max_mag) + { } + 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 { + + ImPlotQuiver vec = Getter(prim); + + ImVec2 p; + p.x = this->Transformer.Tx(vec.x); + p.y = this->Transformer.Ty(vec.y); + + float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v + float cos_theta = ImCos(theta); + float sin_theta = ImSin(theta); + double mag = (double)ImSqrt(vec.mag2); + + double t = ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); + + ImVec4 color = SampleColormap((float)t); + + 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 && mag > 1e-6) { + for (int i = 0; i < Count; i++) { + //get the rotation of this primitive and apply it to the marker points + float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; + float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = ImColor(color); + 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 float Size; + const ImU32 Col; + mutable ImVec2 UV; + double MinMag; + double MaxMag; +}; + + +template +struct RendererVectorFillColorCodedScaled : RendererBase { + RendererVectorFillColorCodedScaled(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size), + Col(col), + MinMag(min_mag), + MaxMag(max_mag) + { } + 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 { + + ImPlotQuiver vec = Getter(prim); + + ImVec2 p; + p.x = this->Transformer.Tx(vec.x); + p.y = this->Transformer.Ty(vec.y); + + float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v + float cos_theta = ImCos(theta); + float sin_theta = ImSin(theta); + double mag = (double)ImSqrt(vec.mag2); + double size_mag = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); + double t = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); + ImVec4 color = SampleColormap((float)t); + + 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++) { + //get the rotation of this primitive and apply it to the marker points + float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; + float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_mag; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size * size_mag; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = ImColor(color); + 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 float Size; + const ImU32 Col; + mutable ImVec2 UV; + double MinMag = 0.0; + double MaxMag = 1.0; +}; + + +template +struct RendererVectorFillNormalized : RendererBase { + RendererVectorFillNormalized(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size), + Col(col), + MinMag(min_mag), + MaxMag(max_mag) + { } + 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 { + + ImPlotQuiver vec = Getter(prim); + + ImVec2 p; + p.x = this->Transformer.Tx(vec.x); + p.y = this->Transformer.Ty(vec.y); + + float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v + float cos_theta = ImCos(theta); + float sin_theta = ImSin(theta); + double mag = (double)ImSqrt(vec.mag2); + + + + + 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 && mag > 1e-6) { + for (int i = 0; i < Count; i++) { + //get the rotation of this primitive and apply it to the marker points + float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; + float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = ImColor(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 float Size; + const ImU32 Col; + mutable ImVec2 UV; + double MinMag; + double MaxMag; +}; + + +template +struct RendererVectorFill : RendererBase { + RendererVectorFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size), + Col(col), + MinMag(min_mag), + MaxMag(max_mag) + { } + 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 { + + ImPlotQuiver vec = Getter(prim); + + ImVec2 p; + p.x = this->Transformer.Tx(vec.x); + p.y = this->Transformer.Ty(vec.y); + + float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v + float cos_theta = ImCos(theta); + float sin_theta = ImSin(theta); + double mag = (double)ImSqrt(vec.mag2); + double size_mag = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); + + + + + 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 && mag > 1e-6) { + for (int i = 0; i < Count; i++) { + //get the rotation of this primitive and apply it to the marker points + float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; + float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_mag; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size* size_mag; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = ImColor(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 float Size; + const ImU32 Col; + mutable ImVec2 UV; + double MinMag; + double MaxMag; +}; + + + +static const ImVec2 MARKER_FILL_ARROW[7] = { + ImVec2(0.5, 0), // tip of arrowhead + ImVec2(0.0, -0.3), // left side of arrowhead + ImVec2(0.0, -0.1), // left inner point where arrowhead meets shaft + ImVec2(-0.5, -0.1), // top left of shaft + ImVec2(-0.5, 0.1), // top right of shaft + ImVec2(0.0, 0.1), // right inner point where arrowhead meets shaft + ImVec2(0.0, 0.3) // right side of arrowhead +}; + + + +template +void RenderVectors(const _Getter& getter, float size, double scaleMin, double scaleMax, bool color_coded, bool normalized, ImU32 color = 0) { + + //to do + //Size of vector related to mag if flag set + //Color of fill of vector related to mag if flag set + //Tidy the code and see if it is worth modifying the transform2 to set the rotation + //Make the line draw too + if (color_coded && normalized) { + + RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); + } + else if (color_coded && !normalized) { + + RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); + } + else if (!color_coded && normalized) { + + RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); + } + else { + + RenderPrimitives1(getter,MARKER_FILL_ARROW,7,size,color,scaleMin,scaleMax); + } + +} + //----------------------------------------------------------------------------- // [SECTION] PlotLine //----------------------------------------------------------------------------- @@ -1691,6 +2034,48 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in return PlotScatterEx(label_id, getter, flags); } + +//----------------------------------------------------------------------------- +// [SECTION] PlotQuiver (Vector Field) +//----------------------------------------------------------------------------- +template +void PlotQuiverEx(const char* label_id, const Getter& getter, const double scaleMin, const double scaleMax, ImPlotQuiverFlags flags){ + if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { + if (getter.Count <= 0) { + EndItem(); + return; + } + const ImPlotNextItemData& s = GetItemData(); + + + const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); + + RenderVectors(getter, s.QuiverSize, scaleMin, scaleMax, ImHasFlag(flags, ImPlotQuiverFlags_Colored),ImHasFlag(flags, ImPlotQuiverFlags_Normalize) , col_fill); + EndItem(); + } + +} + +template +void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags, int offset, int stride) { + GetterXYUV,IndexerIdx,IndexerIdx,IndexerIdx> getter( + IndexerIdx(xs,count,offset,stride), + IndexerIdx(ys,count,offset,stride), + IndexerIdx(us,count,offset,stride), + IndexerIdx(vs,count,offset,stride), + count); + return PlotQuiverEx(label_id, getter, scaleMin, scaleMax, flags); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API void PlotQuiver(const char* label_id, const T* xs, const T* ys, const T* us, const T* vs, int count, const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags, int offset, int stride); +CALL_INSTANTIATE_FOR_NUMERIC_TYPES() +#undef INSTANTIATE_MACRO + + + + + //----------------------------------------------------------------------------- // [SECTION] PlotStairs //----------------------------------------------------------------------------- From e124378972b49e230523cf24c062b0e8f188d21d Mon Sep 17 00:00:00 2001 From: Raphael Nogueira Rezende Laroca Pinto <61098580+katyushapolye@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:48:23 -0300 Subject: [PATCH 02/13] Just version number adjustment I forgot. --- implot.cpp | 41 ++++++++++++++++++++++------------------- implot.h | 2 +- implot_demo.cpp | 4 ++-- implot_internal.h | 2 +- implot_items.cpp | 2 +- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/implot.cpp b/implot.cpp index 5550d031..5910e93d 100644 --- a/implot.cpp +++ b/implot.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.17 +// ImPlot v0.18 WIP /* @@ -4102,31 +4102,34 @@ bool DragRect(int n_id, double* x_min, double* y_min, double* x_max, double* y_m bool modified = false; bool clicked = false, hovered = false, held = false; - ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); - ImGui::KeepAliveID(id); - if (input) { - // middle point - clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); - if (out_clicked) *out_clicked = clicked; - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - } + const bool is_movable = *x_min != *x_max || *y_min != *y_max; + if (is_movable) { + ImGui::KeepAliveID(id); + if (input) { + // middle point + ImRect b_rect(pc.x-DRAG_GRAB_HALF_SIZE,pc.y-DRAG_GRAB_HALF_SIZE,pc.x+DRAG_GRAB_HALF_SIZE,pc.y+DRAG_GRAB_HALF_SIZE); + clicked = ImGui::ButtonBehavior(b_rect,id,&hovered,&held); + if (out_clicked) *out_clicked = clicked; + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + } - if ((hovered || held) && show_curs) - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); - if (held && ImGui::IsMouseDragging(0)) { - for (int i = 0; i < 4; ++i) { - ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); - *y[i] = pp.y; - *x[i] = pp.x; + if ((hovered || held) && show_curs) + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + if (held && ImGui::IsMouseDragging(0)) { + for (int i = 0; i < 4; ++i) { + ImPlotPoint pp = PixelsToPlot(p[i] + ImGui::GetIO().MouseDelta,IMPLOT_AUTO,IMPLOT_AUTO); + *y[i] = pp.y; + *x[i] = pp.x; + } + modified = true; } - modified = true; } for (int i = 0; i < 4; ++i) { // points - b_rect = ImRect(p[i].x-DRAG_GRAB_HALF_SIZE,p[i].y-DRAG_GRAB_HALF_SIZE,p[i].x+DRAG_GRAB_HALF_SIZE,p[i].y+DRAG_GRAB_HALF_SIZE); + ImRect b_rect(p[i].x - DRAG_GRAB_HALF_SIZE, p[i].y - DRAG_GRAB_HALF_SIZE, p[i].x + DRAG_GRAB_HALF_SIZE, p[i].y + DRAG_GRAB_HALF_SIZE); ImGuiID p_id = id + i + 1; ImGui::KeepAliveID(p_id); if (input) { diff --git a/implot.h b/implot.h index 3b507d4a..5cf7130d 100644 --- a/implot.h +++ b/implot.h @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.17 +// ImPlot v0.18 WIP // Table of Contents: // diff --git a/implot_demo.cpp b/implot_demo.cpp index 7fae8151..97e317b1 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.17 +// ImPlot v0.18 WIP // We define this so that the demo does not accidentally use deprecated API #ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS @@ -748,7 +748,7 @@ void Demo_Heatmaps() { ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); - + static ImPlotHeatmapFlags hm_flags = 0; ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); diff --git a/implot_internal.h b/implot_internal.h index 88494a0a..08852f21 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.17 +// ImPlot v0.18 WIP // You may use this file to debug, understand or extend ImPlot features but we // don't provide any guarantee of forward compatibility! diff --git a/implot_items.cpp b/implot_items.cpp index 0d2ad14c..7fb56ff3 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// ImPlot v0.17 +// ImPlot v0.18 WIP #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS From 89ffd7b9ec3b26d1c85fd7dcef26d80f9f840d15 Mon Sep 17 00:00:00 2001 From: Raphael Nogueira Rezende Laroca Pinto <61098580+katyushapolye@users.noreply.github.com> Date: Thu, 18 Dec 2025 11:50:23 -0300 Subject: [PATCH 03/13] Again, numbers i forgot. --- implot.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implot.h b/implot.h index 5cf7130d..a4a0afb2 100644 --- a/implot.h +++ b/implot.h @@ -62,9 +62,9 @@ #endif // ImPlot version string. -#define IMPLOT_VERSION "0.17" +#define IMPLOT_VERSION "0.18 WIP" // ImPlot version integer encoded as XYYZZ (X=major, YY=minor, ZZ=patch). -#define IMPLOT_VERSION_NUM 1700 +#define IMPLOT_VERSION_NUM 1800 // Indicates variable should deduced automatically. #define IMPLOT_AUTO -1 // Special color used to indicate that a color should be deduced automatically. From 3203c0c22a0d5e63a2a1e7fa1e93709f218dd80a Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 07:12:32 +0100 Subject: [PATCH 04/13] refactor: remove animation from quiver plot demo --- implot_demo.cpp | 81 ++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 97e317b1..c5c610cb 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -78,6 +78,16 @@ void StyleSeaborn(); namespace ImPlot { +static void HelpMarker(const char* desc) { + ImGui::TextDisabled("(?)"); + if (ImGui::BeginItemTooltip()) { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + template inline T RandomRange(T min, T max) { T scale = rand() / (T) RAND_MAX; @@ -419,78 +429,59 @@ void Demo_ScatterPlots() { void Demo_QuiverPlots(){ static float xs[100], ys[100], us[100], vs[100]; - static bool animate = false; - static float animation_speed = 1.0f; - - float time = animate ? ImGui::GetTime() * animation_speed : 0.0f; - for (int i = 0; i < 10; ++i) { for(int j = 0; j < 10; ++j){ int idx = i*10 + j; xs[idx] = ((float)j * 0.1f) - 0.5; ys[idx] = ((float)i * 0.1f) - 0.5; - - if (animate) { - float k = 2.0f * 3.14159f; - float decay = expf(-0.1f * time); - us[idx] = decay * (sinf(k * xs[idx]) * cosf(k * ys[idx]) * cosf(time) - - cosf(k * xs[idx]) * sinf(k * ys[idx]) * sinf(time)); - vs[idx] = decay * (-cosf(k * xs[idx]) * sinf(k * ys[idx]) * cosf(time) - - sinf(k * xs[idx]) * cosf(k * ys[idx]) * sinf(time)); - } else { - // Taylor-Green vortex - float k = 2.0f * 3.14159f; - us[idx] = sinf(k * xs[idx]) * cosf(k * ys[idx]); - vs[idx] = -cosf(k * xs[idx]) * sinf(k * ys[idx]); - } + + // Taylor-Green vortex + float k = 2.0f * 3.14159f; + us[idx] = sinf(k * xs[idx]) * cosf(k * ys[idx]); + vs[idx] = -cosf(k * xs[idx]) * sinf(k * ys[idx]); } } - + static float scale_min = 0.00f; static float scale_max = 1.0f; static float baseSize = 12.0f; static ImPlotColormap map = ImPlotColormap_Viridis; - - if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { + + if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225,0), map)) { map = (map + 1) % ImPlot::GetColormapCount(); - BustColorCache("##Heatmap1"); - BustColorCache("##Heatmap2"); } ImGui::SameLine(); ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); - + ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); if (scale_max <= scale_min + 0.01f) { scale_max = scale_min + 0.01f; } - + ImGui::SetNextItemWidth(225); - ImGui::DragFloat("Base Size",&baseSize,0.1f,0,100); - - // Animation controls - ImGui::Checkbox("Time Dependant Function", &animate); - if (animate) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(150); - ImGui::DragFloat("Speed", &animation_speed, 0.1f, 0.1f, 10.0f); - } - + ImGui::DragFloat("Base Size", &baseSize, 0.1f, 0, 100); + static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; - ImGui::CheckboxFlags("Normalized", (unsigned int*)&qv_flags, ImPlotQuiverFlags_Normalize); - ImGui::CheckboxFlags("Color Coded", (unsigned int*)&qv_flags, ImPlotQuiverFlags_Colored); - + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Normalize); + ImGui::SameLine(); + HelpMarker("All arrows will be normalized to the same length"); + + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Colored); + ImGui::SameLine(); + HelpMarker("Arrow colors will be mapped to their magnitudes"); + ImPlot::PushColormap(map); if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { - ImPlot::SetupAxisTicks(ImAxis_X1,-0.5, 0.5, 11); - ImPlot::SetupAxisTicks(ImAxis_Y1,-0.5, 0.5, 11); - ImPlot::SetNextQuiverStyle(baseSize,ImPlot::GetColormapColor(1)); - ImPlot::SetupAxes("x","y"); - ImPlot::PlotQuiver("Magnitude", xs, ys,us,vs,100,scale_min,scale_max, qv_flags); + ImPlot::SetupAxisTicks(ImAxis_X1, -0.5, 0.5, 11); + ImPlot::SetupAxisTicks(ImAxis_Y1, -0.5, 0.5, 11); + ImPlot::SetNextQuiverStyle(baseSize, ImPlot::GetColormapColor(1)); + ImPlot::SetupAxes("x", "y"); + ImPlot::PlotQuiver("Magnitude", xs, ys, us, vs, 100, scale_min, scale_max, qv_flags); ImPlot::EndPlot(); } ImGui::SameLine(); - ImPlot::ColormapScale("##Magnitude Scale", scale_min, scale_max); + ImPlot::ColormapScale("##QuiverScale", scale_min, scale_max); ImPlot::PopColormap(); } From 64e88886b0b99e97d144fa0433ff4c92c07138cf Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 07:46:26 +0100 Subject: [PATCH 05/13] refactor: remove ImPlotCol_QuiverFill --- implot.h | 1 - 1 file changed, 1 deletion(-) diff --git a/implot.h b/implot.h index a4a0afb2..a7cee70e 100644 --- a/implot.h +++ b/implot.h @@ -359,7 +359,6 @@ enum ImPlotCol_ { ImPlotCol_MarkerOutline, // marker outline color (defaults to the current line color) ImPlotCol_MarkerFill, // marker fill color (defaults to the current line color) ImPlotCol_ErrorBar, // error bar color (defaults to ImGuiCol_Text) - ImPlotCol_QuiverFill, // plot styling colors ImPlotCol_FrameBg, // plot frame background color (defaults to ImGuiCol_FrameBg) ImPlotCol_PlotBg, // plot area background color (defaults to ImGuiCol_WindowBg) From 1591a74edae9d075a9b8af21819e8e77cd25b5f3 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 08:05:16 +0100 Subject: [PATCH 06/13] refactor: merge the four quiver renderers into one --- implot_items.cpp | 286 ++++++----------------------------------------- 1 file changed, 36 insertions(+), 250 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 7fb56ff3..5c3790bd 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1545,6 +1545,16 @@ static const ImVec2 MARKER_FILL_UP[3] = {ImVec2(SQRT_3_2,0.5f),ImVec2(0,-1 static const ImVec2 MARKER_FILL_DOWN[3] = {ImVec2(SQRT_3_2,-0.5f),ImVec2(0,1),ImVec2(-SQRT_3_2,-0.5f)}; static const ImVec2 MARKER_FILL_LEFT[3] = {ImVec2(-1,0), ImVec2(0.5, SQRT_3_2), ImVec2(0.5, -SQRT_3_2)}; static const ImVec2 MARKER_FILL_RIGHT[3] = {ImVec2(1,0), ImVec2(-0.5, SQRT_3_2), ImVec2(-0.5, -SQRT_3_2)}; +static const ImVec2 MARKER_FILL_ARROW[7] = { + ImVec2(0.5, 0), + ImVec2(0.0, -0.3), + ImVec2(0.0, -0.1), + ImVec2(-0.5, -0.1), + ImVec2(-0.5, 0.1), + ImVec2(0.0, 0.1), + ImVec2(0.0, 0.3) +}; + static const ImVec2 MARKER_LINE_CIRCLE[20] = { ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f), @@ -1611,202 +1621,9 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool // [SECTION] Vectors //----------------------------------------------------------------------------- -template -struct RendererVectorFillColorCodedNormalized : RendererBase { - RendererVectorFillColorCodedNormalized(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : - RendererBase(getter.Count, (count-2)*3, count), - Getter(getter), - Marker(marker), - Count(count), - Size(size), - Col(col), - MinMag(min_mag), - MaxMag(max_mag) - { } - 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 { - - ImPlotQuiver vec = Getter(prim); - - ImVec2 p; - p.x = this->Transformer.Tx(vec.x); - p.y = this->Transformer.Ty(vec.y); - - float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v - float cos_theta = ImCos(theta); - float sin_theta = ImSin(theta); - double mag = (double)ImSqrt(vec.mag2); - - double t = ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); - - ImVec4 color = SampleColormap((float)t); - - 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 && mag > 1e-6) { - for (int i = 0; i < Count; i++) { - //get the rotation of this primitive and apply it to the marker points - float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; - float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; - draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size; - draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size; - draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = ImColor(color); - 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 float Size; - const ImU32 Col; - mutable ImVec2 UV; - double MinMag; - double MaxMag; -}; - - -template -struct RendererVectorFillColorCodedScaled : RendererBase { - RendererVectorFillColorCodedScaled(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : - RendererBase(getter.Count, (count-2)*3, count), - Getter(getter), - Marker(marker), - Count(count), - Size(size), - Col(col), - MinMag(min_mag), - MaxMag(max_mag) - { } - 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 { - - ImPlotQuiver vec = Getter(prim); - - ImVec2 p; - p.x = this->Transformer.Tx(vec.x); - p.y = this->Transformer.Ty(vec.y); - - float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v - float cos_theta = ImCos(theta); - float sin_theta = ImSin(theta); - double mag = (double)ImSqrt(vec.mag2); - double size_mag = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); - double t = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); - ImVec4 color = SampleColormap((float)t); - - 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++) { - //get the rotation of this primitive and apply it to the marker points - float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; - float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; - draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_mag; - draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size * size_mag; - draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = ImColor(color); - 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 float Size; - const ImU32 Col; - mutable ImVec2 UV; - double MinMag = 0.0; - double MaxMag = 1.0; -}; - - -template -struct RendererVectorFillNormalized : RendererBase { - RendererVectorFillNormalized(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : - RendererBase(getter.Count, (count-2)*3, count), - Getter(getter), - Marker(marker), - Count(count), - Size(size), - Col(col), - MinMag(min_mag), - MaxMag(max_mag) - { } - 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 { - - ImPlotQuiver vec = Getter(prim); - - ImVec2 p; - p.x = this->Transformer.Tx(vec.x); - p.y = this->Transformer.Ty(vec.y); - - float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v - float cos_theta = ImCos(theta); - float sin_theta = ImSin(theta); - double mag = (double)ImSqrt(vec.mag2); - - - - - 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 && mag > 1e-6) { - for (int i = 0; i < Count; i++) { - //get the rotation of this primitive and apply it to the marker points - float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; - float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; - draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size; - draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size; - draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = ImColor(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 float Size; - const ImU32 Col; - mutable ImVec2 UV; - double MinMag; - double MaxMag; -}; - - template struct RendererVectorFill : RendererBase { - RendererVectorFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag) : + RendererVectorFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag, bool color_coded, bool normalized) : RendererBase(getter.Count, (count-2)*3, count), Getter(getter), Marker(marker), @@ -1814,37 +1631,44 @@ struct RendererVectorFill : RendererBase { Size(size), Col(col), MinMag(min_mag), - MaxMag(max_mag) + MaxMag(max_mag), + ColorCoded(color_coded), + Normalized(normalized) { } 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 { - - ImPlotQuiver vec = Getter(prim); + ImPlotQuiver vec = Getter(prim); ImVec2 p; p.x = this->Transformer.Tx(vec.x); p.y = this->Transformer.Ty(vec.y); - float theta = ImAtan2(-vec.v, vec.u); //in radians, correction for the draw axis on the v + float theta = ImAtan2(-vec.v, vec.u); float cos_theta = ImCos(theta); float sin_theta = ImSin(theta); double mag = (double)ImSqrt(vec.mag2); - double size_mag = ImClamp(ImRemap01(mag, MinMag, MaxMag),0.0,1.0); - + double size_scale = Normalized ? 1.0 : ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); + ImU32 color_final = Col; + if (ColorCoded) { + double t = ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); + color_final = ImColor(SampleColormap((float)t)); + } + bool should_render = (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && + p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y); + if (Normalized) should_render = should_render && (mag > 1e-6); - 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 && mag > 1e-6) { + if (should_render) { for (int i = 0; i < Count; i++) { - //get the rotation of this primitive and apply it to the marker points float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; - draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_mag; - draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size* size_mag; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_scale; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size * size_scale; draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = ImColor(Col); + draw_list._VtxWritePtr[0].col = color_final; draw_list._VtxWritePtr++; } for (int i = 2; i < Count; i++) { @@ -1866,49 +1690,10 @@ struct RendererVectorFill : RendererBase { mutable ImVec2 UV; double MinMag; double MaxMag; + bool ColorCoded; + bool Normalized; }; - - -static const ImVec2 MARKER_FILL_ARROW[7] = { - ImVec2(0.5, 0), // tip of arrowhead - ImVec2(0.0, -0.3), // left side of arrowhead - ImVec2(0.0, -0.1), // left inner point where arrowhead meets shaft - ImVec2(-0.5, -0.1), // top left of shaft - ImVec2(-0.5, 0.1), // top right of shaft - ImVec2(0.0, 0.1), // right inner point where arrowhead meets shaft - ImVec2(0.0, 0.3) // right side of arrowhead -}; - - - -template -void RenderVectors(const _Getter& getter, float size, double scaleMin, double scaleMax, bool color_coded, bool normalized, ImU32 color = 0) { - - //to do - //Size of vector related to mag if flag set - //Color of fill of vector related to mag if flag set - //Tidy the code and see if it is worth modifying the transform2 to set the rotation - //Make the line draw too - if (color_coded && normalized) { - - RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); - } - else if (color_coded && !normalized) { - - RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); - } - else if (!color_coded && normalized) { - - RenderPrimitives1(getter,MARKER_FILL_ARROW, 7,size,color,scaleMin,scaleMax); - } - else { - - RenderPrimitives1(getter,MARKER_FILL_ARROW,7,size,color,scaleMin,scaleMax); - } - -} - //----------------------------------------------------------------------------- // [SECTION] PlotLine //----------------------------------------------------------------------------- @@ -2038,22 +1823,23 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in //----------------------------------------------------------------------------- // [SECTION] PlotQuiver (Vector Field) //----------------------------------------------------------------------------- + template void PlotQuiverEx(const char* label_id, const Getter& getter, const double scaleMin, const double scaleMax, ImPlotQuiverFlags flags){ - if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { + if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { if (getter.Count <= 0) { EndItem(); return; } const ImPlotNextItemData& s = GetItemData(); - const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); + const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_Colored); + const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_Normalize); + RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, scaleMin, scaleMax, color_coded, normalized); - RenderVectors(getter, s.QuiverSize, scaleMin, scaleMax, ImHasFlag(flags, ImPlotQuiverFlags_Colored),ImHasFlag(flags, ImPlotQuiverFlags_Normalize) , col_fill); EndItem(); } - } template From 497b9053bbabd59c95e4153d065877724de7d370 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 08:18:49 +0100 Subject: [PATCH 07/13] refactor: rename ImPlotQuiver to ImPlotPoint4D --- implot.h | 20 -------------------- implot_items.cpp | 36 +++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/implot.h b/implot.h index a7cee70e..40021106 100644 --- a/implot.h +++ b/implot.h @@ -494,26 +494,6 @@ struct ImPlotPoint { }; IM_MSVC_RUNTIME_CHECKS_RESTORE -IM_MSVC_RUNTIME_CHECKS_OFF -// Implementation of a 2D plot vector with double precision. -struct ImPlotQuiver { - double x, y; - double u, v; - double mag2; - IMPLOT_API constexpr ImPlotQuiver() : x(0.0), y(0.0), u(0.0), v(0.0), mag2(0.0) { } - IMPLOT_API constexpr ImPlotQuiver(double _x, double _y, double _u, double _v) : x(_x), y(_y), u(_u), v(_v), mag2((_u*_u + _v*_v)) { } - IMPLOT_API constexpr ImPlotQuiver(double _x, double _y, double _u, double _v, double _mag2) : x(_x), y(_y), u(_u), v(_v), mag2(_mag2) { } // ADD THIS - IMPLOT_API constexpr ImPlotQuiver(const ImVec2& p, const ImVec2& q) : x((double)p.x), y((double)p.y), u((double)q.x), v((double)q.y), mag2((q.x*q.x + q.y*q.y)) { } - IMPLOT_API constexpr ImPlotQuiver(const ImVec4& r) : x((double)r.x), y((double)r.y), u((double)r.z), v((double)r.w), mag2((r.z*r.z + r.w*r.w)) { } - IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3 || idx == 4); return ((double*)(void*)(char*)this)[idx]; } - IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3 || idx == 4); return ((const double*)(const void*)(const char*)this)[idx]; } -#ifdef IMPLOT_POINT_CLASS_EXTRA - IMPLOT_POINT_CLASS_EXTRA - -#endif -}; -IM_MSVC_RUNTIME_CHECKS_RESTORE - // Range defined by a min/max value. struct ImPlotRange { double Min, Max; diff --git a/implot_items.cpp b/implot_items.cpp index 5c3790bd..b98da709 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -577,19 +577,29 @@ struct GetterXY { const int Count; }; +// Implementation of a 4D point with double precision. +struct ImPlotPoint4D { + double x, y, z, w; + IMPLOT_API constexpr ImPlotPoint4D() : x(0.0), y(0.0), z(0.0), w(0.0) { } + IMPLOT_API constexpr ImPlotPoint4D(double _x, double _y, double _z, double _w) : x(_x), y(_y), z(_z), w(_w) { } + IMPLOT_API constexpr ImPlotPoint4D(const ImVec2& p, const ImVec2& q) : x((double)p.x), y((double)p.y), z((double)q.x), w((double)q.y) { } + IMPLOT_API constexpr ImPlotPoint4D(const ImVec4& r) : x((double)r.x), y((double)r.y), z((double)r.z), w((double)r.w) { } + IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((double*)(void*)(char*)this)[idx]; } + IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((const double*)(const void*)(const char*)this)[idx]; } +}; + // Functor for returning a 2D vector in 2D space (For Quiver plots) template struct GetterXYUV { - GetterXYUV(_IndexerX x, _IndexerY y, _IndexerU u, _IndexerV v, int count) + GetterXYUV(_IndexerX x, _IndexerY y, _IndexerU u, _IndexerV v, int count) : IndxerX(x), IndxerY(y), IndxerU(u), IndxerV(v), Count(count) { } - - template IMPLOT_INLINE ImPlotQuiver operator()(I idx) const { - double u_val = IndxerU(idx); - double v_val = IndxerV(idx); - double mag = sqrt(u_val*u_val + v_val*v_val); - return ImPlotQuiver(IndxerX(idx), IndxerY(idx), u_val, v_val, mag); - } - + + template IMPLOT_INLINE ImPlotPoint4D operator()(I idx) const { + double z_val = IndxerU(idx); + double w_val = IndxerV(idx); + return ImPlotPoint4D(IndxerX(idx), IndxerY(idx), z_val, w_val); + } + const _IndexerX IndxerX; const _IndexerY IndxerY; const _IndexerU IndxerU; @@ -741,7 +751,7 @@ struct FitterVector { FitterVector(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { - ImPlotQuiver p = Getter(i); + ImPlotPoint4D p = Getter(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } @@ -1639,16 +1649,16 @@ struct RendererVectorFill : RendererBase { UV = draw_list._Data->TexUvWhitePixel; } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { - ImPlotQuiver vec = Getter(prim); + ImPlotPoint4D vec = Getter(prim); ImVec2 p; p.x = this->Transformer.Tx(vec.x); p.y = this->Transformer.Ty(vec.y); - float theta = ImAtan2(-vec.v, vec.u); + float theta = ImAtan2(-vec.w, vec.z); float cos_theta = ImCos(theta); float sin_theta = ImSin(theta); - double mag = (double)ImSqrt(vec.mag2); + double mag = (double)ImSqrt(vec.z*vec.z + vec.w*vec.w); double size_scale = Normalized ? 1.0 : ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); ImU32 color_final = Col; From 4e5e3088debf6273e2723e29a081d7f293d7119b Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 09:15:04 +0100 Subject: [PATCH 08/13] feat: automatic quiver plot coloring when min_mag=max_mag --- implot.h | 4 ++-- implot_demo.cpp | 24 ++++++++++++++---------- implot_items.cpp | 28 +++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/implot.h b/implot.h index 40021106..47d787ed 100644 --- a/implot.h +++ b/implot.h @@ -877,8 +877,8 @@ 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 standard 2D Vector Field arrow plot. It has a direction and magnitude. -IMPLOT_TMP void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count,const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags = 0, int offset= 0, int stride=sizeof(T)); +// Plots a standard 2D quiver plot. The direction and magnitude of the arrows are determined by #us and #vs. Set #mag_min and #mag_max to specify a range of magnitudes to map to the arrow colors. Set mag_min = mag_max = 0 to use the full colormap range. +IMPLOT_TMP void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min=0, double mag_max=0, ImPlotQuiverFlags flags=0, int offset=0, int stride=sizeof(T)); // 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)); diff --git a/implot_demo.cpp b/implot_demo.cpp index c5c610cb..c24b9ed7 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -442,9 +442,9 @@ void Demo_QuiverPlots(){ } } - static float scale_min = 0.00f; - static float scale_max = 1.0f; - static float baseSize = 12.0f; + static float mag_min = 0.00f; + static float mag_max = 1.0f; + static float base_size = 12.0f; static ImPlotColormap map = ImPlotColormap_Viridis; if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225,0), map)) { @@ -454,13 +454,17 @@ void Demo_QuiverPlots(){ ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); - ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); - if (scale_max <= scale_min + 0.01f) { - scale_max = scale_min + 0.01f; + ImGui::DragFloatRange2("Min / Max Magnitude", &mag_min, &mag_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); + if (mag_max < mag_min) { + mag_max = mag_min; } + ImGui::SameLine(); + HelpMarker("Minumum and maximum magnitudes for color mapping"); ImGui::SetNextItemWidth(225); - ImGui::DragFloat("Base Size", &baseSize, 0.1f, 0, 100); + ImGui::DragFloat("Base Size", &base_size, 0.1f, 0, 100); + ImGui::SameLine(); + HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Normalize); @@ -475,13 +479,13 @@ void Demo_QuiverPlots(){ if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { ImPlot::SetupAxisTicks(ImAxis_X1, -0.5, 0.5, 11); ImPlot::SetupAxisTicks(ImAxis_Y1, -0.5, 0.5, 11); - ImPlot::SetNextQuiverStyle(baseSize, ImPlot::GetColormapColor(1)); + ImPlot::SetNextQuiverStyle(base_size, ImPlot::GetColormapColor(1)); ImPlot::SetupAxes("x", "y"); - ImPlot::PlotQuiver("Magnitude", xs, ys, us, vs, 100, scale_min, scale_max, qv_flags); + ImPlot::PlotQuiver("Magnitude", xs, ys, us, vs, 100, mag_min, mag_max, qv_flags); ImPlot::EndPlot(); } ImGui::SameLine(); - ImPlot::ColormapScale("##QuiverScale", scale_min, scale_max); + ImPlot::ColormapScale("##QuiverScale", mag_min, mag_max); ImPlot::PopColormap(); } diff --git a/implot_items.cpp b/implot_items.cpp index b98da709..56155e79 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1835,7 +1835,7 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in //----------------------------------------------------------------------------- template -void PlotQuiverEx(const char* label_id, const Getter& getter, const double scaleMin, const double scaleMax, ImPlotQuiverFlags flags){ +void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_min, const double mag_max, ImPlotQuiverFlags flags){ if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { if (getter.Count <= 0) { EndItem(); @@ -1846,25 +1846,43 @@ void PlotQuiverEx(const char* label_id, const Getter& getter, const double scale const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_Colored); const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_Normalize); - RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, scaleMin, scaleMax, color_coded, normalized); + + double final_mag_min = mag_min; + double final_mag_max = mag_max; + + if (color_coded && mag_min == mag_max) { + final_mag_min = INFINITY; + final_mag_max = -INFINITY; + for (int i = 0; i < getter.Count; ++i) { + ImPlotPoint4D p = getter(i); + double mag = ImSqrt(p.z*p.z + p.w*p.w); + if (mag < final_mag_min) final_mag_min = mag; + if (mag > final_mag_max) final_mag_max = mag; + } + if (final_mag_min == final_mag_max) { + final_mag_max = final_mag_min + 1.0; + } + } + + RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, final_mag_min, final_mag_max, color_coded, normalized); EndItem(); } } template -void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags, int offset, int stride) { +void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride) { GetterXYUV,IndexerIdx,IndexerIdx,IndexerIdx> getter( IndexerIdx(xs,count,offset,stride), IndexerIdx(ys,count,offset,stride), IndexerIdx(us,count,offset,stride), IndexerIdx(vs,count,offset,stride), count); - return PlotQuiverEx(label_id, getter, scaleMin, scaleMax, flags); + return PlotQuiverEx(label_id, getter, mag_min, mag_max, flags); } #define INSTANTIATE_MACRO(T) \ - template IMPLOT_API void PlotQuiver(const char* label_id, const T* xs, const T* ys, const T* us, const T* vs, int count, const T scaleMin, const T scaleMax, ImPlotQuiverFlags flags, int offset, int stride); + template IMPLOT_API void PlotQuiver(const char* label_id, const T* xs, const T* ys, const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From 3fac0ba80441664592925b01e214752aba917839 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 09:20:25 +0100 Subject: [PATCH 09/13] refactor: rename ImPlotQuiverFlags_Normalize to ImPlotQuiverFlags_FixedSize --- implot.h | 2 +- implot_demo.cpp | 4 ++-- implot_items.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/implot.h b/implot.h index 47d787ed..f9d2cb28 100644 --- a/implot.h +++ b/implot.h @@ -251,7 +251,7 @@ enum ImPlotScatterFlags_ { enum ImPlotQuiverFlags_ { ImPlotQuiverFlags_None = 0, // default ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped - ImPlotQuiverFlags_Normalize = 1 << 11, // all arrows will be normalized to the same length + ImPlotQuiverFlags_FixedSize = 1 << 11, // all arrows will have the same size ImPlotQuiverFlags_Colored = 1 << 12 // arrow colors will be mapped to their magnitudes }; diff --git a/implot_demo.cpp b/implot_demo.cpp index c24b9ed7..28bca015 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -467,9 +467,9 @@ void Demo_QuiverPlots(){ HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; - CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Normalize); + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_FixedSize); ImGui::SameLine(); - HelpMarker("All arrows will be normalized to the same length"); + HelpMarker("All arrows will have the length set to base size"); CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Colored); ImGui::SameLine(); diff --git a/implot_items.cpp b/implot_items.cpp index 56155e79..ef69eddf 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1845,7 +1845,7 @@ void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_m const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_Colored); - const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_Normalize); + const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_FixedSize); double final_mag_min = mag_min; double final_mag_max = mag_max; From f9b68269832ddf885713aa179949ba26937ee75b Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 09:25:31 +0100 Subject: [PATCH 10/13] feat: implement ImPlotQuiverFlags_NoClip --- implot_demo.cpp | 5 +++++ implot_items.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/implot_demo.cpp b/implot_demo.cpp index 28bca015..0b908a54 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -467,6 +467,11 @@ void Demo_QuiverPlots(){ HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; + + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_NoClip); + ImGui::SameLine(); + HelpMarker("Arrows on the edge of the plot will not be clipped"); + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_FixedSize); ImGui::SameLine(); HelpMarker("All arrows will have the length set to base size"); diff --git a/implot_items.cpp b/implot_items.cpp index ef69eddf..4af9d716 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1864,6 +1864,10 @@ void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_m } } + if (ImHasFlag(flags, ImPlotQuiverFlags_NoClip)) { + PopPlotClipRect(); + PushPlotClipRect(s.QuiverSize); + } RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, final_mag_min, final_mag_max, color_coded, normalized); EndItem(); From 03d95bc6569d788fa9dcd447c82b5dc0c07b09f0 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 09:31:50 +0100 Subject: [PATCH 11/13] refactor: rename ImPlotQuiverFlags_Colored to ImPlotQuiverFlags_ColorByMagnitude --- implot.h | 8 ++++---- implot_demo.cpp | 6 +++--- implot_items.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/implot.h b/implot.h index f9d2cb28..d82e2b9b 100644 --- a/implot.h +++ b/implot.h @@ -249,10 +249,10 @@ enum ImPlotScatterFlags_ { // Flgs for PlotQuiver enum ImPlotQuiverFlags_ { - ImPlotQuiverFlags_None = 0, // default - ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped - ImPlotQuiverFlags_FixedSize = 1 << 11, // all arrows will have the same size - ImPlotQuiverFlags_Colored = 1 << 12 // arrow colors will be mapped to their magnitudes + ImPlotQuiverFlags_None = 0, // default + ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped + ImPlotQuiverFlags_FixedSize = 1 << 11, // all arrows will have the same size + ImPlotQuiverFlags_ColorByMagnitude = 1 << 12 // arrow colors will be mapped to their magnitudes }; // Flags for PlotStairs diff --git a/implot_demo.cpp b/implot_demo.cpp index 0b908a54..73e37c76 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -466,7 +466,7 @@ void Demo_QuiverPlots(){ ImGui::SameLine(); HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); - static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_Colored; + static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_ColorByMagnitude; CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_NoClip); ImGui::SameLine(); @@ -476,9 +476,9 @@ void Demo_QuiverPlots(){ ImGui::SameLine(); HelpMarker("All arrows will have the length set to base size"); - CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_Colored); + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_ColorByMagnitude); ImGui::SameLine(); - HelpMarker("Arrow colors will be mapped to their magnitudes"); + HelpMarker("Arrow will be colored by on their magnitudes"); ImPlot::PushColormap(map); if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { diff --git a/implot_items.cpp b/implot_items.cpp index 4af9d716..63ab2ed3 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1844,7 +1844,7 @@ void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_m const ImPlotNextItemData& s = GetItemData(); const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); - const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_Colored); + const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_ColorByMagnitude); const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_FixedSize); double final_mag_min = mag_min; From 662fccae7ffc68544ca33146a0f4d87c72186f33 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 10:05:20 +0100 Subject: [PATCH 12/13] refactor: move PlotQuiver to after PlotHeatmap --- implot.h | 27 ++-- implot_demo.cpp | 139 ++++++++++---------- implot_items.cpp | 334 +++++++++++++++++++++++------------------------ 3 files changed, 246 insertions(+), 254 deletions(-) diff --git a/implot.h b/implot.h index a4c35f3f..f134cccb 100644 --- a/implot.h +++ b/implot.h @@ -93,7 +93,6 @@ typedef int ImPlotItemFlags; // -> ImPlotItemFlags_ typedef int ImPlotLineFlags; // -> ImPlotLineFlags_ typedef int ImPlotScatterFlags; // -> ImPlotScatterFlags typedef int ImPlotBubblesFlags; // -> ImPlotBubblesFlags -typedef int ImPlotQuiverFlags; // -> ImPlotQuiverFlags_ typedef int ImPlotStairsFlags; // -> ImPlotStairsFlags_ typedef int ImPlotShadedFlags; // -> ImPlotShadedFlags_ typedef int ImPlotBarsFlags; // -> ImPlotBarsFlags_ @@ -103,6 +102,7 @@ typedef int ImPlotStemsFlags; // -> ImPlotStemsFlags_ typedef int ImPlotInfLinesFlags; // -> ImPlotInfLinesFlags_ typedef int ImPlotPieChartFlags; // -> ImPlotPieChartFlags_ typedef int ImPlotHeatmapFlags; // -> ImPlotHeatmapFlags_ +typedef int ImPlotQuiverFlags; // -> ImPlotQuiverFlags_ typedef int ImPlotHistogramFlags; // -> ImPlotHistogramFlags_ typedef int ImPlotDigitalFlags; // -> ImPlotDigitalFlags_ typedef int ImPlotImageFlags; // -> ImPlotImageFlags_ @@ -253,13 +253,6 @@ enum ImPlotBubblesFlags_ { ImPlotBubblesFlags_None = 0, // default }; -// Flags for PlotQuiver -enum ImPlotQuiverFlags_ { - ImPlotQuiverFlags_None = 0, // default - ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped - ImPlotQuiverFlags_FixedSize = 1 << 11, // all arrows will have the same size - ImPlotQuiverFlags_ColorByMagnitude = 1 << 12 // arrow colors will be mapped to their magnitudes - // Flags for PlotStairs enum ImPlotStairsFlags_ { ImPlotStairsFlags_None = 0, // default @@ -317,6 +310,14 @@ enum ImPlotHeatmapFlags_ { ImPlotHeatmapFlags_ColMajor = 1 << 10, // data will be read in column major order }; +// Flags for PlotQuiver +enum ImPlotQuiverFlags_ { + ImPlotQuiverFlags_None = 0, // default + ImPlotQuiverFlags_NoClip = 1 << 10, // arrows on the edge of a plot will not be clipped + ImPlotQuiverFlags_FixedSize = 1 << 11, // all arrows will have the same size + ImPlotQuiverFlags_ColorByMagnitude = 1 << 12 // arrow colors will be mapped to their magnitudes +}; + // Flags for PlotHistogram and PlotHistogram2D enum ImPlotHistogramFlags_ { ImPlotHistogramFlags_None = 0, // default @@ -886,9 +887,6 @@ 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, ImPlotBubblesFlags flags=0, 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, int offset=0, int stride=sizeof(T)); -// Plots a standard 2D quiver plot. The direction and magnitude of the arrows are determined by #us and #vs. Set #mag_min and #mag_max to specify a range of magnitudes to map to the arrow colors. Set mag_min = mag_max = 0 to use the full colormap range. -IMPLOT_TMP void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min=0, double mag_max=0, ImPlotQuiverFlags flags=0, int offset=0, int stride=sizeof(T)); - // 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)); @@ -926,6 +924,9 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); +// Plots a standard 2D quiver plot. The direction and magnitude of the arrows are determined by #us and #vs. Set #mag_min and #mag_max to specify a range of magnitudes to map to the arrow colors. Set mag_min = mag_max = 0 to use the full colormap range. +IMPLOT_TMP void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min=0, double mag_max=0, ImPlotQuiverFlags flags=0, int offset=0, int stride=sizeof(T)); + // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. IMPLOT_TMP double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, double bar_scale=1.0, ImPlotRange range=ImPlotRange(), ImPlotHistogramFlags flags=0); @@ -1151,10 +1152,10 @@ IMPLOT_API void SetNextLineStyle(const ImVec4& col = IMPLOT_AUTO_COL, float weig IMPLOT_API void SetNextFillStyle(const ImVec4& col = IMPLOT_AUTO_COL, float alpha_mod = IMPLOT_AUTO); // Set the marker style for the next item only. IMPLOT_API void SetNextMarkerStyle(ImPlotMarker marker = IMPLOT_AUTO, float size = IMPLOT_AUTO, const ImVec4& fill = IMPLOT_AUTO_COL, float weight = IMPLOT_AUTO, const ImVec4& outline = IMPLOT_AUTO_COL); -// Set the quiver style for the next item only. -IMPLOT_API void SetNextQuiverStyle(float size, const ImVec4& col = IMPLOT_AUTO_COL); // Set the error bar style for the next item only. IMPLOT_API void SetNextErrorBarStyle(const ImVec4& col = IMPLOT_AUTO_COL, float size = IMPLOT_AUTO, float weight = IMPLOT_AUTO); +// Set the quiver style for the next item only. +IMPLOT_API void SetNextQuiverStyle(float size, const ImVec4& col = IMPLOT_AUTO_COL); // Gets the last item primary color (i.e. its legend icon color) IMPLOT_API ImVec4 GetLastItemColor(); diff --git a/implot_demo.cpp b/implot_demo.cpp index 6d17dece..1ed65ef1 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -425,74 +425,6 @@ void Demo_ScatterPlots() { ImPlot::EndPlot(); } } -//----------------------------------------------------------------------------- - -void Demo_QuiverPlots(){ - static float xs[100], ys[100], us[100], vs[100]; - for (int i = 0; i < 10; ++i) { - for(int j = 0; j < 10; ++j){ - int idx = i*10 + j; - xs[idx] = ((float)j * 0.1f) - 0.5; - ys[idx] = ((float)i * 0.1f) - 0.5; - - // Taylor-Green vortex - float k = 2.0f * 3.14159f; - us[idx] = sinf(k * xs[idx]) * cosf(k * ys[idx]); - vs[idx] = -cosf(k * xs[idx]) * sinf(k * ys[idx]); - } - } - - static float mag_min = 0.00f; - static float mag_max = 1.0f; - static float base_size = 12.0f; - static ImPlotColormap map = ImPlotColormap_Viridis; - - if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225,0), map)) { - map = (map + 1) % ImPlot::GetColormapCount(); - } - ImGui::SameLine(); - ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); - - ImGui::SetNextItemWidth(225); - ImGui::DragFloatRange2("Min / Max Magnitude", &mag_min, &mag_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); - if (mag_max < mag_min) { - mag_max = mag_min; - } - ImGui::SameLine(); - HelpMarker("Minumum and maximum magnitudes for color mapping"); - - ImGui::SetNextItemWidth(225); - ImGui::DragFloat("Base Size", &base_size, 0.1f, 0, 100); - ImGui::SameLine(); - HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); - - static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_ColorByMagnitude; - - CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_NoClip); - ImGui::SameLine(); - HelpMarker("Arrows on the edge of the plot will not be clipped"); - - CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_FixedSize); - ImGui::SameLine(); - HelpMarker("All arrows will have the length set to base size"); - - CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_ColorByMagnitude); - ImGui::SameLine(); - HelpMarker("Arrow will be colored by on their magnitudes"); - - ImPlot::PushColormap(map); - if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { - ImPlot::SetupAxisTicks(ImAxis_X1, -0.5, 0.5, 11); - ImPlot::SetupAxisTicks(ImAxis_Y1, -0.5, 0.5, 11); - ImPlot::SetNextQuiverStyle(base_size, ImPlot::GetColormapColor(1)); - ImPlot::SetupAxes("x", "y"); - ImPlot::PlotQuiver("Magnitude", xs, ys, us, vs, 100, mag_min, mag_max, qv_flags); - ImPlot::EndPlot(); - } - ImGui::SameLine(); - ImPlot::ColormapScale("##QuiverScale", mag_min, mag_max); - ImPlot::PopColormap(); -} //----------------------------------------------------------------------------- @@ -815,6 +747,75 @@ void Demo_Heatmaps() { //----------------------------------------------------------------------------- +void Demo_QuiverPlots(){ + static float xs[100], ys[100], us[100], vs[100]; + for (int i = 0; i < 10; ++i) { + for(int j = 0; j < 10; ++j){ + int idx = i*10 + j; + xs[idx] = ((float)j * 0.1f) - 0.5; + ys[idx] = ((float)i * 0.1f) - 0.5; + + // Taylor-Green vortex + float k = 2.0f * 3.14159f; + us[idx] = sinf(k * xs[idx]) * cosf(k * ys[idx]); + vs[idx] = -cosf(k * xs[idx]) * sinf(k * ys[idx]); + } + } + + static float mag_min = 0.00f; + static float mag_max = 1.0f; + static float base_size = 12.0f; + static ImPlotColormap map = ImPlotColormap_Viridis; + + if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225,0), map)) { + map = (map + 1) % ImPlot::GetColormapCount(); + } + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); + + ImGui::SetNextItemWidth(225); + ImGui::DragFloatRange2("Min / Max Magnitude", &mag_min, &mag_max, 0.01f, -20, 20,nullptr,nullptr,ImGuiSliderFlags_AlwaysClamp); + if (mag_max < mag_min) { + mag_max = mag_min; + } + ImGui::SameLine(); + HelpMarker("Minumum and maximum magnitudes for color mapping"); + + ImGui::SetNextItemWidth(225); + ImGui::DragFloat("Base Size", &base_size, 0.1f, 0, 100); + ImGui::SameLine(); + HelpMarker("Maximum arrow size in pixels. The actual size will depend on the arrow's magnitude"); + + static ImPlotQuiverFlags qv_flags = ImPlotQuiverFlags_ColorByMagnitude; + + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_NoClip); + ImGui::SameLine(); + HelpMarker("Arrows on the edge of the plot will not be clipped"); + + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_FixedSize); + ImGui::SameLine(); + HelpMarker("All arrows will have the length set to base size"); + + CHECKBOX_FLAG(qv_flags, ImPlotQuiverFlags_ColorByMagnitude); + ImGui::SameLine(); + HelpMarker("Arrow will be colored by on their magnitudes"); + + ImPlot::PushColormap(map); + if (ImPlot::BeginPlot("Quiver Plot", ImVec2(ImGui::GetTextLineHeight()*28, ImGui::GetTextLineHeight()*28))) { + ImPlot::SetupAxisTicks(ImAxis_X1, -0.5, 0.5, 11); + ImPlot::SetupAxisTicks(ImAxis_Y1, -0.5, 0.5, 11); + ImPlot::SetNextQuiverStyle(base_size, ImPlot::GetColormapColor(1)); + ImPlot::SetupAxes("x", "y"); + ImPlot::PlotQuiver("Magnitude", xs, ys, us, vs, 100, mag_min, mag_max, qv_flags); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##QuiverScale", mag_min, mag_max); + ImPlot::PopColormap(); +} + +//----------------------------------------------------------------------------- + void Demo_Histogram() { static ImPlotHistogramFlags hist_flags = ImPlotHistogramFlags_Density; static int bins = 50; @@ -2354,7 +2355,6 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Shaded Plots##", Demo_ShadedPlots); DemoHeader("Scatter Plots", Demo_ScatterPlots); DemoHeader("Bubble Plots", Demo_BubblePlots); - DemoHeader("Quiver Plots", Demo_QuiverPlots); DemoHeader("Realtime Plots", Demo_RealtimePlots); DemoHeader("Stairstep Plots", Demo_StairstepPlots); DemoHeader("Bar Plots", Demo_BarPlots); @@ -2365,6 +2365,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Infinite Lines", Demo_InfiniteLines); DemoHeader("Pie Charts", Demo_PieCharts); DemoHeader("Heatmaps", Demo_Heatmaps); + DemoHeader("Quiver Plots", Demo_QuiverPlots); DemoHeader("Histogram", Demo_Histogram); DemoHeader("Histogram 2D", Demo_Histogram2D); DemoHeader("Digital Plots", Demo_DigitalPlots); diff --git a/implot_items.cpp b/implot_items.cpp index 79f02db7..78ce07c5 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -344,13 +344,6 @@ void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, flo gp.NextItemData.MarkerWeight = weight; } -void SetNextQuiverStyle(float size, const ImVec4& col) { - ImPlotContext& gp = *GImPlot; - gp.NextItemData.QuiverSize = size; - gp.NextItemData.Colors[ImPlotCol_Fill] = col; - -} - void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; @@ -358,6 +351,13 @@ void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { gp.NextItemData.ErrorBarWeight = weight; } +void SetNextQuiverStyle(float size, const ImVec4& col) { + ImPlotContext& gp = *GImPlot; + gp.NextItemData.QuiverSize = size; + gp.NextItemData.Colors[ImPlotCol_Fill] = col; + +} + ImVec4 GetLastItemColor() { ImPlotContext& gp = *GImPlot; if (gp.PreviousItem) @@ -566,6 +566,27 @@ struct IndexerConst { // [SECTION] Getters //----------------------------------------------------------------------------- +// Implementation of a 3D point with double precision. +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]; } +}; + +// Implementation of a 4D point with double precision. +struct ImPlotPoint4D { + double x, y, z, w; + IMPLOT_API constexpr ImPlotPoint4D() : x(0.0), y(0.0), z(0.0), w(0.0) { } + IMPLOT_API constexpr ImPlotPoint4D(double _x, double _y, double _z, double _w) : x(_x), y(_y), z(_z), w(_w) { } + IMPLOT_API constexpr ImPlotPoint4D(const ImVec2& p, const ImVec2& q) : x((double)p.x), y((double)p.y), z((double)q.x), w((double)q.y) { } + IMPLOT_API constexpr ImPlotPoint4D(const ImVec4& r) : x((double)r.x), y((double)r.y), z((double)r.z), w((double)r.w) { } + IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((double*)(void*)(char*)this)[idx]; } + IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((const double*)(const void*)(const char*)this)[idx]; } +}; + + template struct GetterXY { GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } @@ -576,15 +597,6 @@ struct GetterXY { const _IndexerY IndxerY; const int Count; }; - -// Double precision point with three coordinates used by ImPlot. -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]; } -}; template struct GetterXYZ { @@ -598,18 +610,6 @@ struct GetterXYZ { const int Count; }; -// Implementation of a 4D point with double precision. -struct ImPlotPoint4D { - double x, y, z, w; - IMPLOT_API constexpr ImPlotPoint4D() : x(0.0), y(0.0), z(0.0), w(0.0) { } - IMPLOT_API constexpr ImPlotPoint4D(double _x, double _y, double _z, double _w) : x(_x), y(_y), z(_z), w(_w) { } - IMPLOT_API constexpr ImPlotPoint4D(const ImVec2& p, const ImVec2& q) : x((double)p.x), y((double)p.y), z((double)q.x), w((double)q.y) { } - IMPLOT_API constexpr ImPlotPoint4D(const ImVec4& r) : x((double)r.x), y((double)r.y), z((double)r.z), w((double)r.w) { } - IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((double*)(void*)(char*)this)[idx]; } - IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1 || idx == 2 || idx == 3); return ((const double*)(const void*)(const char*)this)[idx]; } -}; - -// Functor for returning a 2D vector in 2D space (For Quiver plots) template struct GetterXYUV { GetterXYUV(_IndexerX x, _IndexerY y, _IndexerU u, _IndexerV v, int count) @@ -626,7 +626,7 @@ struct GetterXYUV { const _IndexerU IndxerU; const _IndexerV IndxerV; const int Count; -} +}; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { @@ -1737,6 +1737,78 @@ struct RendererCircleLine : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererVectorFill : RendererBase { + RendererVectorFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag, bool color_coded, bool normalized) : + RendererBase(getter.Count, (count-2)*3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size), + Col(col), + MinMag(min_mag), + MaxMag(max_mag), + ColorCoded(color_coded), + Normalized(normalized) + { } + 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 { + ImPlotPoint4D vec = Getter(prim); + + ImVec2 p; + p.x = this->Transformer.Tx(vec.x); + p.y = this->Transformer.Ty(vec.y); + + float theta = ImAtan2(-vec.w, vec.z); + float cos_theta = ImCos(theta); + float sin_theta = ImSin(theta); + double mag = (double)ImSqrt(vec.z*vec.z + vec.w*vec.w); + + double size_scale = Normalized ? 1.0 : ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); + ImU32 color_final = Col; + if (ColorCoded) { + double t = ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); + color_final = ImColor(SampleColormap((float)t)); + } + + bool should_render = (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && + p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y); + if (Normalized) should_render = should_render && (mag > 1e-6); + + if (should_render) { + for (int i = 0; i < Count; i++) { + float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; + float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; + draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_scale; + draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size * size_scale; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = color_final; + 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 float Size; + const ImU32 Col; + mutable ImVec2 UV; + double MinMag; + double MaxMag; + bool ColorCoded; + bool Normalized; +}; 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)}; @@ -1816,84 +1888,6 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool } } - -//----------------------------------------------------------------------------- -// [SECTION] Vectors -//----------------------------------------------------------------------------- - -template -struct RendererVectorFill : RendererBase { - RendererVectorFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col, double min_mag, double max_mag, bool color_coded, bool normalized) : - RendererBase(getter.Count, (count-2)*3, count), - Getter(getter), - Marker(marker), - Count(count), - Size(size), - Col(col), - MinMag(min_mag), - MaxMag(max_mag), - ColorCoded(color_coded), - Normalized(normalized) - { } - 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 { - ImPlotPoint4D vec = Getter(prim); - - ImVec2 p; - p.x = this->Transformer.Tx(vec.x); - p.y = this->Transformer.Ty(vec.y); - - float theta = ImAtan2(-vec.w, vec.z); - float cos_theta = ImCos(theta); - float sin_theta = ImSin(theta); - double mag = (double)ImSqrt(vec.z*vec.z + vec.w*vec.w); - - double size_scale = Normalized ? 1.0 : ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); - ImU32 color_final = Col; - if (ColorCoded) { - double t = ImClamp(ImRemap01(mag, MinMag, MaxMag), 0.0, 1.0); - color_final = ImColor(SampleColormap((float)t)); - } - - bool should_render = (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && - p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y); - if (Normalized) should_render = should_render && (mag > 1e-6); - - if (should_render) { - for (int i = 0; i < Count; i++) { - float rotated_x = Marker[i].x * cos_theta - Marker[i].y * sin_theta; - float rotated_y = Marker[i].x * sin_theta + Marker[i].y * cos_theta; - draw_list._VtxWritePtr[0].pos.x = p.x + rotated_x * Size * size_scale; - draw_list._VtxWritePtr[0].pos.y = p.y + rotated_y * Size * size_scale; - draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = color_final; - 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 float Size; - const ImU32 Col; - mutable ImVec2 UV; - double MinMag; - double MaxMag; - bool ColorCoded; - bool Normalized; -}; - //----------------------------------------------------------------------------- // [SECTION] PlotLine //----------------------------------------------------------------------------- @@ -2019,71 +2013,6 @@ void PlotScatterG(const char* label_id, ImPlotGetter getter_func, void* data, in return PlotScatterEx(label_id, getter, flags); } - -//----------------------------------------------------------------------------- -// [SECTION] PlotQuiver (Vector Field) -//----------------------------------------------------------------------------- - -template -void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_min, const double mag_max, ImPlotQuiverFlags flags){ - if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { - if (getter.Count <= 0) { - EndItem(); - return; - } - const ImPlotNextItemData& s = GetItemData(); - - const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); - const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_ColorByMagnitude); - const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_FixedSize); - - double final_mag_min = mag_min; - double final_mag_max = mag_max; - - if (color_coded && mag_min == mag_max) { - final_mag_min = INFINITY; - final_mag_max = -INFINITY; - for (int i = 0; i < getter.Count; ++i) { - ImPlotPoint4D p = getter(i); - double mag = ImSqrt(p.z*p.z + p.w*p.w); - if (mag < final_mag_min) final_mag_min = mag; - if (mag > final_mag_max) final_mag_max = mag; - } - if (final_mag_min == final_mag_max) { - final_mag_max = final_mag_min + 1.0; - } - } - - if (ImHasFlag(flags, ImPlotQuiverFlags_NoClip)) { - PopPlotClipRect(); - PushPlotClipRect(s.QuiverSize); - } - RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, final_mag_min, final_mag_max, color_coded, normalized); - - EndItem(); - } -} - -template -void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride) { - GetterXYUV,IndexerIdx,IndexerIdx,IndexerIdx> getter( - IndexerIdx(xs,count,offset,stride), - IndexerIdx(ys,count,offset,stride), - IndexerIdx(us,count,offset,stride), - IndexerIdx(vs,count,offset,stride), - count); - return PlotQuiverEx(label_id, getter, mag_min, mag_max, flags); -} - -#define INSTANTIATE_MACRO(T) \ - template IMPLOT_API void PlotQuiver(const char* label_id, const T* xs, const T* ys, const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride); -CALL_INSTANTIATE_FOR_NUMERIC_TYPES() -#undef INSTANTIATE_MACRO - - - - - //----------------------------------------------------------------------------- // [SECTION] PlotBubbles //----------------------------------------------------------------------------- @@ -2973,6 +2902,67 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO +//----------------------------------------------------------------------------- +// [SECTION] PlotQuiver +//----------------------------------------------------------------------------- + +template +void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_min, const double mag_max, ImPlotQuiverFlags flags){ + if (BeginItemEx(label_id, FitterVector(getter), flags,ImPlotCol_Fill)) { + if (getter.Count <= 0) { + EndItem(); + return; + } + const ImPlotNextItemData& s = GetItemData(); + + const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); + const bool color_coded = ImHasFlag(flags, ImPlotQuiverFlags_ColorByMagnitude); + const bool normalized = ImHasFlag(flags, ImPlotQuiverFlags_FixedSize); + + double final_mag_min = mag_min; + double final_mag_max = mag_max; + + if (color_coded && mag_min == mag_max) { + final_mag_min = INFINITY; + final_mag_max = -INFINITY; + for (int i = 0; i < getter.Count; ++i) { + ImPlotPoint4D p = getter(i); + double mag = ImSqrt(p.z*p.z + p.w*p.w); + if (mag < final_mag_min) final_mag_min = mag; + if (mag > final_mag_max) final_mag_max = mag; + } + if (final_mag_min == final_mag_max) { + final_mag_max = final_mag_min + 1.0; + } + } + + if (ImHasFlag(flags, ImPlotQuiverFlags_NoClip)) { + PopPlotClipRect(); + PushPlotClipRect(s.QuiverSize); + } + RenderPrimitives1(getter, MARKER_FILL_ARROW, 7, s.QuiverSize, col_fill, final_mag_min, final_mag_max, color_coded, normalized); + + EndItem(); + } +} + +template +void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride) { + GetterXYUV,IndexerIdx,IndexerIdx,IndexerIdx> getter( + IndexerIdx(xs,count,offset,stride), + IndexerIdx(ys,count,offset,stride), + IndexerIdx(us,count,offset,stride), + IndexerIdx(vs,count,offset,stride), + count); + return PlotQuiverEx(label_id, getter, mag_min, mag_max, flags); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API void PlotQuiver(const char* label_id, const T* xs, const T* ys, const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride); +CALL_INSTANTIATE_FOR_NUMERIC_TYPES() +#undef INSTANTIATE_MACRO + + //----------------------------------------------------------------------------- // [SECTION] PlotHistogram //----------------------------------------------------------------------------- From 4cbd970237daf16e71a017aacced561d8af91c1c Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 10:08:34 +0100 Subject: [PATCH 13/13] refactor: rename GetterXYUV to GetterXYZW --- implot_items.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 78ce07c5..80b82d70 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -610,21 +610,19 @@ struct GetterXYZ { const int Count; }; -template -struct GetterXYUV { - GetterXYUV(_IndexerX x, _IndexerY y, _IndexerU u, _IndexerV v, int count) - : IndxerX(x), IndxerY(y), IndxerU(u), IndxerV(v), Count(count) { } +template +struct GetterXYZW { + GetterXYZW(_IndexerX x, _IndexerY y, _IndexerZ z, _IndexerW w, int count) + : IndxerX(x), IndxerY(y), IndxerZ(z), IndxerW(w), Count(count) { } template IMPLOT_INLINE ImPlotPoint4D operator()(I idx) const { - double z_val = IndxerU(idx); - double w_val = IndxerV(idx); - return ImPlotPoint4D(IndxerX(idx), IndxerY(idx), z_val, w_val); + return ImPlotPoint4D(IndxerX(idx), IndxerY(idx), IndxerZ(idx), IndxerW(idx)); } const _IndexerX IndxerX; const _IndexerY IndxerY; - const _IndexerU IndxerU; - const _IndexerV IndxerV; + const _IndexerZ IndxerZ; + const _IndexerW IndxerW; const int Count; }; @@ -2948,7 +2946,7 @@ void PlotQuiverEx(const char* label_id, const Getter& getter, const double mag_m template void PlotQuiver(const char* label_id, const T* xs, const T* ys,const T* us, const T* vs, int count, double mag_min, double mag_max, ImPlotQuiverFlags flags, int offset, int stride) { - GetterXYUV,IndexerIdx,IndexerIdx,IndexerIdx> getter( + GetterXYZW,IndexerIdx,IndexerIdx,IndexerIdx> getter( IndexerIdx(xs,count,offset,stride), IndexerIdx(ys,count,offset,stride), IndexerIdx(us,count,offset,stride),