// MIT License // Copyright (c) 2020 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ImPlot v0.14 #include "implot.h" #include "implot_internal.h" //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f #ifndef IMPLOT_NO_FORCE_INLINE #ifdef _MSC_VER #define IMPLOT_INLINE __forceinline #elif defined(__GNUC__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #elif defined(__CLANG__) #if __has_attribute(__always_inline__) #define IMPLOT_INLINE inline __attribute__((__always_inline__)) #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #else #define IMPLOT_INLINE inline #endif #if defined __SSE__ || defined __x86_64__ || defined _M_X64 #ifndef IMGUI_ENABLE_SSE #include #endif static IMPLOT_INLINE float ImInvSqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else static IMPLOT_INLINE float ImInvSqrt(float x) { return 1.0f / sqrtf(x); } #endif #define IMPLOT_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImInvSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) // Support for pre-1.82 versions. Users on 1.82+ can use 0 (default) flags to mean "all corners" but in order to support older versions we are more explicit. #if (IMGUI_VERSION_NUM < 18102) && !defined(ImDrawFlags_RoundCornersAll) #define ImDrawFlags_RoundCornersAll ImDrawCornerFlags_All #endif //----------------------------------------------------------------------------- // [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 in two ways: // 1. Define IMPLOT_INSTANTIATE_ALL_NUMERIC_TYPES at compile time to add support for all known types. // 2. Or, define IMPLOT_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. // -DIMPLOT_CUSTOM_NUMERIC_TYPES="(float)(double)" #ifdef IMPLOT_CUSTOM_NUMERIC_TYPES #define IMPLOT_NUMERIC_TYPES IMPLOT_CUSTOM_NUMERIC_TYPES #elif defined(IMPLOT_INSTANTIATE_ALL_NUMERIC_TYPES) #define IMPLOT_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) #else #define IMPLOT_NUMERIC_TYPES (ImS8)(ImU8)(ImS16)(ImU16)(ImS32)(ImU32)(ImS64)(ImU64)(float)(double) #endif // CALL_INSTANTIATE_FOR_NUMERIC_TYPES will duplicate the template instantion 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(IMPLOT_NUMERIC_TYPES); namespace ImPlot { //----------------------------------------------------------------------------- // [SECTION] Utils //----------------------------------------------------------------------------- // Calc maximum index size of ImDrawIdx template struct MaxIdx { static const unsigned int Value; }; template <> const unsigned int MaxIdx::Value = 65535; template <> const unsigned int MaxIdx::Value = 4294967295; IMPLOT_INLINE void GetLineRenderProps(const ImDrawList& draw_list, float& half_weight, ImVec2& tex_uv0, ImVec2& tex_uv1) { const bool aa = ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLines) && ImHasFlag(draw_list.Flags, ImDrawListFlags_AntiAliasedLinesUseTex); if (aa) { ImVec4 tex_uvs = draw_list._Data->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._Data->TexUvWhitePixel; } } IMPLOT_INLINE void PrimLine(ImDrawList& draw_list, const ImVec2& P1, const ImVec2& P2, float half_weight, ImU32 col, const ImVec2& tex_uv0, const ImVec2 tex_uv1) { float dx = P2.x - P1.x; float dy = P2.y - P1.y; IMPLOT_NORMALIZE2F_OVER_ZERO(dx, dy); dx *= half_weight; dy *= half_weight; draw_list._VtxWritePtr[0].pos.x = P1.x + dy; draw_list._VtxWritePtr[0].pos.y = P1.y - dx; draw_list._VtxWritePtr[0].uv = tex_uv0; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = P2.x + dy; draw_list._VtxWritePtr[1].pos.y = P2.y - dx; draw_list._VtxWritePtr[1].uv = tex_uv0; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = P2.x - dy; draw_list._VtxWritePtr[2].pos.y = P2.y + dx; draw_list._VtxWritePtr[2].uv = tex_uv1; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = P1.x - dy; draw_list._VtxWritePtr[3].pos.y = P1.y + dx; draw_list._VtxWritePtr[3].uv = tex_uv1; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectFill(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos = Pmin; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos = Pmax; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmin.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr += 4; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 4; } IMPLOT_INLINE void PrimRectLine(ImDrawList& draw_list, const ImVec2& Pmin, const ImVec2& Pmax, float weight, ImU32 col, const ImVec2& uv) { draw_list._VtxWritePtr[0].pos.x = Pmin.x; draw_list._VtxWritePtr[0].pos.y = Pmin.y; draw_list._VtxWritePtr[0].uv = uv; draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos.x = Pmin.x; draw_list._VtxWritePtr[1].pos.y = Pmax.y; draw_list._VtxWritePtr[1].uv = uv; draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos.x = Pmax.x; draw_list._VtxWritePtr[2].pos.y = Pmax.y; draw_list._VtxWritePtr[2].uv = uv; draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos.x = Pmax.x; draw_list._VtxWritePtr[3].pos.y = Pmin.y; draw_list._VtxWritePtr[3].uv = uv; draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr[4].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[4].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[4].uv = uv; draw_list._VtxWritePtr[4].col = col; draw_list._VtxWritePtr[5].pos.x = Pmin.x + weight; draw_list._VtxWritePtr[5].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[5].uv = uv; draw_list._VtxWritePtr[5].col = col; draw_list._VtxWritePtr[6].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[6].pos.y = Pmax.y - weight; draw_list._VtxWritePtr[6].uv = uv; draw_list._VtxWritePtr[6].col = col; draw_list._VtxWritePtr[7].pos.x = Pmax.x - weight; draw_list._VtxWritePtr[7].pos.y = Pmin.y + weight; draw_list._VtxWritePtr[7].uv = uv; draw_list._VtxWritePtr[7].col = col; draw_list._VtxWritePtr += 8; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 5); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 6); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 0); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr += 3; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 7); draw_list._IdxWritePtr += 3; draw_list._VtxCurrentIdx += 8; } //----------------------------------------------------------------------------- // [SECTION] Item Utils //----------------------------------------------------------------------------- ImPlotItem* RegisterOrGetItem(const char* label_id, ImPlotItemFlags flags, bool* just_created) { ImPlotContext& gp = *GImPlot; ImPlotItemGroup& Items = *gp.CurrentItems; ImGuiID id = Items.GetItemID(label_id); if (just_created != NULL) *just_created = Items.GetItem(id) == NULL; ImPlotItem* item = Items.GetOrAddItem(id); if (item->SeenThisFrame) return item; item->SeenThisFrame = true; int idx = Items.GetItemIndex(item); item->ID = id; if (!ImHasFlag(flags, ImPlotItemFlags_NoLegend) && ImGui::FindRenderedTextEnd(label_id, NULL) != 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); } else { item->Show = true; } return item; } ImPlotItem* GetItem(const char* label_id) { ImPlotContext& gp = *GImPlot; return gp.CurrentItems->GetItem(label_id); } bool IsItemHidden(const char* label_id) { ImPlotItem* item = GetItem(label_id); return item != NULL && !item->Show; } ImPlotItem* GetCurrentItem() { ImPlotContext& gp = *GImPlot; return gp.CurrentItem; } void SetNextLineStyle(const ImVec4& col, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Line] = col; gp.NextItemData.LineWeight = weight; } void SetNextFillStyle(const ImVec4& col, float alpha) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_Fill] = col; gp.NextItemData.FillAlpha = alpha; } void SetNextMarkerStyle(ImPlotMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Marker = marker; gp.NextItemData.Colors[ImPlotCol_MarkerFill] = fill; gp.NextItemData.MarkerSize = size; gp.NextItemData.Colors[ImPlotCol_MarkerOutline] = outline; gp.NextItemData.MarkerWeight = weight; } void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImPlotContext& gp = *GImPlot; gp.NextItemData.Colors[ImPlotCol_ErrorBar] = col; gp.NextItemData.ErrorBarSize = size; gp.NextItemData.ErrorBarWeight = weight; } ImVec4 GetLastItemColor() { ImPlotContext& gp = *GImPlot; if (gp.PreviousItem) return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color); return ImVec4(); } void BustItemCache() { ImPlotContext& gp = *GImPlot; for (int p = 0; p < gp.Plots.GetBufSize(); ++p) { ImPlotPlot& plot = *gp.Plots.GetByIndex(p); plot.Items.Reset(); } for (int p = 0; p < gp.Subplots.GetBufSize(); ++p) { ImPlotSubplot& subplot = *gp.Subplots.GetByIndex(p); subplot.Items.Reset(); } } void BustColorCache(const char* plot_title_id) { ImPlotContext& gp = *GImPlot; if (plot_title_id == NULL) { BustItemCache(); } else { ImGuiID id = ImGui::GetCurrentWindow()->GetID(plot_title_id); ImPlotPlot* plot = gp.Plots.GetByKey(id); if (plot != NULL) plot->Items.Reset(); else { ImPlotSubplot* subplot = gp.Subplots.GetByKey(id); if (subplot != NULL) subplot->Items.Reset(); } } } //----------------------------------------------------------------------------- // [SECTION] BeginItem / EndItem //----------------------------------------------------------------------------- static const float ITEM_HIGHLIGHT_LINE_SCALE = 2.0f; static const float ITEM_HIGHLIGHT_MARK_SCALE = 1.25f; // Begins a new item. Returns false if the item should not be plotted. bool BeginItem(const char* label_id, ImPlotItemFlags flags, ImPlotCol recolor_from) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotX() needs to be called between BeginPlot() and EndPlot()!"); SetupLock(); bool just_created; ImPlotItem* item = RegisterOrGetItem(label_id, flags, &just_created); // set current item gp.CurrentItem = item; ImPlotNextItemData& s = gp.NextItemData; // set/override item color if (recolor_from != -1) { if (!IsColorAuto(s.Colors[recolor_from])) item->Color = ImGui::ColorConvertFloat4ToU32(s.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(); } // hide/show item if (gp.NextItemData.HasHidden) { if (just_created || gp.NextItemData.HiddenCond == ImGuiCond_Always) item->Show = !gp.NextItemData.Hidden; } if (!item->Show) { // reset next item data gp.NextItemData.Reset(); gp.PreviousItem = item; gp.CurrentItem = NULL; return false; } else { ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); // stage next item colors s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item_color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item_color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; s.Colors[ImPlotCol_MarkerOutline] = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline]; s.Colors[ImPlotCol_MarkerFill] = IsColorAuto(s.Colors[ImPlotCol_MarkerFill]) ? ( IsColorAuto(ImPlotCol_MarkerFill) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerFill] ) : s.Colors[ImPlotCol_MarkerFill]; s.Colors[ImPlotCol_ErrorBar] = IsColorAuto(s.Colors[ImPlotCol_ErrorBar]) ? ( GetStyleColorVec4(ImPlotCol_ErrorBar) ) : s.Colors[ImPlotCol_ErrorBar]; // stage next item style vars s.LineWeight = s.LineWeight < 0 ? gp.Style.LineWeight : s.LineWeight; s.Marker = s.Marker < 0 ? gp.Style.Marker : s.Marker; s.MarkerSize = s.MarkerSize < 0 ? gp.Style.MarkerSize : s.MarkerSize; s.MarkerWeight = s.MarkerWeight < 0 ? gp.Style.MarkerWeight : s.MarkerWeight; s.FillAlpha = s.FillAlpha < 0 ? gp.Style.FillAlpha : s.FillAlpha; s.ErrorBarSize = s.ErrorBarSize < 0 ? gp.Style.ErrorBarSize : s.ErrorBarSize; s.ErrorBarWeight = s.ErrorBarWeight < 0 ? gp.Style.ErrorBarWeight : s.ErrorBarWeight; s.DigitalBitHeight = s.DigitalBitHeight < 0 ? gp.Style.DigitalBitHeight : s.DigitalBitHeight; s.DigitalBitGap = s.DigitalBitGap < 0 ? gp.Style.DigitalBitGap : s.DigitalBitGap; // apply alpha modifier(s) s.Colors[ImPlotCol_Fill].w *= s.FillAlpha; s.Colors[ImPlotCol_MarkerFill].w *= s.FillAlpha; // TODO: this should be separate, if it at all // apply highlight mods if (item->LegendHovered) { if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightItem)) { s.LineWeight *= ITEM_HIGHLIGHT_LINE_SCALE; s.MarkerSize *= ITEM_HIGHLIGHT_MARK_SCALE; s.MarkerWeight *= ITEM_HIGHLIGHT_LINE_SCALE; // TODO: how to highlight fills? } if (!ImHasFlag(gp.CurrentItems->Legend.Flags, ImPlotLegendFlags_NoHighlightAxis)) { if (gp.CurrentPlot->EnabledAxesX() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentX].ColorHiLi = item->Color; if (gp.CurrentPlot->EnabledAxesY() > 1) gp.CurrentPlot->Axes[gp.CurrentPlot->CurrentY].ColorHiLi = item->Color; } } // set render flags s.RenderLine = s.Colors[ImPlotCol_Line].w > 0 && s.LineWeight > 0; s.RenderFill = s.Colors[ImPlotCol_Fill].w > 0; s.RenderMarkerFill = s.Colors[ImPlotCol_MarkerFill].w > 0; s.RenderMarkerLine = s.Colors[ImPlotCol_MarkerOutline].w > 0 && s.MarkerWeight > 0; // push rendering clip rect PushPlotClipRect(); return true; } } // Ends an item (call only if BeginItem returns true) void EndItem() { ImPlotContext& gp = *GImPlot; // pop rendering clip rect PopPlotClipRect(); // reset next item data gp.NextItemData.Reset(); // set current item gp.PreviousItem = gp.CurrentItem; gp.CurrentItem = NULL; } //----------------------------------------------------------------------------- // [SECTION] Indexers //----------------------------------------------------------------------------- template IMPLOT_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(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE double operator()(I idx) const { return (double)IndexData(Data, idx, Count, Offset, Stride); } const T* Data; int Count; int Offset; int Stride; }; template struct IndexerAdd { IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) : Indexer1(indexer1), Indexer2(indexer2), Scale1(scale1), Scale2(scale2), Count(ImMin(Indexer1.Count, Indexer2.Count)) { } template IMPLOT_INLINE double operator()(I idx) const { return Scale1 * Indexer1(idx) + Scale2 * Indexer2(idx); } const _Indexer1& Indexer1; const _Indexer2& Indexer2; double Scale1; double Scale2; int Count; }; struct IndexerLin { IndexerLin(double m, double b) : M(m), B(b) { } template IMPLOT_INLINE double operator()(I idx) const { return M * idx + B; } const double M; const double B; }; struct IndexerConst { IndexerConst(double ref) : Ref(ref) { } template IMPLOT_INLINE double operator()(I) const { return Ref; } const double Ref; }; //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- template struct GetterXY { GetterXY(_IndexerX x, _IndexerY y, int count) : IndxerX(x), IndxerY(y), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return ImPlotPoint(IndxerX(idx),IndxerY(idx)); } const _IndexerX IndxerX; const _IndexerY IndxerY; const int Count; }; /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : Getter(getter), Data(data), Count(count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { return Getter(idx, Data); } ImPlotGetter Getter; void* const Data; const int Count; }; template struct GetterOverrideX { GetterOverrideX(_Getter getter, double x) : Getter(getter), X(x), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.x = X; return p; } const _Getter Getter; const double X; const int Count; }; template struct GetterOverrideY { GetterOverrideY(_Getter getter, double y) : Getter(getter), Y(y), Count(getter.Count) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { ImPlotPoint p = Getter(idx); p.y = Y; return p; } const _Getter Getter; const double Y; const int Count; }; template struct GetterLoop { GetterLoop(_Getter getter) : Getter(getter), Count(getter.Count + 1) { } template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { idx = idx % (Count - 1); return Getter(idx); } const _Getter Getter; const int Count; }; template struct GetterError { GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) : Xs(xs), Ys(ys), Neg(neg), Pos(pos), Count(count), Offset(count ? ImPosMod(offset, count) : 0), Stride(stride) { } template IMPLOT_INLINE ImPlotPointError operator()(I idx) const { return ImPlotPointError((double)IndexData(Xs, idx, Count, Offset, Stride), (double)IndexData(Ys, idx, Count, Offset, Stride), (double)IndexData(Neg, idx, Count, Offset, Stride), (double)IndexData(Pos, idx, Count, Offset, Stride)); } const T* const Xs; const T* const Ys; const T* const Neg; const T* const Pos; const int Count; const int Offset; const int Stride; }; //----------------------------------------------------------------------------- // [SECTION] Fitters //----------------------------------------------------------------------------- template struct Fitter1 { Fitter1(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter; }; template struct FitterX { FitterX(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis&) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); x_axis.ExtendFit(p.x); } } const _Getter1& Getter; }; template struct FitterY { FitterY(const _Getter1& getter) : Getter(getter) { } void Fit(ImPlotAxis&, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter.Count; ++i) { ImPlotPoint p = Getter(i); y_axis.ExtendFit(p.y); } } const _Getter1& Getter; }; template struct Fitter2 { Fitter2(const _Getter1& getter1, const _Getter2& getter2) : Getter1(getter1), Getter2(getter2) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { for (int i = 0; i < Getter1.Count; ++i) { ImPlotPoint p = Getter1(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } for (int i = 0; i < Getter2.Count; ++i) { ImPlotPoint p = Getter2(i); x_axis.ExtendFitWith(y_axis, p.x, p.y); y_axis.ExtendFitWith(x_axis, p.y, p.x); } } const _Getter1& Getter1; const _Getter2& Getter2; }; template struct FitterBarV { FitterBarV(const _Getter1& getter1, const _Getter2& getter2, double width) : Getter1(getter1), Getter2(getter2), HalfWidth(width*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.x -= HalfWidth; ImPlotPoint p2 = Getter2(i); p2.x += HalfWidth; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfWidth; }; template struct FitterBarH { FitterBarH(const _Getter1& getter1, const _Getter2& getter2, double height) : Getter1(getter1), Getter2(getter2), HalfHeight(height*0.5) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { int count = ImMin(Getter1.Count, Getter2.Count); for (int i = 0; i < count; ++i) { ImPlotPoint p1 = Getter1(i); p1.y -= HalfHeight; ImPlotPoint p2 = Getter2(i); p2.y += HalfHeight; x_axis.ExtendFitWith(y_axis, p1.x, p1.y); y_axis.ExtendFitWith(x_axis, p1.y, p1.x); x_axis.ExtendFitWith(y_axis, p2.x, p2.y); y_axis.ExtendFitWith(x_axis, p2.y, p2.x); } } const _Getter1& Getter1; const _Getter2& Getter2; const double HalfHeight; }; struct FitterRect { FitterRect(const ImPlotPoint& pmin, const ImPlotPoint& pmax) : Pmin(pmin), Pmax(pmax) { } FitterRect(const ImPlotRect& rect) : FitterRect(rect.Min(), rect.Max()) { } void Fit(ImPlotAxis& x_axis, ImPlotAxis& y_axis) const { x_axis.ExtendFitWith(y_axis, Pmin.x, Pmin.y); y_axis.ExtendFitWith(x_axis, Pmin.y, Pmin.x); x_axis.ExtendFitWith(y_axis, Pmax.x, Pmax.y); y_axis.ExtendFitWith(x_axis, Pmax.y, Pmax.x); } const ImPlotPoint Pmin; const ImPlotPoint Pmax; }; //----------------------------------------------------------------------------- // [SECTION] Transformers //----------------------------------------------------------------------------- struct Transformer1 { Transformer1(double pixMin, double pltMin, double pltMax, double m, double scaMin, double scaMax, ImPlotTransform fwd, void* data) : ScaMin(scaMin), ScaMax(scaMax), PltMin(pltMin), PltMax(pltMax), PixMin(pixMin), M(m), TransformFwd(fwd), TransformData(data) { } template IMPLOT_INLINE float operator()(T p) const { if (TransformFwd != NULL) { double s = TransformFwd(p, TransformData); double t = (s - ScaMin) / (ScaMax - ScaMin); p = PltMin + (PltMax - PltMin) * t; } return (float)(PixMin + M * (p - PltMin)); } double ScaMin, ScaMax, PltMin, PltMax, PixMin, M; ImPlotTransform TransformFwd; void* TransformData; }; struct Transformer2 { Transformer2(const ImPlotAxis& x_axis, const ImPlotAxis& y_axis) : Tx(x_axis.PixelMin, x_axis.Range.Min, x_axis.Range.Max, x_axis.ScaleToPixel, x_axis.ScaleMin, x_axis.ScaleMax, x_axis.TransformForward, x_axis.TransformData), Ty(y_axis.PixelMin, y_axis.Range.Min, y_axis.Range.Max, y_axis.ScaleToPixel, y_axis.ScaleMin, y_axis.ScaleMax, y_axis.TransformForward, y_axis.TransformData) { } Transformer2(const ImPlotPlot& plot) : Transformer2(plot.Axes[plot.CurrentX], plot.Axes[plot.CurrentY]) { } Transformer2() : Transformer2(*GImPlot->CurrentPlot) { } template IMPLOT_INLINE ImVec2 operator()(const P& plt) const { ImVec2 out; out.x = Tx(plt.x); out.y = Ty(plt.y); return out; } template IMPLOT_INLINE ImVec2 operator()(T x, T y) const { ImVec2 out; out.x = Tx(x); out.y = Ty(y); return out; } Transformer1 Tx; Transformer1 Ty; }; //----------------------------------------------------------------------------- // [SECTION] Renderers //----------------------------------------------------------------------------- struct RendererBase { RendererBase(int prims, int idx_consumed, int vtx_consumed) : Prims(prims), IdxConsumed(idx_consumed), VtxConsumed(vtx_consumed) { } const int Prims; Transformer2 Transformer; const int IdxConsumed; const int VtxConsumed; }; 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) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; 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) { P1 = this->Transformer(Getter(0)); } void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return false; } PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments1 : RendererBase { RendererLineSegments1(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(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter(prim*2+0)); ImVec2 P2 = this->Transformer(Getter(prim*2+1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererLineSegments2 : RendererBase { RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) {} void Init(ImDrawList& draw_list) const { GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P1 = this->Transformer(Getter1(prim)); ImVec2 P2 = this->Transformer(Getter2(prim)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; template struct RendererBarsFillV : RendererBase { RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2) {} 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 { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; mutable ImVec2 UV; }; template struct RendererBarsFillH : RendererBase { RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2) {} 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 { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectFill(draw_list,PMin,PMax,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; mutable ImVec2 UV; }; template struct RendererBarsLineV : RendererBase { RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfWidth(width/2), Weight(weight) {} 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 { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.x += HalfWidth; p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; P2.x += P2.x > P1.x ? (1-width_px) / 2 : (width_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfWidth; const float Weight; mutable ImVec2 UV; }; template struct RendererBarsLineH : RendererBase { RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), Col(col), HalfHeight(height/2), Weight(weight) {} 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 { ImPlotPoint p1 = Getter1(prim); ImPlotPoint p2 = Getter2(prim); p1.y += HalfHeight; p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; P2.y += P2.y > P1.y ? (1-height_px) / 2 : (height_px-1) / 2; } ImVec2 PMin = ImMin(P1, P2); ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; const double HalfHeight; const float Weight; mutable ImVec2 UV; }; template struct RendererStairsPre : RendererBase { RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); } 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 { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPost : RendererBase { RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight) * 0.5f) { P1 = this->Transformer(Getter(0)); } 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 { ImVec2 P2 = this->Transformer(Getter(prim + 1)); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPreShaded : RendererBase { RendererStairsPreShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } 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 { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererStairsPostShaded : RendererBase { RendererStairsPostShaded(const _Getter& getter, ImU32 col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } 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 { ImVec2 P2 = this->Transformer(Getter(prim + 1)); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; return false; } PrimRectFill(draw_list, PMin, PMax, Col, UV); P1 = P2; return true; } const _Getter& Getter; const ImU32 Col; float Y0; mutable ImVec2 P1; mutable ImVec2 UV; }; template struct RendererShaded : RendererBase { RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), Col(col) { P11 = this->Transformer(Getter1(0)); P12 = this->Transformer(Getter2(0)); } 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 { ImVec2 P21 = this->Transformer(Getter1(prim+1)); ImVec2 P22 = this->Transformer(Getter2(prim+1)); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); if (!cull_rect.Overlaps(rect)) { P11 = P21; P12 = P22; return false; } const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); ImVec2 intersection = Intersection(P11,P21,P12,P22); draw_list._VtxWritePtr[0].pos = P11; draw_list._VtxWritePtr[0].uv = UV; draw_list._VtxWritePtr[0].col = Col; draw_list._VtxWritePtr[1].pos = P21; draw_list._VtxWritePtr[1].uv = UV; draw_list._VtxWritePtr[1].col = Col; draw_list._VtxWritePtr[2].pos = intersection; draw_list._VtxWritePtr[2].uv = UV; draw_list._VtxWritePtr[2].col = Col; draw_list._VtxWritePtr[3].pos = P12; draw_list._VtxWritePtr[3].uv = UV; draw_list._VtxWritePtr[3].col = Col; draw_list._VtxWritePtr[4].pos = P22; draw_list._VtxWritePtr[4].uv = UV; draw_list._VtxWritePtr[4].col = Col; draw_list._VtxWritePtr += 5; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 4); draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3 - intersect); draw_list._IdxWritePtr += 6; draw_list._VtxCurrentIdx += 5; P11 = P21; P12 = P22; return true; } const _Getter1& Getter1; const _Getter2& Getter2; const ImU32 Col; mutable ImVec2 P11; mutable ImVec2 P12; mutable ImVec2 UV; }; struct RectC { ImPlotPoint Pos; ImPlotPoint HalfSize; ImU32 Color; }; template struct RendererRectC : RendererBase { RendererRectC(const _Getter& getter) : RendererBase(getter.Count, 6, 4), Getter(getter) {} 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 { RectC rect = Getter(prim); ImVec2 P1 = this->Transformer(rect.Pos.x - rect.HalfSize.x , rect.Pos.y - rect.HalfSize.y); ImVec2 P2 = this->Transformer(rect.Pos.x + rect.HalfSize.x , rect.Pos.y + rect.HalfSize.y); if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; PrimRectFill(draw_list,P1,P2,rect.Color,UV); return true; } const _Getter& Getter; mutable ImVec2 UV; }; //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- /// Renders primitive shapes in bulk as efficiently as possible. template void RenderPrimitivesEx(const _Renderer& renderer, ImDrawList& draw_list, const ImRect& cull_rect) { unsigned int prims = renderer.Prims; unsigned int prims_culled = 0; unsigned int idx = 0; renderer.Init(draw_list); while (prims) { // find how many can be reserved up to end of current draw command's limit unsigned int cnt = ImMin(prims, (MaxIdx::Value - draw_list._VtxCurrentIdx) / renderer.VtxConsumed); // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time if (cnt >= ImMin(64u, prims)) { if (prims_culled >= cnt) prims_culled -= cnt; // reuse previous reservation else { // add more elements to previous reservation draw_list.PrimReserve((cnt - prims_culled) * renderer.IdxConsumed, (cnt - prims_culled) * renderer.VtxConsumed); prims_culled = 0; } } else { if (prims_culled > 0) { draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); prims_culled = 0; } cnt = ImMin(prims, (MaxIdx::Value - 0/*draw_list._VtxCurrentIdx*/) / renderer.VtxConsumed); // reserve new draw command draw_list.PrimReserve(cnt * renderer.IdxConsumed, cnt * renderer.VtxConsumed); } prims -= cnt; for (unsigned int ie = idx + cnt; idx != ie; ++idx) { if (!renderer.Render(draw_list, cull_rect, idx)) prims_culled++; } } if (prims_culled > 0) draw_list.PrimUnreserve(prims_culled * renderer.IdxConsumed, prims_culled * renderer.VtxConsumed); } template