//-------------------------------------------------- // ImPlot3D v0.1 // implot3d_items.cpp // Date: 2024-11-26 // Author: Breno Cunha Queiroz (brenocq.com) // // Acknowledgments: // ImPlot3D is heavily inspired by ImPlot // (https://github.com/epezent/implot) by Evan Pezent, // and follows a similar code style and structure to // maintain consistency with ImPlot's API. //-------------------------------------------------- // Table of Contents: // [SECTION] Includes // [SECTION] Macros & Defines // [SECTION] Template instantiation utility // [SECTION] Item Utils // [SECTION] Draw Utils // [SECTION] Renderers // [SECTION] Indexers // [SECTION] Getters // [SECTION] RenderPrimitives // [SECTION] Markers // [SECTION] PlotScatter // [SECTION] PlotLine // [SECTION] PlotTriangle // [SECTION] PlotText //----------------------------------------------------------------------------- // [SECTION] Includes //----------------------------------------------------------------------------- #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "implot3d.h" #include "implot3d_internal.h" #ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- // [SECTION] Macros & Defines //----------------------------------------------------------------------------- #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f // clang-format off #ifndef IMPLOT3D_NO_FORCE_INLINE #ifdef _MSC_VER #define IMPLOT3D_INLINE __forceinline #elif defined(__GNUC__) #define IMPLOT3D_INLINE inline __attribute__((__always_inline__)) #elif defined(__CLANG__) #if __has_attribute(__always_inline__) #define IMPLOT3D_INLINE inline __attribute__((__always_inline__)) #else #define IMPLOT3D_INLINE inline #endif #else #define IMPLOT3D_INLINE inline #endif #else #define IMPLOT3D_INLINE inline #endif // clang-format on #define IMPLOT3D_NORMALIZE2F(VX, VY) \ do { \ float d2 = VX * VX + VY * VY; \ if (d2 > 0.0f) { \ float inv_len = ImRsqrt(d2); \ VX *= inv_len; \ VY *= inv_len; \ } \ } while (0) IMPLOT3D_INLINE void GetLineRenderProps(const ImDrawList3D& draw_list_3d, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { const bool aa = ImPlot3D::ImHasFlag(draw_list_3d._Flags, ImDrawListFlags_AntiAliasedLines) && ImPlot3D::ImHasFlag(draw_list_3d._Flags, ImDrawListFlags_AntiAliasedLinesUseTex); if (aa) { ImVec4 tex_uvs = draw_list_3d._SharedData->TexUvLines[(int)(half_weight * 2)]; tex_uv0 = ImVec2(tex_uvs.x, tex_uvs.y); tex_uv1 = ImVec2(tex_uvs.z, tex_uvs.w); half_weight += 1; } else { tex_uv0 = tex_uv1 = draw_list_3d._SharedData->TexUvWhitePixel; } } //----------------------------------------------------------------------------- // [SECTION] Template instantiation utility //----------------------------------------------------------------------------- // By default, templates are instantiated for `float`, `double`, and for the following integer types, which are defined in imgui.h: // signed char ImS8; // 8-bit signed integer // unsigned char ImU8; // 8-bit unsigned integer // signed short ImS16; // 16-bit signed integer // unsigned short ImU16; // 16-bit unsigned integer // signed int ImS32; // 32-bit signed integer == int // unsigned int ImU32; // 32-bit unsigned integer // signed long long ImS64; // 64-bit signed integer // unsigned long long ImU64; // 64-bit unsigned integer // (note: this list does *not* include `long`, `unsigned long` and `long double`) // // You can customize the supported types by defining IMPLOT3D_CUSTOM_NUMERIC_TYPES at compile time to define your own type list. // As an example, you could use the compile time define given by the line below in order to support only float and double. // -DIMPLOT3D_CUSTOM_NUMERIC_TYPES="(float)(double)" // In order to support all known C++ types, use: // -DIMPLOT3D_CUSTOM_NUMERIC_TYPES="(signed char)(unsigned char)(signed short)(unsigned short)(signed int)(unsigned int)(signed long)(unsigned long)(signed long long)(unsigned long long)(float)(double)(long double)" #ifdef IMPLOT3D_CUSTOM_NUMERIC_TYPES #define IMPLOT3D_NUMERIC_TYPES IMPLOT3D_CUSTOM_NUMERIC_TYPES #else #define IMPLOT3D_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double) #endif // CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantiation code `INSTANTIATE_MACRO(T)` on supported types. #define _CAT(x, y) _CAT_(x, y) #define _CAT_(x, y) x##y #define _INSTANTIATE_FOR_NUMERIC_TYPES(chain) _CAT(_INSTANTIATE_FOR_NUMERIC_TYPES_1 chain, _END) #define _INSTANTIATE_FOR_NUMERIC_TYPES_1(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_2 #define _INSTANTIATE_FOR_NUMERIC_TYPES_2(T) INSTANTIATE_MACRO(T) _INSTANTIATE_FOR_NUMERIC_TYPES_1 #define _INSTANTIATE_FOR_NUMERIC_TYPES_1_END #define _INSTANTIATE_FOR_NUMERIC_TYPES_2_END #define CALL_INSTANTIATE_FOR_NUMERIC_TYPES() _INSTANTIATE_FOR_NUMERIC_TYPES(IMPLOT3D_NUMERIC_TYPES) //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- namespace ImPlot3D { static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; bool BeginItem(const char* label_id, ImPlot3DItemFlags flags, ImPlot3DCol recolor_from) { ImPlot3DContext& gp = *GImPlot3D; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); // Lock setup SetupLock(); ImPlot3DStyle& style = gp.Style; ImPlot3DNextItemData& n = gp.NextItemData; // Register item bool just_created; ImPlot3DItem* item = RegisterOrGetItem(label_id, flags, &just_created); // Set/override item color if (recolor_from != -1) { if (!IsColorAuto(n.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(n.Colors[recolor_from]); else if (!IsColorAuto(gp.Style.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]); else if (just_created) item->Color = NextColormapColorU32(); } else if (just_created) { item->Color = NextColormapColorU32(); } // Set next item color ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); n.IsAutoLine = IsColorAuto(n.Colors[ImPlot3DCol_Line]) && IsColorAuto(ImPlot3DCol_Line); n.IsAutoFill = IsColorAuto(n.Colors[ImPlot3DCol_Fill]) && IsColorAuto(ImPlot3DCol_Fill); n.Colors[ImPlot3DCol_Line] = IsColorAuto(n.Colors[ImPlot3DCol_Line]) ? (IsColorAuto(ImPlot3DCol_Line) ? item_color : gp.Style.Colors[ImPlot3DCol_Line]) : n.Colors[ImPlot3DCol_Line]; n.Colors[ImPlot3DCol_Fill] = IsColorAuto(n.Colors[ImPlot3DCol_Fill]) ? (IsColorAuto(ImPlot3DCol_Fill) ? item_color : gp.Style.Colors[ImPlot3DCol_Fill]) : n.Colors[ImPlot3DCol_Fill]; n.Colors[ImPlot3DCol_MarkerOutline] = IsColorAuto(n.Colors[ImPlot3DCol_MarkerOutline]) ? (IsColorAuto(ImPlot3DCol_MarkerOutline) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerOutline]) : n.Colors[ImPlot3DCol_MarkerOutline]; n.Colors[ImPlot3DCol_MarkerFill] = IsColorAuto(n.Colors[ImPlot3DCol_MarkerFill]) ? (IsColorAuto(ImPlot3DCol_MarkerFill) ? n.Colors[ImPlot3DCol_Line] : gp.Style.Colors[ImPlot3DCol_MarkerFill]) : n.Colors[ImPlot3DCol_MarkerFill]; // Set size & weight n.LineWeight = n.LineWeight < 0.0f ? style.LineWeight : n.LineWeight; n.Marker = n.Marker < 0 ? style.Marker : n.Marker; n.MarkerSize = n.MarkerSize < 0.0f ? style.MarkerSize : n.MarkerSize; n.MarkerWeight = n.MarkerWeight < 0.0f ? style.MarkerWeight : n.MarkerWeight; n.FillAlpha = n.FillAlpha < 0 ? gp.Style.FillAlpha : n.FillAlpha; // Apply alpha modifiers n.Colors[ImPlot3DCol_Fill].w *= n.FillAlpha; n.Colors[ImPlot3DCol_MarkerFill].w *= n.FillAlpha; // Set render flags n.RenderLine = n.Colors[ImPlot3DCol_Line].w > 0 && n.LineWeight > 0; n.RenderFill = n.Colors[ImPlot3DCol_Fill].w > 0; n.RenderMarkerFill = n.Colors[ImPlot3DCol_MarkerFill].w > 0; n.RenderMarkerLine = n.Colors[ImPlot3DCol_MarkerOutline].w > 0 && n.MarkerWeight > 0; // Don't render if item is hidden if (!item->Show) { EndItem(); return false; } else { // Legend hover highlight if (item->LegendHovered) { if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlot3DLegendFlags_NoHighlightItem)) { n.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; n.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; n.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; } } } return true; } template bool BeginItemEx(const char* label_id, const _Getter& getter, ImPlot3DItemFlags flags = 0, ImPlot3DCol recolor_from = IMPLOT3D_AUTO) { if (BeginItem(label_id, flags, recolor_from)) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DPlot& plot = *gp.CurrentPlot; if (plot.FitThisFrame && !ImHasFlag(flags, ImPlot3DItemFlags_NoFit)) { for (int i = 0; i < getter.Count; i++) plot.ExtendFit(getter(i)); } return true; } return false; } void EndItem() { ImPlot3DContext& gp = *GImPlot3D; gp.NextItemData.Reset(); } ImPlot3DItem* RegisterOrGetItem(const char* label_id, ImPlot3DItemFlags flags, bool* just_created) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DItemGroup& Items = *gp.CurrentItems; ImGuiID id = Items.GetItemID(label_id); if (just_created != nullptr) *just_created = Items.GetItem(id) == nullptr; ImPlot3DItem* item = Items.GetOrAddItem(id); // Avoid re-adding the same item to the legend (the legend is reset every frame) if (item->SeenThisFrame) return item; item->SeenThisFrame = true; // Add item to the legend int idx = Items.GetItemIndex(item); item->ID = id; if (!ImHasFlag(flags, ImPlot3DItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, nullptr) != label_id) { Items.Legend.Indices.push_back(idx); item->NameOffset = Items.Legend.Labels.size(); Items.Legend.Labels.append(label_id, label_id + strlen(label_id) + 1); } return item; } void BustItemCache() { ImPlot3DContext& gp = *GImPlot3D; for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlot3DPlot& plot = *gp.Plots.GetByIndex(p); plot.Items.Reset(); } } void SetNextLineStyle(const ImVec4& col, float weight) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DNextItemData& n = gp.NextItemData; n.Colors[ImPlot3DCol_Line] = col; n.LineWeight = weight; } void SetNextFillStyle(const ImVec4& col, float alpha) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DNextItemData& n = gp.NextItemData; gp.NextItemData.Colors[ImPlot3DCol_Fill] = col; gp.NextItemData.FillAlpha = alpha; } void SetNextMarkerStyle(ImPlot3DMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DNextItemData& n = gp.NextItemData; n.Marker = marker; n.Colors[ImPlot3DCol_MarkerFill] = fill; n.MarkerSize = size; n.Colors[ImPlot3DCol_MarkerOutline] = outline; n.MarkerWeight = weight; } //----------------------------------------------------------------------------- // [SECTION] Draw Utils //----------------------------------------------------------------------------- IMPLOT3D_INLINE void PrimLine(ImDrawList3D& draw_list_3d, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1, float z) { float dx = P2.x - P1.x; float dy = P2.y - P1.y; IMPLOT3D_NORMALIZE2F(dx, dy); dx *= half_weight; dy *= half_weight; draw_list_3d._VtxWritePtr[0].pos.x = P1.x + dy; draw_list_3d._VtxWritePtr[0].pos.y = P1.y - dx; draw_list_3d._VtxWritePtr[0].uv = tex_uv0; draw_list_3d._VtxWritePtr[0].col = col; draw_list_3d._VtxWritePtr[1].pos.x = P2.x + dy; draw_list_3d._VtxWritePtr[1].pos.y = P2.y - dx; draw_list_3d._VtxWritePtr[1].uv = tex_uv0; draw_list_3d._VtxWritePtr[1].col = col; draw_list_3d._VtxWritePtr[2].pos.x = P2.x - dy; draw_list_3d._VtxWritePtr[2].pos.y = P2.y + dx; draw_list_3d._VtxWritePtr[2].uv = tex_uv1; draw_list_3d._VtxWritePtr[2].col = col; draw_list_3d._VtxWritePtr[3].pos.x = P1.x - dy; draw_list_3d._VtxWritePtr[3].pos.y = P1.y + dx; draw_list_3d._VtxWritePtr[3].uv = tex_uv1; draw_list_3d._VtxWritePtr[3].col = col; draw_list_3d._VtxWritePtr += 4; draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1); draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3); draw_list_3d._IdxWritePtr += 6; draw_list_3d._VtxCurrentIdx += 4; draw_list_3d._ZWritePtr[0] = z; draw_list_3d._ZWritePtr[1] = z; draw_list_3d._ZWritePtr += 2; } //----------------------------------------------------------------------------- // [SECTION] Renderers //----------------------------------------------------------------------------- float GetPointDepth(ImPlot3DPoint p) { ImPlot3DContext& gp = *GImPlot3D; ImPlot3DPlot& plot = *gp.CurrentPlot; ImPlot3DPoint p_rot = plot.Rotation * p; return p_rot.z; } struct RendererBase { RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) {} const unsigned int Prims; // Number of primitives to render const unsigned int IdxConsumed; // Number of indices consumed per primitive const unsigned int VtxConsumed; // Number of vertices consumed per primitive }; template struct RendererMarkersFill : RendererBase { RendererMarkersFill(const _Getter& getter, const ImVec2* marker, int count, float size, ImU32 col) : RendererBase(getter.Count, (count - 2) * 3, count), Getter(getter), Marker(marker), Count(count), Size(size), Col(col) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { ImPlot3DPoint p_plot = Getter(prim); if (!cull_box.Contains(p_plot)) return false; ImVec2 p = PlotToPixels(p_plot); // 3 vertices per triangle for (int i = 0; i < Count; i++) { draw_list_3d._VtxWritePtr[0].pos.x = p.x + Marker[i].x * Size; draw_list_3d._VtxWritePtr[0].pos.y = p.y + Marker[i].y * Size; draw_list_3d._VtxWritePtr[0].uv = UV; draw_list_3d._VtxWritePtr[0].col = Col; draw_list_3d._VtxWritePtr++; } // 3 indices per triangle for (int i = 2; i < Count; i++) { // Indices draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + i - 1); draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + i); draw_list_3d._IdxWritePtr += 3; // Z draw_list_3d._ZWritePtr[0] = GetPointDepth(p_plot); draw_list_3d._ZWritePtr++; } // Update vertex count draw_list_3d._VtxCurrentIdx += (ImDrawIdx)Count; return true; } const _Getter& Getter; const ImVec2* Marker; const int Count; const float Size; const ImU32 Col; mutable ImVec2 UV; }; template struct RendererMarkersLine : RendererBase { RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col) : RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), Getter(getter), Marker(marker), Count(count), HalfWeight(ImMax(1.0f, weight) * 0.5f), Size(size), Col(col) {} void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { ImPlot3DPoint p_plot = Getter(prim); if (!cull_box.Contains(p_plot)) return false; ImVec2 p = PlotToPixels(p_plot); for (int i = 0; i < Count; i = i + 2) { ImVec2 p1(p.x + Marker[i].x * Size, p.y + Marker[i].y * Size); ImVec2 p2(p.x + Marker[i + 1].x * Size, p.y + Marker[i + 1].y * Size); PrimLine(draw_list_3d, p1, p2, HalfWeight, Col, UV0, UV1, GetPointDepth(p_plot)); } return true; } const _Getter& Getter; const ImVec2* Marker; const int Count; mutable float HalfWeight; const float Size; const ImU32 Col; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineStrip : RendererBase { RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) { // Initialize the first point in plot coordinates P1_plot = Getter(0); } void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { ImPlot3DPoint P2_plot = Getter(prim + 1); // Clip the line segment to the culling box using Liang-Barsky algorithm ImPlot3DPoint P1_clipped, P2_clipped; bool visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped); if (visible) { // Convert clipped points to pixel coordinates ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f)); } // Update for next segment P1_plot = P2_plot; return visible; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImPlot3DPoint P1_plot; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineStripSkip : RendererBase { RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) { // Initialize the first point in plot coordinates P1_plot = Getter(0); } void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { // Get the next point in plot coordinates ImPlot3DPoint P2_plot = Getter(prim + 1); bool visible = false; // Check for NaNs in P1_plot and P2_plot if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) { // Clip the line segment to the culling box ImPlot3DPoint P1_clipped, P2_clipped; visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped); if (visible) { // Convert clipped points to pixel coordinates ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f)); } } // Update P1_plot if P2_plot is valid if (!ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) P1_plot = P2_plot; return visible; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImPlot3DPoint P1_plot; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments : RendererBase { RendererLineSegments(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f, weight) * 0.5f) {} void Init(ImDrawList3D& draw_list_3d) const { GetLineRenderProps(draw_list_3d, HalfWeight, UV0, UV1); } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { // Get the segment's endpoints in plot coordinates ImPlot3DPoint P1_plot = Getter(prim * 2 + 0); ImPlot3DPoint P2_plot = Getter(prim * 2 + 1); // Check for NaNs in P1_plot and P2_plot if (!ImNan(P1_plot.x) && !ImNan(P1_plot.y) && !ImNan(P1_plot.z) && !ImNan(P2_plot.x) && !ImNan(P2_plot.y) && !ImNan(P2_plot.z)) { // Clip the line segment to the culling box ImPlot3DPoint P1_clipped, P2_clipped; bool visible = cull_box.ClipLineSegment(P1_plot, P2_plot, P1_clipped, P2_clipped); if (visible) { // Convert clipped points to pixel coordinates ImVec2 P1_screen = PlotToPixels(P1_clipped); ImVec2 P2_screen = PlotToPixels(P2_clipped); // Render the line segment PrimLine(draw_list_3d, P1_screen, P2_screen, HalfWeight, Col, UV0, UV1, GetPointDepth((P1_plot + P2_plot) * 0.5f)); } return visible; } return false; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererTriangleFill : RendererBase { RendererTriangleFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 3, 3, 3), Getter(getter), Col(col) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { ImPlot3DPoint p_plot[3]; p_plot[0] = Getter(3 * prim); p_plot[1] = Getter(3 * prim + 1); p_plot[2] = Getter(3 * prim + 2); // Check if the triangle is outside the culling box if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1])) return false; // Project the triangle vertices to screen space ImVec2 p[3]; p[0] = PlotToPixels(p_plot[0]); p[1] = PlotToPixels(p_plot[1]); p[2] = PlotToPixels(p_plot[2]); // 3 vertices per triangle draw_list_3d._VtxWritePtr[0].pos.x = p[0].x; draw_list_3d._VtxWritePtr[0].pos.y = p[0].y; draw_list_3d._VtxWritePtr[0].uv = UV; draw_list_3d._VtxWritePtr[0].col = Col; draw_list_3d._VtxWritePtr[1].pos.x = p[1].x; draw_list_3d._VtxWritePtr[1].pos.y = p[1].y; draw_list_3d._VtxWritePtr[1].uv = UV; draw_list_3d._VtxWritePtr[1].col = Col; draw_list_3d._VtxWritePtr[2].pos.x = p[2].x; draw_list_3d._VtxWritePtr[2].pos.y = p[2].y; draw_list_3d._VtxWritePtr[2].uv = UV; draw_list_3d._VtxWritePtr[2].col = Col; draw_list_3d._VtxWritePtr += 3; // 3 indices per triangle draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1); draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr += 3; // 1 Z per vertex draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3); draw_list_3d._ZWritePtr++; // Update vertex count draw_list_3d._VtxCurrentIdx += 3; return true; } const _Getter& Getter; mutable ImVec2 UV; const ImU32 Col; }; template struct RendererQuadFill : RendererBase { RendererQuadFill(const _Getter& getter, ImU32 col) : RendererBase(getter.Count / 4, 6, 4), Getter(getter), Col(col) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { ImPlot3DPoint p_plot[4]; p_plot[0] = Getter(4 * prim); p_plot[1] = Getter(4 * prim + 1); p_plot[2] = Getter(4 * prim + 2); p_plot[3] = Getter(4 * prim + 3); // Check if the quad is outside the culling box if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3])) return false; // Project the quad vertices to screen space ImVec2 p[4]; p[0] = PlotToPixels(p_plot[0]); p[1] = PlotToPixels(p_plot[1]); p[2] = PlotToPixels(p_plot[2]); p[3] = PlotToPixels(p_plot[3]); // Add vertices for two triangles draw_list_3d._VtxWritePtr[0].pos.x = p[0].x; draw_list_3d._VtxWritePtr[0].pos.y = p[0].y; draw_list_3d._VtxWritePtr[0].uv = UV; draw_list_3d._VtxWritePtr[0].col = Col; draw_list_3d._VtxWritePtr[1].pos.x = p[1].x; draw_list_3d._VtxWritePtr[1].pos.y = p[1].y; draw_list_3d._VtxWritePtr[1].uv = UV; draw_list_3d._VtxWritePtr[1].col = Col; draw_list_3d._VtxWritePtr[2].pos.x = p[2].x; draw_list_3d._VtxWritePtr[2].pos.y = p[2].y; draw_list_3d._VtxWritePtr[2].uv = UV; draw_list_3d._VtxWritePtr[2].col = Col; draw_list_3d._VtxWritePtr[3].pos.x = p[3].x; draw_list_3d._VtxWritePtr[3].pos.y = p[3].y; draw_list_3d._VtxWritePtr[3].uv = UV; draw_list_3d._VtxWritePtr[3].col = Col; draw_list_3d._VtxWritePtr += 4; // Add indices for two triangles draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1); draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3); draw_list_3d._IdxWritePtr += 6; // Add depth values for the two triangles draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3.0f); draw_list_3d._ZWritePtr[1] = GetPointDepth((p_plot[0] + p_plot[2] + p_plot[3]) / 3.0f); draw_list_3d._ZWritePtr += 2; // Update vertex count draw_list_3d._VtxCurrentIdx += 4; return true; } const _Getter& Getter; mutable ImVec2 UV; const ImU32 Col; }; template struct RendererSurfaceFill : RendererBase { RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col) : RendererBase((x_count - 1) * (y_count - 1), 6, 4), Getter(getter), XCount(x_count), YCount(y_count), Col(col) {} void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; // Compute min and max values for the colormap (if not solid fill) const ImPlot3DNextItemData& n = GetItemData(); if (n.IsAutoFill) { Min = FLT_MAX; Max = -FLT_MAX; for (int i = 0; i < Getter.Count; i++) { float z = Getter(i).z; Min = ImMin(Min, z); Max = ImMax(Max, z); } } } IMPLOT3D_INLINE bool Render(ImDrawList3D& draw_list_3d, const ImPlot3DBox& cull_box, int prim) const { int x = prim % (XCount - 1); int y = prim / (XCount - 1); ImPlot3DPoint p_plot[4]; p_plot[0] = Getter(x + y * XCount); p_plot[1] = Getter(x + 1 + y * XCount); p_plot[2] = Getter(x + 1 + (y + 1) * XCount); p_plot[3] = Getter(x + (y + 1) * XCount); // Check if the quad is outside the culling box if (!cull_box.Contains(p_plot[0]) && !cull_box.Contains(p_plot[1]) && !cull_box.Contains(p_plot[2]) && !cull_box.Contains(p_plot[3])) return false; // Compute colors ImU32 cols[4] = {Col, Col, Col, Col}; const ImPlot3DNextItemData& n = GetItemData(); if (n.IsAutoFill) { float alpha = GImPlot3D->NextItemData.FillAlpha; for (int i = 0; i < 4; i++) { ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, Min, Max), 0.0f, 1.0f)); col.w *= alpha; cols[i] = ImGui::ColorConvertFloat4ToU32(col); } } // Project the quad vertices to screen space ImVec2 p[4]; p[0] = PlotToPixels(p_plot[0]); p[1] = PlotToPixels(p_plot[1]); p[2] = PlotToPixels(p_plot[2]); p[3] = PlotToPixels(p_plot[3]); // Add vertices for two triangles draw_list_3d._VtxWritePtr[0].pos.x = p[0].x; draw_list_3d._VtxWritePtr[0].pos.y = p[0].y; draw_list_3d._VtxWritePtr[0].uv = UV; draw_list_3d._VtxWritePtr[0].col = cols[0]; draw_list_3d._VtxWritePtr[1].pos.x = p[1].x; draw_list_3d._VtxWritePtr[1].pos.y = p[1].y; draw_list_3d._VtxWritePtr[1].uv = UV; draw_list_3d._VtxWritePtr[1].col = cols[1]; draw_list_3d._VtxWritePtr[2].pos.x = p[2].x; draw_list_3d._VtxWritePtr[2].pos.y = p[2].y; draw_list_3d._VtxWritePtr[2].uv = UV; draw_list_3d._VtxWritePtr[2].col = cols[2]; draw_list_3d._VtxWritePtr[3].pos.x = p[3].x; draw_list_3d._VtxWritePtr[3].pos.y = p[3].y; draw_list_3d._VtxWritePtr[3].uv = UV; draw_list_3d._VtxWritePtr[3].col = cols[3]; draw_list_3d._VtxWritePtr += 4; // Add indices for two triangles draw_list_3d._IdxWritePtr[0] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[1] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 1); draw_list_3d._IdxWritePtr[2] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[3] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx); draw_list_3d._IdxWritePtr[4] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 2); draw_list_3d._IdxWritePtr[5] = (ImDrawIdx)(draw_list_3d._VtxCurrentIdx + 3); draw_list_3d._IdxWritePtr += 6; // Add depth values for the two triangles draw_list_3d._ZWritePtr[0] = GetPointDepth((p_plot[0] + p_plot[1] + p_plot[2]) / 3.0f); draw_list_3d._ZWritePtr[1] = GetPointDepth((p_plot[0] + p_plot[2] + p_plot[3]) / 3.0f); draw_list_3d._ZWritePtr += 2; // Update vertex count draw_list_3d._VtxCurrentIdx += 4; return true; } const _Getter& Getter; mutable ImVec2 UV; mutable float Min; // Minimum value for the colormap mutable float Max; // Minimum value for the colormap const int XCount; const int YCount; const ImU32 Col; }; //----------------------------------------------------------------------------- // [SECTION] Indexers //----------------------------------------------------------------------------- template IMPLOT3D_INLINE T IndexData(const T* data, int idx, int count, int offset, int stride) { const int s = ((offset == 0) << 0) | ((stride == sizeof(T)) << 1); switch (s) { case 3: return data[idx]; case 2: return data[(offset + idx) % count]; case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx)) * stride); case 0: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((offset + idx) % count) * stride); default: return T(0); } } template struct IndexerIdx { IndexerIdx(const T* data, int count, int offset = 0, int stride = sizeof(T)) : Data(data), Count(count), Offset(offset), Stride(stride) {} template IMPLOT3D_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); } const T* Data; int Count; int Offset; int Stride; }; //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- template struct GetterXYZ { GetterXYZ(_IndexerX x, _IndexerY y, _IndexerZ z, int count) : IndexerX(x), IndexerY(y), IndexerZ(z), Count(count) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return ImPlot3DPoint(IndexerX(idx), IndexerY(idx), IndexerZ(idx)); } const _IndexerX IndexerX; const _IndexerY IndexerY; const _IndexerZ IndexerZ; const int Count; }; template struct GetterLoop { GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { idx = idx % (Count - 1); return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterTriangleLines { GetterTriangleLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { idx = ((idx % 6 + 1) / 2) % 3 + idx / 6 * 3; return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterQuadLines { GetterQuadLines(_Getter getter) : Getter(getter), Count(getter.Count * 2) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { idx = ((idx % 8 + 1) / 2) % 4 + idx / 8 * 4; return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterSurfaceLines { GetterSurfaceLines(_Getter getter, int x_count, int y_count) : Getter(getter), XCount(x_count), YCount(y_count) { int horizontal_segments = (XCount - 1) * YCount; int vertical_segments = (YCount - 1) * XCount; int segments = horizontal_segments + vertical_segments; Count = segments * 2; // Each segment has 2 endpoints } template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { // idx is an endpoint index int endpoint_i = (int)(idx % 2); int segment_i = (int)(idx / 2); int horizontal_segments = (XCount - 1) * YCount; int px, py; if (segment_i < horizontal_segments) { // Horizontal segment int row = segment_i / (XCount - 1); int col = segment_i % (XCount - 1); // Endpoint 0 is (col, row), endpoint 1 is (col+1, row) px = endpoint_i == 0 ? col : col + 1; py = row; } else { // Vertical segment int seg_v = segment_i - horizontal_segments; int col = seg_v / (YCount - 1); int row = seg_v % (YCount - 1); // Endpoint 0 is (col, row), endpoint 1 is (col, row+1) px = col; py = row + endpoint_i; } return Getter(py * XCount + px); } const _Getter Getter; int Count; const int XCount; const int YCount; }; struct Getter3DPoints { Getter3DPoints(const ImPlot3DPoint* points, int count) : Points(points), Count(count) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I idx) const { return Points[idx]; } const ImPlot3DPoint* Points; const int Count; }; struct GetterMeshTriangles { GetterMeshTriangles(const ImPlot3DPoint* vtx, const unsigned int* idx, int idx_count) : Vtx(vtx), Idx(idx), IdxCount(idx_count), TriCount(idx_count / 3), Count(idx_count) {} template IMPLOT3D_INLINE ImPlot3DPoint operator()(I i) const { unsigned int vi = Idx[i]; return Vtx[vi]; } const ImPlot3DPoint* Vtx; const unsigned int* Idx; int IdxCount; int TriCount; int Count; }; //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- /// Renders primitive shapes template