From 948cbe0a9ccc0fa4680e9b44d125dfa5fac380a6 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 2 Feb 2025 13:08:08 +0100 Subject: [PATCH] build: Updated dependencies --- lib/third_party/fmt | 2 +- lib/third_party/imgui/imgui/include/imgui.h | 86 ++-- .../imgui/imgui/include/imgui_internal.h | 35 +- .../include/misc/freetype/imgui_freetype.h | 7 + lib/third_party/imgui/imgui/source/imgui.cpp | 223 +++++---- .../imgui/imgui/source/imgui_demo.cpp | 78 ++-- .../imgui/imgui/source/imgui_draw.cpp | 83 ++-- .../imgui/imgui/source/imgui_tables.cpp | 10 +- .../imgui/imgui/source/imgui_widgets.cpp | 236 ++++++---- .../source/misc/freetype/imgui_freetype.cpp | 63 ++- .../imgui/implot/source/implot.cpp | 6 + .../imgui/implot3d/include/implot3d.h | 114 +++-- .../implot3d/include/implot3d_internal.h | 80 +++- .../imgui/implot3d/source/implot3d.cpp | 437 ++++++++++++------ .../imgui/implot3d/source/implot3d_demo.cpp | 151 +++++- .../imgui/implot3d/source/implot3d_items.cpp | 42 +- .../source/content/data_visualizers.cpp | 6 +- 17 files changed, 1088 insertions(+), 571 deletions(-) diff --git a/lib/third_party/fmt b/lib/third_party/fmt index 8303d140a..9cf9f38ed 160000 --- a/lib/third_party/fmt +++ b/lib/third_party/fmt @@ -1 +1 @@ -Subproject commit 8303d140a1a11f19b982a9f664bbe59a1ccda3f4 +Subproject commit 9cf9f38eded63e5e0fb95cd536ba51be601d7fa2 diff --git a/lib/third_party/imgui/imgui/include/imgui.h b/lib/third_party/imgui/imgui/include/imgui.h index 4734c180d..ca650be4f 100644 --- a/lib/third_party/imgui/imgui/include/imgui.h +++ b/lib/third_party/imgui/imgui/include/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (headers) // Help: @@ -28,8 +28,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.7" -#define IMGUI_VERSION_NUM 19170 +#define IMGUI_VERSION "1.91.8" +#define IMGUI_VERSION_NUM 19180 #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_DOCK // Docking WIP branch @@ -160,7 +160,7 @@ typedef unsigned int ImU32; // 32-bit unsigned integer (often used to st typedef signed long long ImS64; // 64-bit signed integer typedef unsigned long long ImU64; // 64-bit unsigned integer -// Forward declarations +// Forward declarations: ImDrawList, ImFontAtlas layer struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit() struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) struct ImDrawData; // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix. @@ -175,6 +175,8 @@ struct ImFontConfig; // Configuration data when adding a font or struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) + +// Forward declarations: ImGui layer struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO) struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) @@ -1037,6 +1039,7 @@ namespace ImGui IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay); // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available @@ -1129,12 +1132,12 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] + ImGuiWindowFlags_DockNodeHost = 1 << 23, // Don't use! For internal use by Begin()/NewFrame() ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() - ImGuiWindowFlags_DockNodeHost = 1 << 29, // Don't use! For internal use by Begin()/NewFrame() // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1829,10 +1832,16 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) + // Alpha preview + // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview. + // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + // - The new flags may be combined better and allow finer controls. + ImGuiColorEditFlags_AlphaOpaque = 1 << 11, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. + ImGuiColorEditFlags_AlphaNoBg = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. + ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. + // User Options (right-click on widget to change some of them). ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. - ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " @@ -1849,12 +1858,16 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks + ImGuiColorEditFlags_AlphaMask_ = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf, ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV, // Obsolete names +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiColorEditFlags_AlphaPreview = 0, // [Removed in 1.91.8] This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set. +#endif //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] }; @@ -2499,6 +2512,7 @@ struct ImGuiIO ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down + double MouseReleasedTime[5]; // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click). bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. @@ -3042,7 +3056,7 @@ struct ImGuiSelectionExternalStorage // The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking. #ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX -#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63) +#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (32) #endif // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] @@ -3116,7 +3130,6 @@ struct ImDrawChannel ImVector _IdxBuffer; }; - // Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order. // This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call. struct ImDrawListSplitter @@ -3387,26 +3400,27 @@ struct ImFontAtlasShadowTexConfig int CalcConvexTexHeight() const; // The height of the texture area required for the convex shape shadow texture. }; +// A font input/source (we may rename this to ImFontSource in the future) struct ImFontConfig { void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). - int FontNo; // 0 // Index of font within TTF/OTF file - float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 2 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. + int FontNo; // 0 // Index of font within TTF/OTF file + int OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. + float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. const ImWchar* GlyphRanges; // NULL // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. float RasterizerDensity; // 1.0f // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered. - ImWchar EllipsisChar; // 0 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. + ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. // [Internal] char Name[40]; // Name (strictly to ease debugging) @@ -3496,8 +3510,8 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. + IMGUI_API void ClearFonts(); // Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. - IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). IMGUI_API void Clear(); // Clear all input and output. // Build atlas, retrieve pixel data. @@ -3530,7 +3544,7 @@ struct ImFontAtlas IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietnamese characters //------------------------------------------- - // [BETA] Custom Rectangles/Glyphs API + // [ALPHA] Custom Rectangles/Glyphs API //------------------------------------------- // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. @@ -3556,11 +3570,11 @@ struct ImFontAtlas ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // FIXME: Should be called "TexPackPadding". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). - bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. void* UserData; // Store your own atlas related user-data (if e.g. you have multiple font atlas). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. + bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. bool TexReady; // Set when texture was built matching current font input bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight @@ -3597,39 +3611,39 @@ struct ImFontAtlas struct ImFont { // [Internal] Members: Hot ~20/24 bytes (for CalcTextSize) - ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). + ImVector IndexAdvanceX; // 12-16 // out // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX - float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) + float FontSize; // 4 // in // Height of characters/line, set during loading (don't change after loading) // [Internal] Members: Hot ~28/40 bytes (for RenderText loop) - ImVector IndexLookup; // 12-16 // out // // Sparse. Index glyphs by Unicode code-point. - ImVector Glyphs; // 12-16 // out // // All glyphs. + ImVector IndexLookup; // 12-16 // out // Sparse. Index glyphs by Unicode code-point. + ImVector Glyphs; // 12-16 // out // All glyphs. const ImFontGlyph* FallbackGlyph; // 4-8 // out // = FindGlyph(FontFallbackChar) // [Internal] Members: Cold ~32/40 bytes // Conceptually ConfigData[] is the list of font sources merged to create this font. - ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into - const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances - short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImFontAtlas* ContainerAtlas; // 4-8 // out // What we has been loaded into + const ImFontConfig* ConfigData; // 4-8 // in // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances + short ConfigDataCount; // 2 // in // Number of ImFontConfig involved in creating this font. Usually 1, or >1 when merging multiple font sources into one ImFont. short EllipsisCharCount; // 1 // out // 1 or 3 - ImWchar EllipsisChar; // 2-4 // out // = '...'/'.'// Character used for ellipsis rendering. - ImWchar FallbackChar; // 2-4 // out // = FFFD/'?' // Character used if a glyph isn't found. - float EllipsisWidth; // 4 // out // Width - float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0 + ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). + ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?') + float EllipsisWidth; // 4 // out // Total ellipsis Width + float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0 + float Scale; // 4 // in // Base font scale (1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale() + float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) + int MetricsTotalSurface;// 4 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) bool DirtyLookupTables; // 1 // out // - float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() - float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) - int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) - ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. + ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c); IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c); - float GetCharAdvance(ImWchar c) { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } - bool IsLoaded() const { return ContainerAtlas != NULL; } - const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } + float GetCharAdvance(ImWchar c) { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } + bool IsLoaded() const { return ContainerAtlas != NULL; } + const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. diff --git a/lib/third_party/imgui/imgui/include/imgui_internal.h b/lib/third_party/imgui/imgui/include/imgui_internal.h index ebf7e5371..9f97b006a 100644 --- a/lib/third_party/imgui/imgui/include/imgui_internal.h +++ b/lib/third_party/imgui/imgui/include/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -130,10 +130,17 @@ Index of this file: // [SECTION] Forward declarations //----------------------------------------------------------------------------- +// Utilities +// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImPool<>, ImChunkStream<>) struct ImBitVector; // Store 1-bit per value struct ImRect; // An axis-aligned rectangle (2 points) +struct ImGuiTextIndex; // Maintain a line index for a text buffer. + +// ImDrawList/ImFontAtlas struct ImDrawDataBuilder; // Helper to build a ImDrawData instance struct ImDrawListSharedData; // Data shared between all ImDrawList instances + +// ImGui struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others) struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiContext; // Main Dear ImGui context @@ -232,7 +239,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. -#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0) +#define IMGUI_DEBUG_LOG_ERROR(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g.DebugLogSkippedErrors++; } while (0) #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) @@ -474,7 +481,7 @@ static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * (T)t); } +template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } @@ -749,6 +756,7 @@ struct ImGuiTextIndex // Helper: ImGuiStorage IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key); + //----------------------------------------------------------------------------- // [SECTION] ImDrawList support //----------------------------------------------------------------------------- @@ -2666,6 +2674,12 @@ struct IMGUI_API ImGuiWindowTempData ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() ImU32 ModalDimBgColor; + // Status flags + ImGuiItemStatusFlags WindowItemStatusFlags; + ImGuiItemStatusFlags ChildItemStatusFlags; + ImGuiItemStatusFlags DockTabItemStatusFlags; + ImRect DockTabItemRect; + // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). @@ -2768,7 +2782,9 @@ struct IMGUI_API ImGuiWindow ImGuiStorage StateStorage; ImVector ColumnsStorage; float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() + float FontWindowScaleParents; float FontDpiScale; + float FontRefSize; // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window. int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) @@ -2802,8 +2818,6 @@ struct IMGUI_API ImGuiWindow ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more - ImGuiItemStatusFlags DockTabItemStatusFlags; - ImRect DockTabItemRect; public: ImGuiWindow(ImGuiContext* context, const char* name); @@ -2817,7 +2831,7 @@ public: // We don't use g.FontSize because the window may be != g.CurrentWindow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - float CalcFontSize() const { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale * FontDpiScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } + float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontBaseSize * FontWindowScale * FontDpiScale * FontWindowScaleParents; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } }; @@ -2840,7 +2854,8 @@ enum ImGuiTabItemFlagsPrivate_ ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button - ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. + ImGuiTabItemFlags_Invisible = 1 << 22, // To reserve space e.g. with ImGuiTabItemFlags_Leading + ImGuiTabItemFlags_Unsorted = 1 << 23, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. }; // Storage for one active tab item (sizeof() 48 bytes) @@ -3105,6 +3120,7 @@ struct IMGUI_API ImGuiTable ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; + ImS8 NavLayer; // ImGuiNavLayer at the time of BeginTable(). bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). bool IsInitializing; @@ -3247,6 +3263,7 @@ namespace ImGui // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } + IMGUI_API void PushPasswordFont(); inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); } IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); @@ -3332,7 +3349,7 @@ namespace ImGui IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); - IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); @@ -3670,6 +3687,7 @@ namespace ImGui IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API void TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); @@ -3869,6 +3887,7 @@ IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); +IMGUI_API void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* cfg, int* out_oversample_h, int* out_oversample_v); //----------------------------------------------------------------------------- // [SECTION] Test Engine specific hooks (imgui_test_engine) diff --git a/lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h b/lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h index b4e1d4893..6572b1547 100644 --- a/lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h +++ b/lib/third_party/imgui/imgui/include/misc/freetype/imgui_freetype.h @@ -5,6 +5,13 @@ #include "imgui.h" // IMGUI_API #ifndef IMGUI_DISABLE +// Usage: +// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to enable support for imgui_freetype in imgui. + +// Optional support for OpenType SVG fonts: +// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927. +// - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591. + // Forward declarations struct ImFontAtlas; struct ImFontBuilderIO; diff --git a/lib/third_party/imgui/imgui/source/imgui.cpp b/lib/third_party/imgui/imgui/source/imgui.cpp index 186b2bbd8..e7c8d2c45 100644 --- a/lib/third_party/imgui/imgui/source/imgui.cpp +++ b/lib/third_party/imgui/imgui/source/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (main code and documentation) // Help: @@ -438,6 +438,9 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. + - 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior. + prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls: - 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937) - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions. As a result, old .ini data may be partially lost (docking and tables information particularly). @@ -1268,6 +1271,7 @@ static void RenderWindowTitleBarContents(ImGuiWindow* window, const static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect); +static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. @@ -1360,11 +1364,11 @@ ImGuiStyle::ImGuiStyle() GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. - TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. - TabBarOverlineSize = 2.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. + TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. @@ -4381,7 +4385,8 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL LastFrameActive = -1; LastFrameJustFocused = -1; LastTimeActive = -1.0f; - FontWindowScale = FontDpiScale = 1.0f; + FontRefSize = 0.0f; + FontWindowScale = FontWindowScaleParents = FontDpiScale = 1.0f; SettingsOffset = -1; DockOrder = -1; DrawList = &DrawListInst; @@ -4803,15 +4808,30 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect. -void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; g.LastItemData.ID = item_id; - g.LastItemData.ItemFlags = in_flags; - g.LastItemData.StatusFlags = item_flags; + g.LastItemData.ItemFlags = item_flags; + g.LastItemData.StatusFlags = status_flags; g.LastItemData.Rect = g.LastItemData.NavRect = item_rect; } +static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + if (window->DockIsActive) + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.DockTabItemStatusFlags, window->DC.DockTabItemRect); + else + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect); +} + +static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect); +} + float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) @@ -5274,6 +5294,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() } // Called once a frame. Followed by SetCurrentFont() which sets up the remaining data. +// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! static void SetupDrawListSharedData() { ImGuiContext& g = *GImGui; @@ -6434,7 +6455,14 @@ void ImGui::EndChild() } if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags; + //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd() } + else + { + SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); + } + g.WithinEndChildID = backup_within_end_child_id; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } @@ -7033,7 +7061,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { float y = window->Pos.y + window->TitleBarHeight - 1; - window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize); + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize); } } @@ -7135,9 +7163,9 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); + window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock @@ -7177,9 +7205,10 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar continue; const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + const float border_inner = IM_ROUND(window_border_size * 0.5f); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12); window->DrawList->PathFillConvex(col); } } @@ -7487,6 +7516,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId); IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId. } + + // Inherit SetWindowFontScale() from parent until we fix this system... + window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f; } // Add to focus scope stack @@ -7664,6 +7696,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f; window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f; + window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window. // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible. // Those flags will be altered further down in the function depending on more conditions. @@ -8159,6 +8192,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. + window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None; + window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; SetLastItemDataForWindow(window, title_bar_rect); // [DEBUG] @@ -8287,15 +8322,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) return !window->SkipItems; } -static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - if (window->DockIsActive) - SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect); - else - SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect); -} - void ImGui::End() { ImGuiContext& g = *GImGui; @@ -9849,6 +9875,17 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } +// Use if you absolutely need to distinguish single-click from double-click by introducing a delay. +// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test. +// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. +bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]); + return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); +} + bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; @@ -10124,6 +10161,8 @@ static void ImGui::UpdateMouseInputs() io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; io.MouseClickedCount[i] = 0; // Will be filled below io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + if (io.MouseReleased[i]) + io.MouseReleasedTime[i] = g.Time; io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; if (io.MouseClicked[i]) @@ -10295,7 +10334,7 @@ void ImGui::UpdateMouseWheel() { LockWheelingWindow(window, wheel.x); float max_step = window->InnerRect.GetWidth() * 0.67f; - float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(2 * window->FontRefSize, max_step)); SetScrollX(window, window->Scroll.x - wheel.x * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } @@ -10303,7 +10342,7 @@ void ImGui::UpdateMouseWheel() { LockWheelingWindow(window, wheel.y); float max_step = window->InnerRect.GetHeight() * 0.67f; - float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(5 * window->FontRefSize, max_step)); SetScrollY(window, window->Scroll.y - wheel.y * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } @@ -10956,6 +10995,11 @@ void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()"); EndMultiSelect(); } + if (window->DC.MenuBarAppending) //-V1044 + { + IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()"); + EndMenuBar(); + } while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing TreePop()"); @@ -13709,7 +13753,7 @@ static void ImGui::NavUpdate() { // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; - const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. const ImGuiDir move_dir = g.NavMoveDir; if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None) { @@ -13899,8 +13943,8 @@ void ImGui::NavUpdateCreateMoveRequest() if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); - float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); - float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item + float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f); + float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; @@ -14158,7 +14202,7 @@ static float ImGui::NavUpdatePageUpPageDown() else { ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; if (IsKeyPressed(ImGuiKey_PageUp, true)) { @@ -14336,7 +14380,7 @@ static void ImGui::NavUpdateWindowing() // Start CTRL+Tab or Square+L/R window selection // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab) - const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing"); + const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); @@ -14546,12 +14590,12 @@ void ImGui::NavUpdateWindowingOverlay() return; if (g.NavWindowingListWindow == NULL) - g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); + g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay"); const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); - Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); if (g.ContextName[0] != 0) SeparatorText(g.ContextName); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -18520,8 +18564,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->VisibleWindow = window; // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call - window->DockTabItemStatusFlags = g.LastItemData.StatusFlags; - window->DockTabItemRect = g.LastItemData.Rect; + window->DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags; + window->DC.DockTabItemRect = g.LastItemData.Rect; // Update navigation ID on menu layer if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) @@ -21986,18 +22030,24 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co // [DEBUG] Display details for a single font, called by ShowStyleEditor(). void ImGui::DebugNodeFont(ImFont* font) { - bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", + bool opened = TreeNode(font, "Font: \"%s\": %.2f px, %d glyphs, %d sources(s)", font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); - SameLine(); - if (SmallButton("Set as default")) - GetIO().FontDefault = font; - if (!opened) - return; // Display preview text + if (!opened) + Indent(); + Indent(); PushFont(font); Text("The quick brown fox jumps over the lazy dog"); PopFont(); + if (!opened) + { + Unindent(); + Unindent(); + return; + } + if (SmallButton("Set as default")) + GetIO().FontDefault = font; // Display details SetNextItemWidth(GetFontSize() * 8); @@ -22016,62 +22066,69 @@ void ImGui::DebugNodeFont(ImFont* font) Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + { + const ImFontConfig* cfg = &font->ConfigData[config_i]; + int oversample_h, oversample_v; + ImFontAtlasBuildGetOversampleFactors(cfg, &oversample_h, &oversample_v); + BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", + config_i, cfg->Name, cfg->OversampleH, oversample_h, cfg->OversampleV, oversample_v, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + } // Display all glyphs of the fonts in separate pages of 256 characters - if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { - ImDrawList* draw_list = GetWindowDrawList(); - const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); - const float cell_size = font->FontSize * 1; - const float cell_spacing = GetStyle().ItemSpacing.y; - for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { - // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) - // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT - // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) - if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) + ImDrawList* draw_list = GetWindowDrawList(); + const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); + const float cell_size = font->FontSize * 1; + const float cell_spacing = GetStyle().ItemSpacing.y; + for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) { - base += 4096 - 256; - continue; - } - - int count = 0; - for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) - count++; - if (count <= 0) - continue; - if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) - continue; - - // Draw a 16x16 grid of glyphs - ImVec2 base_pos = GetCursorScreenPos(); - for (unsigned int n = 0; n < 256; n++) - { - // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions - // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (!glyph) - continue; - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip()) + // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) + // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT + // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) + if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191)) { - DebugNodeFontGlyph(font, glyph); - EndTooltip(); + base += 8192 - 256; + continue; } + + int count = 0; + for (unsigned int n = 0; n < 256; n++) + if (font->FindGlyphNoFallback((ImWchar)(base + n))) + count++; + if (count <= 0) + continue; + if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) + continue; + + // Draw a 16x16 grid of glyphs + ImVec2 base_pos = GetCursorScreenPos(); + for (unsigned int n = 0; n < 256; n++) + { + // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions + // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (!glyph) + continue; + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip()) + { + DebugNodeFontGlyph(font, glyph); + EndTooltip(); + } + } + Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + TreePop(); } - Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); TreePop(); } - TreePop(); } TreePop(); + Unindent(); } void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph) diff --git a/lib/third_party/imgui/imgui/source/imgui_demo.cpp b/lib/third_party/imgui/imgui/source/imgui_demo.cpp index bdf05f645..25a07c70e 100644 --- a/lib/third_party/imgui/imgui/source/imgui_demo.cpp +++ b/lib/third_party/imgui/imgui/source/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (demo code) // Help: @@ -2177,19 +2177,16 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) if (ImGui::TreeNode("Color/Picker Widgets")) { static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); + static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None; - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool drag_and_drop = true; - static bool options_menu = true; - static bool hdr = false; ImGui::SeparatorText("Options"); - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::SeparatorText("Inline color editor"); @@ -2197,15 +2194,15 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "CTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); @@ -2213,7 +2210,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only " "be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); @@ -2233,7 +2230,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) } static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); + bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags); ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); open_popup |= ImGui::Button("Palette"); if (open_popup) @@ -2245,7 +2242,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) { ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); + ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); ImGui::SameLine(); ImGui::BeginGroup(); // Lock X position @@ -2287,40 +2284,42 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); + ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::SeparatorText("Color picker"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; + static bool ref_color = false; static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f); - static int display_mode = 0; static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) + static int display_mode = 0; + static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar; + + ImGui::PushID("Color picker"); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview); + if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview) { ImGui::SameLine(); ImGui::Checkbox("With Ref Color", &ref_color); if (ref_color) { ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags); } } - ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); + + ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0"); + ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode."); + + ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0"); ImGui::SameLine(); HelpMarker( "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); - ImGui::SameLine(); HelpMarker("When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + + ImGuiColorEditFlags flags = base_flags | color_picker_flags; if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays @@ -2349,6 +2348,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::SameLine(); ImGui::SetNextItemWidth(w); ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); + ImGui::PopID(); // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV! @@ -7483,6 +7483,8 @@ static void ShowDemoWindowInputs() ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + ImGui::Text("Mouse clicked count:"); + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows // displaying the data for old/new backends. @@ -8062,7 +8064,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); - ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 2.0f, "%.0f"); + ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); ImGui::SeparatorText("Rounding"); @@ -8145,9 +8147,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = 0; - if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); - if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); - if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); + if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine(); + if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); + if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); HelpMarker( "In the color list:\n" "Left-click on color square to open color picker,\n" diff --git a/lib/third_party/imgui/imgui/source/imgui_draw.cpp b/lib/third_party/imgui/imgui/source/imgui_draw.cpp index ebe1684c8..93c9cefd1 100644 --- a/lib/third_party/imgui/imgui/source/imgui_draw.cpp +++ b/lib/third_party/imgui/imgui/source/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (drawing and font code) /* @@ -1704,8 +1704,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 // Accept null ranges if (text_begin == text_end || text_begin[0] == 0) return; - if (text_end == NULL) - text_end = text_begin + strlen(text_begin); + // No need to strlen() here: font->RenderText() will do it and may early out. // Pull default font/size from the shared ImDrawListSharedData instance if (font == NULL) @@ -1728,7 +1727,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) { - AddText(NULL, 0.0f, pos, col, text_begin, text_end); + AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end); } void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) @@ -3128,8 +3127,8 @@ ImFontConfig::ImFontConfig() { memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - OversampleH = 2; - OversampleV = 1; + OversampleH = 0; // Auto == 1 or 2 depending on size + OversampleV = 0; // Auto == 1 GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; @@ -3277,6 +3276,7 @@ void ImFontAtlas::ClearTexData() void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + ClearInputData(); Fonts.clear_delete(); TexReady = false; } @@ -3329,8 +3329,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?"); - IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?"); - IM_ASSERT(font_cfg->RasterizerDensity > 0.0f); + IM_ASSERT(font_cfg->RasterizerDensity > 0.0f && "Is ImFontConfig struct correctly initialized?"); // Create new font if (!font_cfg->MergeMode) @@ -3576,6 +3575,13 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig *data = table[*data]; } +void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* cfg, int* out_oversample_h, int* out_oversample_v) +{ + // Automatically disable horizontal oversampling over size 36 + *out_oversample_h = (cfg->OversampleH != 0) ? cfg->OversampleH : (cfg->SizePixels * cfg->RasterizerDensity > 36.0f || cfg->PixelSnapH) ? 1 : 2; + *out_oversample_v = (cfg->OversampleV != 0) ? cfg->OversampleV : 1; +} + #ifdef IMGUI_ENABLE_STB_TRUETYPE // Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) // (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) @@ -3741,15 +3747,19 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) buf_rects_out_n += src_tmp.GlyphsCount; buf_packedchars_out_n += src_tmp.GlyphsCount; - // Convert our ranges in the format stb_truetype wants + // Automatic selection of oversampling parameters ImFontConfig& cfg = atlas->ConfigData[src_i]; + int oversample_h, oversample_v; + ImFontAtlasBuildGetOversampleFactors(&cfg, &oversample_h, &oversample_v); + + // Convert our ranges in the format stb_truetype wants src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity; src_tmp.PackRange.first_unicode_codepoint_in_range = 0; src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars; - src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH; - src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; + src_tmp.PackRange.h_oversample = (unsigned char)oversample_h; + src_tmp.PackRange.v_oversample = (unsigned char)oversample_v; // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity); @@ -3758,9 +3768,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) int x0, y0, x1, y1; const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]); IM_ASSERT(glyph_index_in_font != 0); - stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1); - src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + pack_padding + cfg.OversampleH - 1); - src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + pack_padding + cfg.OversampleV - 1); + stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * oversample_h, scale * oversample_v, 0, 0, &x0, &y0, &x1, &y1); + src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + pack_padding + oversample_h - 1); + src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + pack_padding + oversample_v - 1); total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; } } @@ -4666,7 +4676,7 @@ ImFont::ImFont() Scale = 1.0f; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); } ImFont::~ImFont() @@ -4686,7 +4696,7 @@ void ImFont::ClearOutputData() DirtyLookupTables = true; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); } static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) @@ -4709,17 +4719,17 @@ void ImFont::BuildLookupTable() IndexAdvanceX.clear(); IndexLookup.clear(); DirtyLookupTables = false; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); GrowIndex(max_codepoint + 1); for (int i = 0; i < Glyphs.Size; i++) { int codepoint = (int)Glyphs[i].Codepoint; IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (ImWchar)i; + IndexLookup[codepoint] = (ImU16)i; // Mark 4K page as used - const int page_n = codepoint / 4096; - Used4kPagesMap[page_n >> 3] |= 1 << (page_n & 7); + const int page_n = codepoint / 8192; + Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7); } // Create a glyph to handle TAB @@ -4733,7 +4743,7 @@ void ImFont::BuildLookupTable() tab_glyph.Codepoint = '\t'; tab_glyph.AdvanceX *= IM_TABSIZE; IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size - 1); + IndexLookup[(int)tab_glyph.Codepoint] = (ImU16)(Glyphs.Size - 1); } // Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons) @@ -4781,15 +4791,15 @@ void ImFont::BuildLookupTable() } } -// API is designed this way to avoid exposing the 4K page size +// API is designed this way to avoid exposing the 8K page size // e.g. use with IsGlyphRangeUnused(0, 255) bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last) { - unsigned int page_begin = (c_begin / 4096); - unsigned int page_last = (c_last / 4096); + unsigned int page_begin = (c_begin / 8192); + unsigned int page_last = (c_last / 8192); for (unsigned int page_n = page_begin; page_n <= page_last; page_n++) - if ((page_n >> 3) < sizeof(Used4kPagesMap)) - if (Used4kPagesMap[page_n >> 3] & (1 << (page_n & 7))) + if ((page_n >> 3) < sizeof(Used8kPagesMap)) + if (Used8kPagesMap[page_n >> 3] & (1 << (page_n & 7))) return false; return true; } @@ -4806,7 +4816,7 @@ void ImFont::GrowIndex(int new_size) if (new_size <= IndexLookup.Size) return; IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (ImWchar)-1); + IndexLookup.resize(new_size, (ImU16)-1); } // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. @@ -4849,6 +4859,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa glyph.U1 = u1; glyph.V1 = v1; glyph.AdvanceX = advance_x; + IM_ASSERT(Glyphs.Size < 0xFFFF); // IndexLookup[] hold 16-bit values and -1 is reserved. // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling. @@ -4862,13 +4873,13 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. unsigned int index_size = (unsigned int)IndexLookup.Size; - if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists + if (dst < index_size && IndexLookup.Data[dst] == (ImU16)-1 && !overwrite_dst) // 'dst' already exists return; if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op return; GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1; + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImU16)-1; IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; } @@ -4877,8 +4888,8 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) { if (c >= (size_t)IndexLookup.Size) return FallbackGlyph; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) + const ImU16 i = IndexLookup.Data[c]; + if (i == (ImU16)-1) return FallbackGlyph; return &Glyphs.Data[i]; } @@ -4887,8 +4898,8 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) { if (c >= (size_t)IndexLookup.Size) return NULL; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) + const ImU16 i = IndexLookup.Data[c]; + if (i == (ImU16)-1) return NULL; return &Glyphs.Data[i]; } @@ -5102,15 +5113,15 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) { - if (!text_end) - text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. - // Align to be pixel perfect float x = IM_TRUNC(pos.x); float y = IM_TRUNC(pos.y); if (y > clip_rect.w) return; + if (!text_end) + text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. + const float scale = size / FontSize; const float line_height = FontSize * scale; const float origin_x = x; diff --git a/lib/third_party/imgui/imgui/source/imgui_tables.cpp b/lib/third_party/imgui/imgui/source/imgui_tables.cpp index efef5ff74..e7a3b8e8d 100644 --- a/lib/third_party/imgui/imgui/source/imgui_tables.cpp +++ b/lib/third_party/imgui/imgui/source/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (tables and columns code) /* @@ -374,6 +374,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; + table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent; temp_data->UserOuterSize = outer_size; // Instance data (for instance 0, TableID == TableInstanceID) @@ -1050,7 +1051,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen + // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen + column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer); if (offset_x_frozen && table->FreezeColumnsCount == visible_n) { @@ -1493,7 +1495,7 @@ void ImGui::EndTable() if (inner_window != outer_window) { short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; - inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. + inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently. g.CurrentTable = NULL; // To avoid error recovery recursing EndChild(); g.CurrentTable = table; @@ -2032,7 +2034,7 @@ void ImGui::TableEndRow(ImGuiTable* table) if (unfreeze_rows_request) { for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; + table->Columns[column_n].NavLayerCurrent = table->NavLayer; const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; diff --git a/lib/third_party/imgui/imgui/source/imgui_widgets.cpp b/lib/third_party/imgui/imgui/source/imgui_widgets.cpp index d8180acbb..eb789f079 100644 --- a/lib/third_party/imgui/imgui/source/imgui_widgets.cpp +++ b/lib/third_party/imgui/imgui/source/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 // (widgets code) /* @@ -915,15 +915,17 @@ ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) // Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) { + ImGuiContext& g = *GImGui; const ImRect outer_rect = window->Rect(); const ImRect inner_rect = window->InnerRect; - const float border_size = window->WindowBorderSize; const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size > 0.0f); + const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f); + const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f; if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); + return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::Scrollbar(ImGuiAxis axis) @@ -4250,6 +4252,23 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons BufTextLen += new_text_len; } +void ImGui::PushPasswordFont() +{ + ImGuiContext& g = *GImGui; + ImFont* in_font = g.Font; + ImFont* out_font = &g.InputTextPasswordFont; + const ImFontGlyph* glyph = in_font->FindGlyph('*'); + out_font->FontSize = in_font->FontSize; + out_font->Scale = in_font->Scale; + out_font->Ascent = in_font->Ascent; + out_font->Descent = in_font->Descent; + out_font->ContainerAtlas = in_font->ContainerAtlas; + out_font->FallbackGlyph = glyph; + out_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(out_font->Glyphs.Size == 0 && out_font->IndexAdvanceX.Size == 0 && out_font->IndexLookup.Size == 0); + PushFont(out_font); +} + // Return false to discard a character. static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { @@ -4660,19 +4679,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Password pushes a temporary font with only a fallback glyph if (is_password && !is_displaying_hint) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } + PushPasswordFont(); // Process mouse inputs and character inputs if (g.ActiveId == id) @@ -5909,7 +5916,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoTooltip; ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { @@ -5949,7 +5956,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) @@ -6125,8 +6132,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + if (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque)) + flags &= ~(ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf); ImVec4 col_rgb = col; if (flags & ImGuiColorEditFlags_InputHSV) @@ -6145,14 +6152,17 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); - RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + if ((flags & ImGuiColorEditFlags_AlphaNoBg) == 0) + RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + else + window->DrawList->AddRectFilled(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), rounding, ImDrawFlags_RoundCornersRight); window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); } else { // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; - if (col_source.w < 1.0f) + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaOpaque) ? col_rgb_without_alpha : col_rgb; + if (col_source.w < 1.0f && (flags & ImGuiColorEditFlags_AlphaNoBg) == 0) RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); @@ -6182,7 +6192,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_)); return pressed; } @@ -6223,7 +6233,8 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ImGuiColorEditFlags flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_; + ColorButton("##preview", cf, (flags & flags_to_forward) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) { @@ -6944,13 +6955,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x); - // Text stays at the submission position, but bounding box may be extended on both sides - const ImVec2 text_min = pos; - const ImVec2 text_max(min_x + size.x, pos.y + size.y); - // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. - ImRect bb(min_x, pos.y, text_max.x, text_max.y); + ImRect bb(min_x, pos.y, min_x + size.x, pos.y + size.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; @@ -7086,8 +7093,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl PopColumnsBackground(); } + // Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns. if (is_visible) - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + RenderTextClipped(pos, ImVec2(window->WorkRect.Max.x, pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) @@ -8629,12 +8637,14 @@ bool ImGui::BeginMenuBar() IM_ASSERT(!window->DC.MenuBarAppending); BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore - PushID("##menubar"); + PushID("##MenuBar"); // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. + const float border_top = ImMax(IM_ROUND(window->WindowBorderSize * 0.5f - window->TitleBarHeight), 0.0f); + const float border_half = IM_ROUND(window->WindowBorderSize * 0.5f); ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(ImFloor(bar_rect.Min.x + window->WindowBorderSize), ImFloor(bar_rect.Min.y + window->WindowBorderSize), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), ImFloor(bar_rect.Max.y)); + ImRect clip_rect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, border_half))), ImFloor(bar_rect.Max.y)); clip_rect.ClipWith(window->OuterRectClipped); PushClipRect(clip_rect.Min, clip_rect.Max, false); @@ -8655,6 +8665,10 @@ void ImGui::EndMenuBar() return; ImGuiContext& g = *GImGui; + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { @@ -8681,9 +8695,6 @@ void ImGui::EndMenuBar() } } - IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" - IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); - IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); PopID(); window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. @@ -8730,7 +8741,7 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im viewport->BuildWorkInsetMax[axis] += axis_size; } - window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; // Create window SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. @@ -8760,22 +8771,33 @@ bool ImGui::BeginMainMenuBar() float height = GetFrameHeight(); bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - - if (is_open) - BeginMenuBar(); - else + if (!is_open) + { End(); + return false; + } + + // Temporarily disable _NoSavedSettings, in the off-chance that tables or child windows submitted within the menu-bar may want to use settings. (#8356) + g.CurrentWindow->Flags &= ~ImGuiWindowFlags_NoSavedSettings; + BeginMenuBar(); return is_open; } void ImGui::EndMainMenuBar() { + ImGuiContext& g = *GImGui; + if (!g.CurrentWindow->DC.MenuBarAppending) + { + IM_ASSERT_USER_ERROR(0, "Calling EndMainMenuBar() not from a menu-bar!"); // Not technically testing that it is the main menu bar + return; + } + EndMenuBar(); + g.CurrentWindow->Flags |= ImGuiWindowFlags_NoSavedSettings; // Restore _NoSavedSettings (#8356) // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // FIXME: With this strategy we won't be able to restore a NULL focus. - ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) + if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest && g.ActiveId == 0) FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); End(); @@ -9980,7 +10002,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! + IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) @@ -10026,6 +10048,23 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } +void ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + SetNextItemWidth(width); + TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL); +} + bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done @@ -10173,8 +10212,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + bool hovered, held, pressed; + if (flags & ImGuiTabItemFlags_Invisible) + hovered = held = pressed = false; + else + pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); @@ -10259,57 +10301,71 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, #endif // Render tab shape - ImDrawList* display_draw_list = window->DrawList; - const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); - TabItemBackground(display_draw_list, bb, flags, tab_col); - if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible); + if (is_visible) { - float x_offset = IM_TRUNC(0.4f * style.TabRounding); - if (x_offset < 2.0f * g.CurrentDpiScale) - x_offset = 0.0f; - float y_offset = 1.0f * g.CurrentDpiScale; - display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize); + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + { + // Might be moved to TabItemBackground() ? + ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline); + if (style.TabRounding > 0.0f) + { + float rounding = style.TabRounding; + display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); + display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); + display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); + } + else + { + display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize); + } + } + RenderNavCursor(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + TabBarQueueFocus(tab_bar, tab); + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; + bool just_closed; + bool text_clipped; + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + if (just_closed && p_open != NULL) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) + // That state is copied to window->DockTabItemStatusFlags by our caller. + if (docked_window && (hovered || g.HoveredId == close_button_id)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? + if (text_clipped && g.HoveredId == id && !held) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); } - RenderNavCursor(bb, id); - - // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. - const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) - TabBarQueueFocus(tab_bar, tab); - - if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) - flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; - - // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; - bool just_closed; - bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); - if (just_closed && p_open != NULL) - { - *p_open = false; - TabBarCloseTab(tab_bar, tab); - } - - // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) - // That state is copied to window->DockTabItemStatusFlags by our caller. - if (docked_window && (hovered || g.HoveredId == close_button_id)) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip - // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) - // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) - // FIXME: This is a mess. - // FIXME: We may want disabled tab to still display the tooltip? - if (text_clipped && g.HoveredId == id && !held) - if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); - IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) return pressed; diff --git a/lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp b/lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp index 9565cdf7c..cf4440176 100644 --- a/lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp +++ b/lib/third_party/imgui/imgui/source/misc/freetype/imgui_freetype.cpp @@ -6,10 +6,11 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927) // 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics. -// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591) +// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591) // 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly. -// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL. +// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr. // 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. // 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format. // 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). @@ -32,7 +33,7 @@ // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). -// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). +// FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better. #include "imgui.h" #ifndef IMGUI_DISABLE @@ -45,12 +46,21 @@ #include FT_GLYPH_H // #include FT_SYNTHESIS_H // -#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +// Handle LunaSVG and PlutoSVG +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) +#error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG" +#endif +#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG #include FT_OTSVG_H // #include FT_BBOX_H // #include +#endif +#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG +#include +#endif +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG) #if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) -#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 +#error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 #endif #endif @@ -94,6 +104,9 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_ // Code //------------------------------------------------------------------------- +#define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point +#define FT_SCALEFACTOR 64.0f + namespace { // Glyph metrics: @@ -159,6 +172,7 @@ namespace const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint); const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info); void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr); + FreeTypeFont() { memset((void*)this, 0, sizeof(*this)); } ~FreeTypeFont() { CloseFont(); } // [Internals] @@ -171,9 +185,6 @@ namespace float InvRasterizationDensity; }; - // From SDL_ttf: Handy routines for converting from fixed point - #define FT_CEIL(X) (((X + 63) & -64) / 64) - bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags) { FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face); @@ -269,11 +280,11 @@ namespace // Need an outline for this to work FT_GlyphSlot slot = Face->glyph; -#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG +#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG); #else #if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12)) - IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); + IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font"); #endif IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP); #endif // IMGUI_ENABLE_FREETYPE_LUNASVG @@ -305,7 +316,7 @@ namespace out_glyph_info->Height = (int)ft_bitmap->rows; out_glyph_info->OffsetX = Face->glyph->bitmap_left; out_glyph_info->OffsetY = -Face->glyph->bitmap_top; - out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x); + out_glyph_info->AdvanceX = (float)slot->advance.x / FT_SCALEFACTOR; out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); return ft_bitmap; @@ -480,8 +491,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) { // Check for valid range. This may also help detect *some* dangling pointers, because a common - // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent. - IM_ASSERT(src_range[0] <= src_range[1]); + // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, + // or to forget to zero-terminate the glyph range array. + IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?"); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); } dst_tmp.SrcCount++; @@ -561,6 +573,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // 8. Render/rasterize font characters into the texture int total_surface = 0; int buf_rects_out_n = 0; + const int pack_padding = atlas->TexGlyphPadding; for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; @@ -578,7 +591,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); // Gather the sizes of all rectangles we will need to pack - const int padding = atlas->TexGlyphPadding; for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) { ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i]; @@ -606,11 +618,13 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u buf_bitmap_current_used_bytes += bitmap_size_in_bytes; src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr); - src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding); - src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding); + src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + pack_padding); + src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + pack_padding); total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; } } + for (int i = 0; i < atlas->CustomRects.Size; i++) + total_surface += (atlas->CustomRects[i].Width + pack_padding) * (atlas->CustomRects[i].Height + pack_padding); // We need a width for the skyline algorithm, any width! // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. @@ -636,8 +650,10 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; + // IMHEX PATCH BEGIN + // if (src_tmp.GlyphsCount == 0) + // continue; + // IMHEX PATCH END stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount); @@ -670,10 +686,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i]; - // IMHEX PATCH BEGIN - // if (src_tmp.GlyphsCount == 0) - // continue; - // IMHEX PATCH END // When merging fonts with MergeMode=true: // - We can have multiple input fonts writing into a same destination font. @@ -684,6 +696,9 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u const float ascent = src_tmp.Font.Info.Ascender; const float descent = src_tmp.Font.Info.Descender; ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); + + if (src_tmp.GlyphsCount == 0) + continue; const float font_off_x = cfg.GlyphOffset.x; const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); @@ -811,6 +826,10 @@ static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas) SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot }; FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks); #endif // IMGUI_ENABLE_FREETYPE_LUNASVG +#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG + // With plutosvg, use provided hooks + FT_Property_Set(ft_library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks()); +#endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags); FT_Done_Library(ft_library); diff --git a/lib/third_party/imgui/implot/source/implot.cpp b/lib/third_party/imgui/implot/source/implot.cpp index d92602811..2a5d66c4b 100644 --- a/lib/third_party/imgui/implot/source/implot.cpp +++ b/lib/third_party/imgui/implot/source/implot.cpp @@ -5010,9 +5010,15 @@ void ShowStyleEditor(ImPlotStyle* ref) { filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; +#if IMGUI_VERSION_NUM < 19173 if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); +#else + if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine(); + if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); + if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); +#endif HelpMarker( "In the color list:\n" "Left-click on colored square to open color picker,\n" diff --git a/lib/third_party/imgui/implot3d/include/implot3d.h b/lib/third_party/imgui/implot3d/include/implot3d.h index 11c3ac6e8..7abaa179e 100644 --- a/lib/third_party/imgui/implot3d/include/implot3d.h +++ b/lib/third_party/imgui/implot3d/include/implot3d.h @@ -15,6 +15,7 @@ // [SECTION] Macros and Defines // [SECTION] Forward declarations and basic types // [SECTION] Flags & Enumerations +// [SECTION] Callbacks // [SECTION] Context // [SECTION] Begin/End Plot // [SECTION] Setup @@ -30,7 +31,6 @@ // [SECTION] ImPlot3DBox // [SECTION] ImPlot3DQuat // [SECTION] ImPlot3DStyle -// [SECTION] Callbacks // [SECTION] Meshes #pragma once @@ -97,6 +97,7 @@ enum ImPlot3DFlags_ { ImPlot3DFlags_NoLegend = 1 << 1, // Hide plot legend ImPlot3DFlags_NoMouseText = 1 << 2, // Hide mouse position in plot coordinates ImPlot3DFlags_NoClip = 1 << 3, // Disable 3D box clipping + ImPlot3DFlags_NoMenus = 1 << 4, // The user will not be able to open context menus ImPlot3DFlags_CanvasOnly = ImPlot3DFlags_NoTitle | ImPlot3DFlags_NoLegend | ImPlot3DFlags_NoMouseText, }; @@ -248,6 +249,7 @@ enum ImPlot3DAxisFlags_ { ImPlot3DAxisFlags_LockMin = 1 << 4, // The axis minimum value will be locked when panning/zooming ImPlot3DAxisFlags_LockMax = 1 << 5, // The axis maximum value will be locked when panning/zooming ImPlot3DAxisFlags_AutoFit = 1 << 6, // Axis will be auto-fitting to data extents + ImPlot3DAxisFlags_Invert = 1 << 7, // The axis will be inverted ImPlot3DAxisFlags_Lock = ImPlot3DAxisFlags_LockMin | ImPlot3DAxisFlags_LockMax, ImPlot3DAxisFlags_NoDecorations = ImPlot3DAxisFlags_NoLabel | ImPlot3DAxisFlags_NoGridLines | ImPlot3DAxisFlags_NoTickLabels, }; @@ -288,6 +290,13 @@ enum ImPlot3DColormap_ { ImPlot3DColormap_Greys = 15, // White/black }; +//----------------------------------------------------------------------------- +// [SECTION] Callbacks +//----------------------------------------------------------------------------- + +// Callback signature for axis tick label formatter +typedef int (*ImPlot3DFormatter)(float value, char* buff, int size, void* user_data); + namespace ImPlot3D { //----------------------------------------------------------------------------- @@ -353,12 +362,23 @@ IMPLOT3D_API void SetupAxis(ImAxis3D axis, const char* label = nullptr, ImPlot3D IMPLOT3D_API void SetupAxisLimits(ImAxis3D axis, double v_min, double v_max, ImPlot3DCond cond = ImPlot3DCond_Once); +IMPLOT3D_API void SetupAxisFormat(ImAxis3D idx, ImPlot3DFormatter formatter, void* data = nullptr); + +// Sets an axis' ticks and optionally the labels. To keep the default ticks, set #keep_default=true +IMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, const double* values, int n_ticks, const char* const labels[] = nullptr, bool keep_default = false); + +// Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true +IMPLOT3D_API void SetupAxisTicks(ImAxis3D axis, double v_min, double v_max, int n_ticks, const char* const labels[] = nullptr, bool keep_default = false); + // Sets the label and/or flags for primary X/Y/Z axes (shorthand for three calls to SetupAxis) IMPLOT3D_API void SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags = 0, ImPlot3DAxisFlags y_flags = 0, ImPlot3DAxisFlags z_flags = 0); // Sets the X/Y/Z axes range limits. If ImPlotCond_Always is used, the axes limits will be locked (shorthand for two calls to SetupAxisLimits) IMPLOT3D_API void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max, ImPlot3DCond cond = ImPlot3DCond_Once); +// Sets the plot box X/Y/Z scale. A scale of 1.0 is the default. Values greater than 1.0 enlarge the plot, while values between 0.0 and 1.0 shrink it. +IMPLOT3D_API void SetupBoxScale(float x, float y, float z); + IMPLOT3D_API void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags flags = 0); //----------------------------------------------------------------------------- @@ -373,7 +393,8 @@ IMPLOT3D_TMP void PlotTriangle(const char* label_id, const T* xs, const T* ys, c IMPLOT3D_TMP void PlotQuad(const char* label_id, const T* xs, const T* ys, const T* zs, int count, ImPlot3DQuadFlags flags = 0, int offset = 0, int stride = sizeof(T)); -IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T)); +// Plot the surface defined by a grid of vertices. The grid is defined by the x and y arrays, and the z array contains the height of each vertex. A total of x_count * y_count vertices are expected for each array. Leave #scale_min and #scale_max both at 0 for automatic color scaling, or set them to a predefined range. +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min = 0.0, double scale_max = 0.0, ImPlot3DSurfaceFlags flags = 0, int offset = 0, int stride = sizeof(T)); IMPLOT3D_API void PlotMesh(const char* label_id, const ImPlot3DPoint* vtx, const unsigned int* idx, int vtx_count, int idx_count, ImPlot3DMeshFlags flags = 0); @@ -523,51 +544,51 @@ struct ImPlot3DPoint { } // Binary operators - ImPlot3DPoint operator*(float rhs) const; - ImPlot3DPoint operator/(float rhs) const; - ImPlot3DPoint operator+(const ImPlot3DPoint& rhs) const; - ImPlot3DPoint operator-(const ImPlot3DPoint& rhs) const; - ImPlot3DPoint operator*(const ImPlot3DPoint& rhs) const; - ImPlot3DPoint operator/(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API ImPlot3DPoint operator*(float rhs) const; + IMPLOT3D_API ImPlot3DPoint operator/(float rhs) const; + IMPLOT3D_API ImPlot3DPoint operator+(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API ImPlot3DPoint operator-(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API ImPlot3DPoint operator*(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API ImPlot3DPoint operator/(const ImPlot3DPoint& rhs) const; // Unary operator - ImPlot3DPoint operator-() const; + IMPLOT3D_API ImPlot3DPoint operator-() const; // Compound assignment operators - ImPlot3DPoint& operator*=(float rhs); - ImPlot3DPoint& operator/=(float rhs); - ImPlot3DPoint& operator+=(const ImPlot3DPoint& rhs); - ImPlot3DPoint& operator-=(const ImPlot3DPoint& rhs); - ImPlot3DPoint& operator*=(const ImPlot3DPoint& rhs); - ImPlot3DPoint& operator/=(const ImPlot3DPoint& rhs); + IMPLOT3D_API ImPlot3DPoint& operator*=(float rhs); + IMPLOT3D_API ImPlot3DPoint& operator/=(float rhs); + IMPLOT3D_API ImPlot3DPoint& operator+=(const ImPlot3DPoint& rhs); + IMPLOT3D_API ImPlot3DPoint& operator-=(const ImPlot3DPoint& rhs); + IMPLOT3D_API ImPlot3DPoint& operator*=(const ImPlot3DPoint& rhs); + IMPLOT3D_API ImPlot3DPoint& operator/=(const ImPlot3DPoint& rhs); // Comparison operators - bool operator==(const ImPlot3DPoint& rhs) const; - bool operator!=(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API bool operator==(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API bool operator!=(const ImPlot3DPoint& rhs) const; // Dot product - float Dot(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API float Dot(const ImPlot3DPoint& rhs) const; // Cross product - ImPlot3DPoint Cross(const ImPlot3DPoint& rhs) const; + IMPLOT3D_API ImPlot3DPoint Cross(const ImPlot3DPoint& rhs) const; // Get vector length - float Length() const; + IMPLOT3D_API float Length() const; // Get vector squared length - float LengthSquared() const; + IMPLOT3D_API float LengthSquared() const; // Normalize to unit length - void Normalize(); + IMPLOT3D_API void Normalize(); // Return vector normalized to unit length - ImPlot3DPoint Normalized() const; + IMPLOT3D_API ImPlot3DPoint Normalized() const; // Friend binary operators to allow commutative behavior - friend ImPlot3DPoint operator*(float lhs, const ImPlot3DPoint& rhs); + IMPLOT3D_API friend ImPlot3DPoint operator*(float lhs, const ImPlot3DPoint& rhs); // Check if the point is NaN - bool IsNaN() const; + IMPLOT3D_API bool IsNaN() const; #ifdef IMPLOT3D_POINT_CLASS_EXTRA IMPLOT3D_POINT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImPlot3DPoint @@ -607,13 +628,13 @@ struct ImPlot3DBox { constexpr ImPlot3DBox(const ImPlot3DPoint& min, const ImPlot3DPoint& max) : Min(min), Max(max) {} // Method to expand the box to include a point - void Expand(const ImPlot3DPoint& point); + IMPLOT3D_API void Expand(const ImPlot3DPoint& point); // Method to check if a point is inside the box - bool Contains(const ImPlot3DPoint& point) const; + IMPLOT3D_API bool Contains(const ImPlot3DPoint& point) const; // Method to clip a line segment against the box - bool ClipLineSegment(const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, ImPlot3DPoint& p0_clipped, ImPlot3DPoint& p1_clipped) const; + IMPLOT3D_API bool ClipLineSegment(const ImPlot3DPoint& p0, const ImPlot3DPoint& p1, ImPlot3DPoint& p0_clipped, ImPlot3DPoint& p1_clipped) const; }; //----------------------------------------------------------------------------- @@ -627,8 +648,8 @@ struct ImPlot3DRange { constexpr ImPlot3DRange() : Min(0.0f), Max(0.0f) {} constexpr ImPlot3DRange(float min, float max) : Min(min), Max(max) {} - void Expand(float value); - bool Contains(float value) const; + IMPLOT3D_API void Expand(float value); + IMPLOT3D_API bool Contains(float value) const; float Size() const { return Max - Min; } }; @@ -643,41 +664,41 @@ struct ImPlot3DQuat { constexpr ImPlot3DQuat() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {} constexpr ImPlot3DQuat(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) {} - ImPlot3DQuat(float _angle, const ImPlot3DPoint& _axis); + IMPLOT3D_API ImPlot3DQuat(float _angle, const ImPlot3DPoint& _axis); // Set quaternion from two vectors - static ImPlot3DQuat FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1); + IMPLOT3D_API static ImPlot3DQuat FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1); // Get quaternion length - float Length() const; + IMPLOT3D_API float Length() const; // Get normalized quaternion - ImPlot3DQuat Normalized() const; + IMPLOT3D_API ImPlot3DQuat Normalized() const; // Conjugate of the quaternion - ImPlot3DQuat Conjugate() const; + IMPLOT3D_API ImPlot3DQuat Conjugate() const; // Inverse of the quaternion - ImPlot3DQuat Inverse() const; + IMPLOT3D_API ImPlot3DQuat Inverse() const; // Binary operators - ImPlot3DQuat operator*(const ImPlot3DQuat& rhs) const; + IMPLOT3D_API ImPlot3DQuat operator*(const ImPlot3DQuat& rhs) const; // Normalize the quaternion in place - ImPlot3DQuat& Normalize(); + IMPLOT3D_API ImPlot3DQuat& Normalize(); // Rotate a 3D point using the quaternion - ImPlot3DPoint operator*(const ImPlot3DPoint& point) const; + IMPLOT3D_API ImPlot3DPoint operator*(const ImPlot3DPoint& point) const; // Comparison operators - bool operator==(const ImPlot3DQuat& rhs) const; - bool operator!=(const ImPlot3DQuat& rhs) const; + IMPLOT3D_API bool operator==(const ImPlot3DQuat& rhs) const; + IMPLOT3D_API bool operator!=(const ImPlot3DQuat& rhs) const; // Interpolate between two quaternions - static ImPlot3DQuat Slerp(const ImPlot3DQuat& q1, const ImPlot3DQuat& q2, float t); + IMPLOT3D_API static ImPlot3DQuat Slerp(const ImPlot3DQuat& q1, const ImPlot3DQuat& q2, float t); // Get quaternion dot product - float Dot(const ImPlot3DQuat& rhs) const; + IMPLOT3D_API float Dot(const ImPlot3DQuat& rhs) const; #ifdef IMPLOT3D_QUAT_CLASS_EXTRA IMPLOT3D_QUAT_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImPlot3DQuat @@ -712,13 +733,6 @@ struct ImPlot3DStyle { IMPLOT3D_API ImPlot3DStyle(); }; -//----------------------------------------------------------------------------- -// [SECTION] Callbacks -//----------------------------------------------------------------------------- - -// Callback signature for axis tick label formatter -typedef int (*ImPlot3DFormatter)(float value, char* buff, int size, void* user_data); - //----------------------------------------------------------------------------- // [SECTION] Meshes //----------------------------------------------------------------------------- diff --git a/lib/third_party/imgui/implot3d/include/implot3d_internal.h b/lib/third_party/imgui/implot3d/include/implot3d_internal.h index b0ce4f0d0..859e43d4a 100644 --- a/lib/third_party/imgui/implot3d/include/implot3d_internal.h +++ b/lib/third_party/imgui/implot3d/include/implot3d_internal.h @@ -97,6 +97,16 @@ static inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) { #endif } +// Fills a buffer with n samples linear interpolated from vmin to vmax +template +void FillRange(ImVector& buffer, int n, T vmin, T vmax) { + buffer.resize(n); + T step = (vmax - vmin) / (n - 1); + for (int i = 0; i < n; ++i) { + buffer[i] = vmin + i * step; + } +} + } // namespace ImPlot3D //----------------------------------------------------------------------------- @@ -109,24 +119,30 @@ struct ImPlot3DTicker; // [SECTION] Callbacks //------------------------------------------------------------------------------ -typedef void (*ImPlot3DLocator)(ImPlot3DTicker& ticker, const ImPlot3DRange& range, ImPlot3DFormatter formatter, void* formatter_data); +typedef void (*ImPlot3DLocator)(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data); //----------------------------------------------------------------------------- // [SECTION] Structs //----------------------------------------------------------------------------- struct ImDrawList3D { - ImVector IdxBuffer = {}; // Index buffer - ImVector VtxBuffer = {}; // Vertex buffer - ImVector ZBuffer = {}; // Z buffer. Depth value for each triangle - unsigned int _VtxCurrentIdx = 0; // [Internal] current vertex index - ImDrawVert* _VtxWritePtr = nullptr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImDrawIdx* _IdxWritePtr = nullptr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - float* _ZWritePtr = nullptr; // [Internal] point within ZBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImDrawListFlags _Flags = ImDrawListFlags_None; // [Internal] draw list flags - ImDrawListSharedData* _SharedData = nullptr; // [Internal] shared draw list data + ImVector IdxBuffer; // Index buffer + ImVector VtxBuffer; // Vertex buffer + ImVector ZBuffer; // Z buffer. Depth value for each triangle + unsigned int _VtxCurrentIdx; // [Internal] current vertex index + ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + float* _ZWritePtr; // [Internal] point within ZBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImDrawListFlags _Flags; // [Internal] draw list flags + ImDrawListSharedData* _SharedData; // [Internal] shared draw list data ImDrawList3D() { + _VtxCurrentIdx = 0; + _VtxWritePtr = nullptr; + _IdxWritePtr = nullptr; + _ZWritePtr = nullptr; + _Flags = ImDrawListFlags_None; + _SharedData = nullptr; } void PrimReserve(int idx_count, int vtx_count); @@ -360,7 +376,7 @@ struct ImPlot3DTick { int Idx; ImPlot3DTick(double value, bool major, bool show_label) { - PlotPos = value; + PlotPos = (float)value; Major = major; ShowLabel = show_label; TextOffset = -1; @@ -434,10 +450,12 @@ struct ImPlot3DAxis { ImPlot3DFormatter Formatter; void* FormatterData; ImPlot3DLocator Locator; + bool ShowDefaultTicks; // Fit data bool FitThisFrame; ImPlot3DRange FitExtents; // User input + bool Hovered; bool Held; // Constructor @@ -451,36 +469,48 @@ struct ImPlot3DAxis { Formatter = nullptr; FormatterData = nullptr; Locator = nullptr; + ShowDefaultTicks = true; // Fit data FitThisFrame = true; FitExtents.Min = HUGE_VAL; FitExtents.Max = -HUGE_VAL; // User input + Hovered = false; Held = false; } + inline void Reset() { + Formatter = nullptr; + FormatterData = nullptr; + Locator = nullptr; + ShowDefaultTicks = true; + FitExtents.Min = HUGE_VAL; + FitExtents.Max = -HUGE_VAL; + Ticker.Reset(); + } + inline void SetRange(double v1, double v2) { - Range.Min = ImMin(v1, v2); - Range.Max = ImMax(v1, v2); + Range.Min = (float)ImMin(v1, v2); + Range.Max = (float)ImMax(v1, v2); } inline bool SetMin(double _min, bool force = false) { if (!force && IsLockedMin()) return false; - _min = ImPlot3D::ImConstrainNan(ImPlot3D::ImConstrainInf(_min)); + _min = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_min)); if (_min >= Range.Max) return false; - Range.Min = _min; + Range.Min = (float)_min; return true; } inline bool SetMax(double _max, bool force = false) { if (!force && IsLockedMax()) return false; - _max = ImPlot3D::ImConstrainNan(ImPlot3D::ImConstrainInf(_max)); + _max = ImPlot3D::ImConstrainNan((float)ImPlot3D::ImConstrainInf(_max)); if (_max <= Range.Min) return false; - Range.Max = _max; + Range.Max = (float)_max; return true; } @@ -488,6 +518,9 @@ struct ImPlot3DAxis { inline bool IsLockedMin() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMin); } inline bool IsLockedMax() const { return IsRangeLocked() || ImPlot3D::ImHasFlag(Flags, ImPlot3DAxisFlags_LockMax); } inline bool IsLocked() const { return IsLockedMin() && IsLockedMax(); } + inline bool IsInputLockedMin() const { return IsLockedMin() || IsAutoFitting(); } + inline bool IsInputLockedMax() const { return IsLockedMax() || IsAutoFitting(); } + inline bool IsInputLocked() const { return IsLocked() || IsAutoFitting(); } inline void SetLabel(const char* label) { Label.Buf.shrink(0); @@ -504,8 +537,6 @@ struct ImPlot3DAxis { bool IsAutoFitting() const; void ExtendFit(float value); void ApplyFit(); - float PlotToNDC(float value) const; - float NDCToPlot(float value) const; }; // Holds plot state information that must persist after EndPlot @@ -520,9 +551,10 @@ struct ImPlot3DPlot { ImRect FrameRect; // Outermost bounding rectangle that encapsulates whole the plot/title/padding/etc ImRect CanvasRect; // Frame rectangle reduced by padding ImRect PlotRect; // Bounding rectangle for the actual plot area - // Rotation & Axes - ImPlot3DQuat Rotation; - ImPlot3DAxis Axes[3]; + // Rotation & axes & box + ImPlot3DQuat Rotation; // Current rotation quaternion + ImPlot3DAxis Axes[3]; // X, Y, Z axes + ImPlot3DPoint BoxScale; // Scale factor for plot box X, Y, Z axes // Animation float AnimationTime; // Remaining animation time ImPlot3DQuat RotationAnimationEnd; // End rotation for animation @@ -550,6 +582,7 @@ struct ImPlot3DPlot { Rotation = ImPlot3DQuat(0.0f, 0.0f, 0.0f, 1.0f); for (int i = 0; i < 3; i++) Axes[i] = ImPlot3DAxis(); + BoxScale = ImPlot3DPoint(1.0f, 1.0f, 1.0f); AnimationTime = 0.0f; RotationAnimationEnd = Rotation; SetupLocked = false; @@ -575,6 +608,7 @@ struct ImPlot3DPlot { ImPlot3DPoint RangeMax() const; ImPlot3DPoint RangeCenter() const; void SetRange(const ImPlot3DPoint& min, const ImPlot3DPoint& max); + float GetBoxZoom() const; }; struct ImPlot3DContext { @@ -680,7 +714,7 @@ int Formatter_Default(float value, char* buff, int size, void* data); // [SECTION] Locator //------------------------------------------------------------------------------ -void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, ImPlot3DFormatter formatter, void* formatter_data); +void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data); } // namespace ImPlot3D diff --git a/lib/third_party/imgui/implot3d/source/implot3d.cpp b/lib/third_party/imgui/implot3d/source/implot3d.cpp index 7a51e6d01..aae78de05 100644 --- a/lib/third_party/imgui/implot3d/source/implot3d.cpp +++ b/lib/third_party/imgui/implot3d/source/implot3d.cpp @@ -48,11 +48,14 @@ #define IMGUI_DEFINE_MATH_OPERATORS #endif +// We define this to avoid accidentally using the deprecated API +#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS +#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS +#endif + #include "implot3d.h" #include "implot3d_internal.h" -#include - #ifndef IMGUI_DISABLE //----------------------------------------------------------------------------- @@ -73,7 +76,7 @@ namespace ImPlot3D { ImPlot3DContext* GImPlot3D = nullptr; #endif -static ImPlot3DQuat init_rotation = ImPlot3DQuat(-0.513269, -0.212596, -0.318184, 0.76819); +static ImPlot3DQuat init_rotation = ImPlot3DQuat(-0.513269f, -0.212596f, -0.318184f, 0.76819f); ImPlot3DContext* CreateContext() { ImPlot3DContext* ctx = IM_NEW(ImPlot3DContext)(); @@ -107,8 +110,7 @@ void AddTextRotated(ImDrawList* draw_list, ImVec2 pos, float angle, ImU32 col, c ImFont* font = g.Font; // Align to be pixel perfect - pos.x = IM_FLOOR(pos.x); - pos.y = IM_FLOOR(pos.y); + pos = ImFloor(pos); const float scale = g.FontSize / font->FontSize; @@ -244,7 +246,6 @@ void ShowLegendEntries(ImPlot3DItemGroup& items, const ImRect& legend_bb, bool h const int num_items = items.GetLegendCount(); if (num_items == 0) return; - ImPlot3DContext& gp = *GImPlot3D; // Render legend items for (int i = 0; i < num_items; i++) { @@ -508,7 +509,7 @@ int GetMouseOverAxis(const ImPlot3DPlot& plot, const bool* active_faces, const I void RenderPlotBackground(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImVec2* corners_pix, const bool* active_faces, const int plane_2d) { const ImVec4 col_bg = GetStyleColorVec4(ImPlot3DCol_PlotBg); - const ImVec4 col_bg_hov = col_bg + ImVec4(0.03, 0.03, 0.03, 0.0); + const ImVec4 col_bg_hov = col_bg + ImVec4(0.03f, 0.03f, 0.03f, 0.0f); int hovered_plane = -1; if (!plot.Held) { @@ -531,8 +532,6 @@ void RenderPlotBackground(ImDrawList* draw_list, const ImPlot3DPlot& plot, const } void RenderPlotBorder(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImVec2* corners_pix, const bool* active_faces, const int plane_2d) { - ImGuiIO& io = ImGui::GetIO(); - int hovered_edge = -1; if (!plot.Held) GetMouseOverAxis(plot, active_faces, corners_pix, plane_2d, &hovered_edge); @@ -575,13 +574,11 @@ void RenderGrid(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DP // Get the two axes (u and v) that define the face plane int idx0 = faces[face_idx][0]; int idx1 = faces[face_idx][1]; - int idx2 = faces[face_idx][2]; int idx3 = faces[face_idx][3]; // Corners of the face in plot space ImPlot3DPoint p0 = corners[idx0]; ImPlot3DPoint p1 = corners[idx1]; - ImPlot3DPoint p2 = corners[idx2]; ImPlot3DPoint p3 = corners[idx3]; // Vectors along the edges @@ -595,6 +592,11 @@ void RenderGrid(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DP // Compute position along u float t_u = (tick.PlotPos - axis_u.Range.Min) / (axis_u.Range.Max - axis_u.Range.Min); + + // Skip ticks that are out of range + if (t_u < 0.0f || t_u > 1.0f) + continue; + ImPlot3DPoint p_start = p0 + u_vec * t_u; ImPlot3DPoint p_end = p3 + u_vec * t_u; @@ -616,6 +618,11 @@ void RenderGrid(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DP // Compute position along v float t_v = (tick.PlotPos - axis_v.Range.Min) / (axis_v.Range.Max - axis_v.Range.Min); + + // Skip ticks that are out of range + if (t_v < 0.0f || t_v > 1.0f) + continue; + ImPlot3DPoint p_start = p0 + v_vec * t_v; ImPlot3DPoint p_end = p1 + v_vec * t_v; @@ -718,6 +725,9 @@ void RenderTickMarks(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPl const ImPlot3DTick& tick = axis.Ticker.Ticks[t]; float v = (tick.PlotPos - axis.Range.Min) / (axis.Range.Max - axis.Range.Min); + // Skip ticks that are out of range + if (v < 0.0f || v > 1.0f) + continue; ImPlot3DPoint tick_pos_ndc = PlotToNDC(axis_start + axis_dir * (v * axis_len)); // Half tick on each side of the axis line @@ -736,7 +746,6 @@ void RenderTickMarks(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPl } void RenderTickLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const ImVec2* corners_pix, const int axis_corners[3][2]) { - ImVec2 box_center_pix = PlotToPixels(plot.RangeCenter()); ImU32 col_tick_txt = GetStyleColorU32(ImPlot3DCol_AxisText); for (int a = 0; a < 3; a++) { @@ -809,6 +818,10 @@ void RenderTickLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImP // Compute position along the axis float t_axis = (tick.PlotPos - axis.Range.Min) / (axis.Range.Max - axis.Range.Min); + + // Skip ticks that are out of range + if (t_axis < 0.0f || t_axis > 1.0f) + continue; ImPlot3DPoint tick_pos = axis_start + axis_dir * t_axis; // Convert to pixel coordinates @@ -827,7 +840,6 @@ void RenderTickLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImP } void RenderAxisLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImPlot3DPoint* corners, const ImVec2* corners_pix, const int axis_corners[3][2]) { - ImPlot3DPoint range_center = plot.RangeCenter(); for (int a = 0; a < 3; a++) { const ImPlot3DAxis& axis = plot.Axes[a]; if (!axis.HasLabel()) @@ -844,12 +856,13 @@ void RenderAxisLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImP continue; // Position at the end of the axis - ImPlot3DPoint label_pos = (corners[idx0] + corners[idx1]) * 0.5f; + ImPlot3DPoint label_pos = (PlotToNDC(corners[idx0]) + PlotToNDC(corners[idx1])) * 0.5f; + ImPlot3DPoint center_dir = label_pos.Normalized(); // Add offset - label_pos += (label_pos - range_center) * 0.4f; + label_pos += center_dir * 0.3f; // Convert to pixel coordinates - ImVec2 label_pos_pix = PlotToPixels(label_pos); + ImVec2 label_pos_pix = NDCToPixels(label_pos); // Adjust label position and angle ImU32 col_ax_txt = GetStyleColorU32(ImPlot3DCol_AxisText); @@ -869,7 +882,7 @@ void RenderAxisLabels(ImDrawList* draw_list, const ImPlot3DPlot& plot, const ImP // Function to compute active faces based on the rotation // If the plot is close to 2D, plane_2d is set to the plane index (0 -> YZ, 1 -> XZ, 2 -> XY) // plane_2d is set to -1 otherwise -void ComputeActiveFaces(bool* active_faces, const ImPlot3DQuat& rotation, int* plane_2d = nullptr) { +void ComputeActiveFaces(bool* active_faces, const ImPlot3DQuat& rotation, const ImPlot3DAxis* axes, int* plane_2d = nullptr) { if (plane_2d) *plane_2d = -1; @@ -888,7 +901,8 @@ void ComputeActiveFaces(bool* active_faces, const ImPlot3DQuat& rotation, int* p num_deg++; } else { // Otherwise, determine based on the Z component - active_faces[i] = rot_face_n[i].z < 0.0f; + bool is_inverted = ImHasFlag(axes[i].Flags, ImPlot3DAxisFlags_Invert); + active_faces[i] = is_inverted ? (rot_face_n[i].z > 0.0f) : (rot_face_n[i].z < 0.0f); // Set this plane as possible 2d plane if (plane_2d) *plane_2d = i; @@ -920,16 +934,14 @@ void ComputeBoxCornersPix(ImVec2* corners_pix, const ImPlot3DPoint* corners) { void RenderPlotBox(ImDrawList* draw_list, const ImPlot3DPlot& plot) { // Get plot parameters - const ImRect& plot_area = plot.PlotRect; const ImPlot3DQuat& rotation = plot.Rotation; ImPlot3DPoint range_min = plot.RangeMin(); ImPlot3DPoint range_max = plot.RangeMax(); - ImPlot3DPoint range_center = plot.RangeCenter(); // Compute active faces bool active_faces[3]; int plane_2d = -1; - ComputeActiveFaces(active_faces, rotation, &plane_2d); + ComputeActiveFaces(active_faces, rotation, plot.Axes, &plane_2d); bool is_2d = plane_2d != -1; // Compute box corners in plot space @@ -1016,7 +1028,7 @@ void RenderPlotBox(ImDrawList* draw_list, const ImPlot3DPlot& plot) { axis_corners[y_axis][1] = y_corner; } } else { - int index = (active_faces[0] << 2) | (active_faces[1] << 1) | (active_faces[2]); + int index = ((int)active_faces[0] << 2) | ((int)active_faces[1] << 1) | ((int)active_faces[2]); for (int a = 0; a < 3; a++) { axis_corners[a][0] = axis_corners_lookup_3d[index][a][0]; axis_corners[a][1] = axis_corners_lookup_3d[index][a][1]; @@ -1048,7 +1060,7 @@ int Formatter_Default(float value, char* buff, int size, void* data) { double NiceNum(double x, bool round) { double f; double nf; - int expv = (int)floor(ImLog10(x)); + int expv = (int)floor(ImLog10((float)x)); f = x / ImPow(10.0, (double)expv); if (round) if (f < 1.5) @@ -1070,11 +1082,11 @@ double NiceNum(double x, bool round) { return nf * ImPow(10.0, expv); } -void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, ImPlot3DFormatter formatter, void* formatter_data) { +void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, float pixels, ImPlot3DFormatter formatter, void* formatter_data) { if (range.Min == range.Max) return; - const int nMinor = 5; - const int nMajor = 3; + const int nMinor = ImMin(ImMax(1, (int)IM_ROUND(pixels / 30.0f)), 5); + const int nMajor = ImMax(2, (int)IM_ROUND(pixels / 80.0f)); const int max_ticks_labels = 7; const double nice_range = NiceNum(range.Size() * 0.99, false); const double interval = NiceNum(nice_range / (nMajor - 1), true); @@ -1082,23 +1094,22 @@ void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, ImPlot3 const double graphmax = ceil(range.Max / interval) * interval; bool first_major_set = false; int first_major_idx = 0; - const int idx0 = ticker.TickCount(); // ticker may have user custom ticks - ImVec2 total_size(0, 0); + const int idx0 = ticker.TickCount(); // Ticker may have user custom ticks for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { - // is this zero? combat zero formatting issues + // Is this zero? combat zero formatting issues if (major - interval < 0 && major + interval > 0) major = 0; - if (range.Contains(major)) { + if (range.Contains((float)major)) { if (!first_major_set) { first_major_idx = ticker.TickCount(); first_major_set = true; } - total_size += ticker.AddTick(major, true, true, formatter, formatter_data).LabelSize; + ticker.AddTick(major, true, true, formatter, formatter_data); } for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; - if (range.Contains(minor)) { - total_size += ticker.AddTick(minor, false, true, formatter, formatter_data).LabelSize; + if (range.Contains((float)minor)) { + ticker.AddTick(minor, false, true, formatter, formatter_data); } } } @@ -1112,10 +1123,24 @@ void Locator_Default(ImPlot3DTicker& ticker, const ImPlot3DRange& range, ImPlot3 } } +void AddTicksCustom(const double* values, const char* const labels[], int n, ImPlot3DTicker& ticker, ImPlot3DFormatter formatter, void* data) { + for (int i = 0; i < n; ++i) { + if (labels != nullptr) + ticker.AddTick(values[i], false, true, labels[i]); + else + ticker.AddTick(values[i], false, true, formatter, data); + } +} + //------------------------------------------------------------------------------ // [SECTION] Context Menus //------------------------------------------------------------------------------ +static const char* axis_contexts[3] = {"##XAxisContext", "##YAxisContext", "##ZAxisContext"}; +static const char* axis_labels[3] = {"X-Axis", "Y-Axis", "Z-Axis"}; +static const char* plane_contexts[3] = {"##YZPlaneContext", "##XZPlaneContext", "##XYPlaneContext"}; +static const char* plane_labels[3] = {"YZ-Plane", "XZ-Plane", "XY-Plane"}; + bool ShowLegendContextMenu(ImPlot3DLegend& legend, bool visible) { const float s = ImGui::GetFrameHeight(); bool ret = false; @@ -1180,6 +1205,12 @@ void ShowAxisContextMenu(ImPlot3DAxis& axis) { ImGui::CheckboxFlags("Auto-Fit", (unsigned int*)&axis.Flags, ImPlot3DAxisFlags_AutoFit); ImGui::Separator(); + bool inverted = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert); + if (ImGui::Checkbox("Invert", &inverted)) + ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_Invert); + + ImGui::Separator(); + ImGui::BeginDisabled(axis.Label.empty()); if (ImGui::Checkbox("Label", &label)) ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoLabel); @@ -1193,13 +1224,22 @@ void ShowAxisContextMenu(ImPlot3DAxis& axis) { ImFlipFlag(axis.Flags, ImPlot3DAxisFlags_NoTickLabels); } +void ShowPlaneContextMenu(ImPlot3DPlot& plot, int plane_idx) { + for (int i = 0; i < 3; i++) { + if (i == plane_idx) + continue; + ImPlot3DAxis& axis = plot.Axes[i]; + ImGui::PushID(i); + if (ImGui::BeginMenu(axis.HasLabel() ? axis.GetLabel() : axis_labels[i])) { + ShowAxisContextMenu(axis); + ImGui::EndMenu(); + } + ImGui::PopID(); + } +} + void ShowPlotContextMenu(ImPlot3DPlot& plot) { - ImPlot3DContext& gp = *GImPlot3D; - const bool owns_legend = gp.CurrentItems == &plot.Items; - char buf[16] = {}; - - const char* axis_labels[3] = {"X-Axis", "Y-Axis", "Z-Axis"}; for (int i = 0; i < 3; i++) { ImPlot3DAxis& axis = plot.Axes[i]; ImGui::PushID(i); @@ -1212,6 +1252,22 @@ void ShowPlotContextMenu(ImPlot3DPlot& plot) { } ImGui::Separator(); + + if ((ImGui::BeginMenu("Box"))) { + ImGui::PushItemWidth(75); + float temp_scale[3] = {plot.BoxScale[0], plot.BoxScale[1], plot.BoxScale[2]}; + if (ImGui::DragFloat("Scale X", &temp_scale[0], 0.01f, 0.1f, 3.0f)) + plot.BoxScale[0] = ImMax(temp_scale[0], 0.01f); + if (ImGui::DragFloat("Scale Y", &temp_scale[1], 0.01f, 0.1f, 3.0f)) + plot.BoxScale[1] = ImMax(temp_scale[1], 0.01f); + if (ImGui::DragFloat("Scale Z", &temp_scale[2], 0.01f, 0.1f, 3.0f)) + plot.BoxScale[2] = ImMax(temp_scale[2], 0.01f); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + if ((ImGui::BeginMenu("Legend"))) { if (ShowLegendContextMenu(plot.Items.Legend, !ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoLegend))) ImFlipFlag(plot.Flags, ImPlot3DFlags_NoLegend); @@ -1223,6 +1279,10 @@ void ShowPlotContextMenu(ImPlot3DPlot& plot) { if (ImGui::MenuItem("Title", nullptr, plot.HasTitle())) ImFlipFlag(plot.Flags, ImPlot3DFlags_NoTitle); ImGui::EndDisabled(); + if (ImGui::MenuItem("Clip", nullptr, !ImHasFlag(plot.Flags, ImPlot3DFlags_NoClip))) + ImFlipFlag(plot.Flags, ImPlot3DFlags_NoClip); + if (ImGui::MenuItem("Mouse Position", nullptr, !ImHasFlag(plot.Flags, ImPlot3DFlags_NoMouseText))) + ImFlipFlag(plot.Flags, ImPlot3DFlags_NoMouseText); ImGui::EndMenu(); } } @@ -1295,6 +1355,11 @@ bool BeginPlot(const char* title_id, const ImVec2& size, ImPlot3DFlags flags) { // Reset legend plot.Items.Legend.Reset(); + // Reset axes + for (int i = 0; i < ImAxis3D_COUNT; ++i) { + plot.Axes[i].Reset(); + } + // Push frame rect clipping ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); plot.DrawList._Flags = window->DrawList->Flags; @@ -1345,7 +1410,6 @@ void EndPlot() { } // Axis context menus - static const char* axis_contexts[3] = {"##XAxisContext", "##YAxisContext", "##ZAxisContext"}; for (int i = 0; i < 3; i++) { ImPlot3DAxis& axis = plot.Axes[i]; if (ImGui::BeginPopup(axis_contexts[i])) { @@ -1356,6 +1420,16 @@ void EndPlot() { } } + // Plane context menus + for (int i = 0; i < 3; i++) { + if (ImGui::BeginPopup(plane_contexts[i])) { + ImGui::Text("%s", plane_labels[i]); + ImGui::Separator(); + ShowPlaneContextMenu(plot, i); + ImGui::EndPopup(); + } + } + // Plot context menu if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); @@ -1408,6 +1482,41 @@ void SetupAxisLimits(ImAxis3D idx, double min_lim, double max_lim, ImPlot3DCond } } +void SetupAxisFormat(ImAxis3D idx, ImPlot3DFormatter formatter, void* data) { + ImPlot3DContext& gp = *GImPlot3D; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlot3DPlot& plot = *gp.CurrentPlot; + ImPlot3DAxis& axis = plot.Axes[idx]; + axis.Formatter = formatter; + axis.FormatterData = data; +} + +void SetupAxisTicks(ImAxis3D idx, const double* values, int n_ticks, const char* const labels[], bool keep_default) { + ImPlot3DContext& gp = *GImPlot3D; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + ImPlot3DPlot& plot = *gp.CurrentPlot; + ImPlot3DAxis& axis = plot.Axes[idx]; + axis.ShowDefaultTicks = keep_default; + AddTicksCustom(values, + labels, + n_ticks, + axis.Ticker, + axis.Formatter ? axis.Formatter : Formatter_Default, + (axis.Formatter && axis.FormatterData) ? axis.FormatterData : (void*)IMPLOT3D_LABEL_FORMAT); +} + +void SetupAxisTicks(ImAxis3D idx, double v_min, double v_max, int n_ticks, const char* const labels[], bool keep_default) { + ImPlot3DContext& gp = *GImPlot3D; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, + "Setup needs to be called after BeginPlot and before any setup locking functions (e.g. PlotX)!"); + n_ticks = n_ticks < 2 ? 2 : n_ticks; + ImVector temp; + FillRange(temp, n_ticks, v_min, v_max); + SetupAxisTicks(idx, temp.Data, n_ticks, labels, keep_default); +} + void SetupAxes(const char* x_label, const char* y_label, const char* z_label, ImPlot3DAxisFlags x_flags, ImPlot3DAxisFlags y_flags, ImPlot3DAxisFlags z_flags) { SetupAxis(ImAxis3D_X, x_label, x_flags); SetupAxis(ImAxis3D_Y, y_label, y_flags); @@ -1422,6 +1531,15 @@ void SetupAxesLimits(double x_min, double x_max, double y_min, double y_max, dou GImPlot3D->CurrentPlot->FitThisFrame = false; } +void SetupBoxScale(float x, float y, float z) { + ImPlot3DContext& gp = *GImPlot3D; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, + "SetupBoxScale() needs to be called after BeginPlot() and before any setup locking functions (e.g. PlotX)!"); + IM_ASSERT_USER_ERROR(x > 0.0f && y > 0.0f && z > 0.0f, "SetupBoxScale() requires all aspect ratios to be greater than 0!"); + ImPlot3DPlot& plot = *gp.CurrentPlot; + plot.BoxScale = ImPlot3DPoint(x, y, z); +} + void SetupLegend(ImPlot3DLocation location, ImPlot3DLegendFlags flags) { ImPlot3DContext& gp = *GImPlot3D; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr && !gp.CurrentPlot->SetupLocked, @@ -1457,7 +1575,7 @@ ImVec2 PlotToPixels(const ImPlot3DPoint& point) { } ImVec2 PlotToPixels(double x, double y, double z) { - return PlotToPixels(ImPlot3DPoint(x, y, z)); + return PlotToPixels(ImPlot3DPoint((float)x, (float)y, (float)z)); } ImPlot3DRay PixelsToPlotRay(const ImVec2& pix) { @@ -1467,7 +1585,7 @@ ImPlot3DRay PixelsToPlotRay(const ImVec2& pix) { } ImPlot3DRay PixelsToPlotRay(double x, double y) { - return PixelsToPlotRay(ImVec2(x, y)); + return PixelsToPlotRay(ImVec2((float)x, (float)y)); } ImPlot3DPoint PixelsToPlotPlane(const ImVec2& pix, ImPlane3D plane, bool mask) { @@ -1509,22 +1627,22 @@ ImPlot3DPoint PixelsToPlotPlane(const ImVec2& pix, ImPlane3D plane, bool mask) { return O + D * t; }; - // Helper lambda to check if point P is within the plot box - auto InRange = [&](const ImPlot3DPoint& P) { - return P.x >= -0.5 && P.x <= 0.5 && - P.y >= -0.5 && P.y <= 0.5 && - P.z >= -0.5 && P.z <= 0.5; - }; - // Compute which plane to intersect with bool active_faces[3]; - ComputeActiveFaces(active_faces, plot.Rotation); + ComputeActiveFaces(active_faces, plot.Rotation, plot.Axes); // Calculate intersection point with the planes - ImPlot3DPoint P = IntersectPlane(active_faces[plane] ? 0.5 : -0.5); + ImPlot3DPoint P = IntersectPlane(active_faces[plane] ? 0.5f * plot.BoxScale[plane] : -0.5f * plot.BoxScale[plane]); if (P.IsNaN()) return P; + // Helper lambda to check if point P is within the plot box + auto InRange = [&](const ImPlot3DPoint& P) { + return P.x >= -0.5f * plot.BoxScale.x && P.x <= 0.5f * plot.BoxScale.x && + P.y >= -0.5f * plot.BoxScale.y && P.y <= 0.5f * plot.BoxScale.y && + P.z >= -0.5f * plot.BoxScale.z && P.z <= 0.5f * plot.BoxScale.z; + }; + // Handle mask (if one of the intersections is out of range, set it to NAN) if (mask) { switch (plane) { @@ -1547,7 +1665,7 @@ ImPlot3DPoint PixelsToPlotPlane(const ImVec2& pix, ImPlane3D plane, bool mask) { } ImPlot3DPoint PixelsToPlotPlane(double x, double y, ImPlane3D plane, bool mask) { - return PixelsToPlotPlane(ImVec2(x, y), plane, mask); + return PixelsToPlotPlane(ImVec2((float)x, (float)y), plane, mask); } ImVec2 GetPlotPos() { @@ -1583,8 +1701,13 @@ ImPlot3DPoint PlotToNDC(const ImPlot3DPoint& point) { SetupLock(); ImPlot3DPoint ndc_point; - for (int i = 0; i < 3; i++) - ndc_point[i] = plot.Axes[i].PlotToNDC(point[i]); + for (int i = 0; i < 3; i++) { + ImPlot3DAxis& axis = plot.Axes[i]; + float ndc_range = 0.5f * plot.BoxScale[i]; + float t = (point[i] - axis.Range.Min) / (axis.Range.Max - axis.Range.Min); + t *= plot.BoxScale[i]; + ndc_point[i] = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert) ? (ndc_range - t) : (t - ndc_range); + } return ndc_point; } @@ -1595,8 +1718,13 @@ ImPlot3DPoint NDCToPlot(const ImPlot3DPoint& point) { SetupLock(); ImPlot3DPoint plot_point; - for (int i = 0; i < 3; i++) - plot_point[i] = plot.Axes[i].NDCToPlot(point[i]); + for (int i = 0; i < 3; i++) { + ImPlot3DAxis& axis = plot.Axes[i]; + float ndc_range = 0.5f * plot.BoxScale[i]; + float t = ImPlot3D::ImHasFlag(axis.Flags, ImPlot3DAxisFlags_Invert) ? (ndc_range - point[i]) : (point[i] + ndc_range); + t /= plot.BoxScale[i]; + plot_point[i] = axis.Range.Min + t * (axis.Range.Max - axis.Range.Min); + } return plot_point; } @@ -1606,9 +1734,8 @@ ImVec2 NDCToPixels(const ImPlot3DPoint& point) { ImPlot3DPlot& plot = *gp.CurrentPlot; SetupLock(); - float zoom = ImMin(plot.PlotRect.GetWidth(), plot.PlotRect.GetHeight()) / 1.8f; ImVec2 center = plot.PlotRect.GetCenter(); - ImPlot3DPoint point_pix = zoom * (plot.Rotation * point); + ImPlot3DPoint point_pix = plot.GetBoxZoom() * (plot.Rotation * point); point_pix.y *= -1.0f; // Invert y-axis point_pix.x += center.x; point_pix.y += center.y; @@ -1623,7 +1750,7 @@ ImPlot3DRay PixelsToNDCRay(const ImVec2& pix) { SetupLock(); // Calculate zoom factor and plot center - float zoom = ImMin(plot.PlotRect.GetWidth(), plot.PlotRect.GetHeight()) / 1.8f; + float zoom = plot.GetBoxZoom(); ImVec2 center = plot.PlotRect.GetCenter(); // Undo screen transformations to get back to NDC space @@ -1645,7 +1772,6 @@ ImPlot3DRay PixelsToNDCRay(const ImVec2& pix) { ImPlot3DRay NDCRayToPlotRay(const ImPlot3DRay& ray) { ImPlot3DContext& gp = *GImPlot3D; IM_ASSERT_USER_ERROR(gp.CurrentPlot != nullptr, "NDCRayToPlotRay() needs to be called between BeginPlot() and EndPlot()!"); - ImPlot3DPlot& plot = *gp.CurrentPlot; SetupLock(); // Convert NDC origin and a point along the ray to plot coordinates @@ -1697,7 +1823,7 @@ void HandleInput(ImPlot3DPlot& plot) { ImPlot3DPoint range_max = plot.RangeMax(); bool active_faces[3]; int plane_2d = -1; - ComputeActiveFaces(active_faces, rotation, &plane_2d); + ComputeActiveFaces(active_faces, rotation, plot.Axes, &plane_2d); ImPlot3DPoint corners[8]; ComputeBoxCorners(corners, range_min, range_max); ImVec2 corners_pix[8]; @@ -1725,25 +1851,37 @@ void HandleInput(ImPlot3DPlot& plot) { // Check which axes should be transformed (fit/zoom/translate) bool any_axis_held = plot.Axes[0].Held || plot.Axes[1].Held || plot.Axes[2].Held; - static bool transform_axis[3] = {false, false, false}; if (!any_axis_held) { // Only update the transformation axes if the user is not already performing a transformation - transform_axis[0] = transform_axis[1] = transform_axis[2] = false; + plot.Axes[0].Hovered = plot.Axes[1].Hovered = plot.Axes[2].Hovered = false; if (hovered_axis != -1) { - transform_axis[hovered_axis] = true; + plot.Axes[hovered_axis].Hovered = true; } else if (hovered_plane != -1) { - transform_axis[(hovered_plane + 1) % 3] = true; - transform_axis[(hovered_plane + 2) % 3] = true; + plot.Axes[(hovered_plane + 1) % 3].Hovered = true; + plot.Axes[(hovered_plane + 2) % 3].Hovered = true; } else { - transform_axis[0] = transform_axis[1] = transform_axis[2] = true; + plot.Axes[0].Hovered = plot.Axes[1].Hovered = plot.Axes[2].Hovered = true; } } + // Compute plane that is being hovered by mouse + ImPlane3D mouse_plane = ImPlane3D_XY; + if (plane_2d != -1) + mouse_plane = plane_2d; + else if (plot.Axes[1].Hovered && plot.Axes[2].Hovered) + mouse_plane = ImPlane3D_YZ; + else if (plot.Axes[0].Hovered && plot.Axes[2].Hovered) + mouse_plane = ImPlane3D_XZ; + else if (plot.Axes[2].Hovered) + mouse_plane = ImPlane3D_YZ; + ImVec2 mouse_pos = ImGui::GetMousePos(); + ImPlot3DPoint mouse_pos_plot = PixelsToPlotPlane(mouse_pos, mouse_plane, false); + // Handle translation/zoom fit with double click - if (plot_clicked && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Middle)) { + if (plot_clicked && (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Middle))) { plot.FitThisFrame = true; for (int i = 0; i < 3; i++) - plot.Axes[i].FitThisFrame = transform_axis[i]; + plot.Axes[i].FitThisFrame = plot.Axes[i].Hovered; } // Handle auto fit @@ -1757,23 +1895,32 @@ void HandleInput(ImPlot3DPlot& plot) { if (plot.Held && ImGui::IsMouseDown(ImGuiMouseButton_Left)) { ImVec2 delta(IO.MouseDelta.x, IO.MouseDelta.y); - if (transform_axis[0] && transform_axis[1] && transform_axis[2]) { + if (plot.Axes[0].Hovered && plot.Axes[1].Hovered && plot.Axes[2].Hovered) { // Perform unconstrained translation (translate on the viewer plane) // Compute delta_pixels in 3D (invert y-axis) ImPlot3DPoint delta_pixels(delta.x, -delta.y, 0.0f); // Convert delta to NDC space - float zoom = ImMin(plot.PlotRect.GetWidth(), plot.PlotRect.GetHeight()) / 1.8f; + float zoom = plot.GetBoxZoom(); ImPlot3DPoint delta_NDC = plot.Rotation.Inverse() * (delta_pixels / zoom); // Convert delta to plot space - ImPlot3DPoint delta_plot = delta_NDC * (plot.RangeMax() - plot.RangeMin()); + ImPlot3DPoint delta_plot = delta_NDC * (plot.RangeMax() - plot.RangeMin()) / plot.BoxScale; + + // Adjust delta for inverted axes + for (int i = 0; i < 3; i++) { + if (ImHasFlag(plot.Axes[i].Flags, ImPlot3DAxisFlags_Invert)) + delta_plot[i] *= -1; + } // Adjust plot range to translate the plot for (int i = 0; i < 3; i++) { - if (transform_axis[i]) { - plot.Axes[i].SetRange(plot.Axes[i].Range.Min - delta_plot[i], plot.Axes[i].Range.Max - delta_plot[i]); + if (plot.Axes[i].Hovered) { + if (!plot.Axes[i].IsInputLocked()) { + plot.Axes[i].SetMin(plot.Axes[i].Range.Min - delta_plot[i]); + plot.Axes[i].SetMax(plot.Axes[i].Range.Max - delta_plot[i]); + } plot.Axes[i].Held = true; } // If no axis was held before (user started translating in this frame), set the held edge/plane indices @@ -1782,32 +1929,22 @@ void HandleInput(ImPlot3DPlot& plot) { plot.HeldPlaneIdx = hovered_plane_idx; } } - } else if (transform_axis[0] || transform_axis[1] || transform_axis[2]) { + } else if (plot.Axes[0].Hovered || plot.Axes[1].Hovered || plot.Axes[2].Hovered) { // Translate along plane/axis // Mouse delta in pixels - ImVec2 mouse_pos = ImGui::GetMousePos(); ImVec2 mouse_delta(IO.MouseDelta.x, IO.MouseDelta.y); - // TODO Choose best plane given transform_axis and current view - // For now it crashes when transforming only one axis in the 2D view - ImPlane3D plane = ImPlane3D_XY; - if (transform_axis[1] && transform_axis[2]) - plane = ImPlane3D_YZ; - else if (transform_axis[0] && transform_axis[2]) - plane = ImPlane3D_XZ; - else if (transform_axis[2]) - plane = ImPlane3D_YZ; - - ImPlot3DPoint mouse_plot = PixelsToPlotPlane(mouse_pos, plane, false); - ImPlot3DPoint mouse_delta_plot = PixelsToPlotPlane(mouse_pos + mouse_delta, plane, false); - ImPlot3DPoint delta_plot = mouse_delta_plot - mouse_plot; + ImPlot3DPoint mouse_delta_plot = PixelsToPlotPlane(mouse_pos + mouse_delta, mouse_plane, false); + ImPlot3DPoint delta_plot = mouse_delta_plot - mouse_pos_plot; // Apply translation to the selected axes for (int i = 0; i < 3; i++) { - if (transform_axis[i]) { - plot.Axes[i].SetRange(plot.Axes[i].Range.Min - delta_plot[i], - plot.Axes[i].Range.Max - delta_plot[i]); + if (plot.Axes[i].Hovered) { + if (!plot.Axes[i].IsInputLocked()) { + plot.Axes[i].SetMin(plot.Axes[i].Range.Min - delta_plot[i]); + plot.Axes[i].SetMax(plot.Axes[i].Range.Max - delta_plot[i]); + } plot.Axes[i].Held = true; } if (!any_axis_held) { @@ -1819,7 +1956,7 @@ void HandleInput(ImPlot3DPlot& plot) { } // Handle context click with right mouse button - if (plot.Held && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) + if (plot.Held && ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !ImPlot3D::ImHasFlag(plot.Flags, ImPlot3DFlags_NoMenus)) plot.ContextClick = true; if (rotating || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) plot.ContextClick = false; @@ -1835,6 +1972,8 @@ void HandleInput(ImPlot3DPlot& plot) { // Compute plane normal ImPlot3DPoint axis_normal = ImPlot3DPoint(0.0f, 0.0f, 0.0f); axis_normal[hovered_plane] = active_faces[hovered_plane] ? -1.0f : 1.0f; + if (ImPlot3D::ImHasFlag(plot.Axes[hovered_plane].Flags, ImPlot3DAxisFlags_Invert)) + axis_normal[hovered_plane] *= -1; // Compute rotation to align the plane normal with the z-axis ImPlot3DQuat align_normal = ImPlot3DQuat::FromTwoVectors(plot.RotationAnimationEnd * axis_normal, ImPlot3DPoint(0.0f, 0.0f, 1.0f)); @@ -1909,13 +2048,37 @@ void HandleInput(ImPlot3DPlot& plot) { float zoom = 1.0f + delta; for (int i = 0; i < 3; i++) { ImPlot3DAxis& axis = plot.Axes[i]; - float center = (axis.Range.Min + axis.Range.Max) * 0.5f; float size = axis.Range.Max - axis.Range.Min; - size *= zoom; - if (transform_axis[i]) { - plot.Axes[i].SetRange(center - size * 0.5f, center + size * 0.5f); + float new_min, new_max; + if (hovered_axis != -1 || hovered_plane != -1) { + // If mouse over the plot box, zoom around the mouse plot position + float new_size = size * zoom; + + // Calculate offset ratio of the mouse position relative to the axis range + float offset = mouse_pos_plot[i] - axis.Range.Min; + float ratio = offset / size; + + // Adjust the axis range to zoom around the mouse position + new_min = mouse_pos_plot[i] - new_size * ratio; + new_max = mouse_pos_plot[i] + new_size * (1.0f - ratio); + } else { + // If mouse is not over the plot box, zoom around the plot center + float center = (axis.Range.Min + axis.Range.Max) * 0.5f; + + // Adjust the axis range to zoom around plot center + new_min = center - zoom * size * 0.5f; + new_max = center + zoom * size * 0.5f; + } + + // Set new range after zoom + if (plot.Axes[i].Hovered) { + if (!plot.Axes[i].IsInputLocked()) { + plot.Axes[i].SetMin(new_min); + plot.Axes[i].SetMax(new_max); + } plot.Axes[i].Held = true; } + // If no axis was held before (user started zoom in this frame), set the held edge/plane indices if (!any_axis_held) { plot.HeldEdgeIdx = hovered_edge_idx; @@ -1931,15 +2094,13 @@ void HandleInput(ImPlot3DPlot& plot) { plot.OpenContextThisFrame = true; } - // TODO Only open context menu if the mouse is not in the middle of double click action - const char* axis_contexts[3] = {"##XAxisContext", "##YAxisContext", "##ZAxisContext"}; if (plot.OpenContextThisFrame) { if (plot.Items.Legend.Hovered) ImGui::OpenPopup("##LegendContext"); else if (hovered_axis != -1) { ImGui::OpenPopup(axis_contexts[hovered_axis]); } else if (hovered_plane != -1) { - ImGui::OpenPopup(axis_contexts[hovered_plane]); + ImGui::OpenPopup(plane_contexts[hovered_plane]); } else if (plot.Hovered) { ImGui::OpenPopup("##PlotContext"); } @@ -1988,8 +2149,10 @@ void SetupLock() { // Compute ticks for (int i = 0; i < 3; i++) { ImPlot3DAxis& axis = plot.Axes[i]; - axis.Ticker.Reset(); - axis.Locator(axis.Ticker, axis.Range, axis.Formatter, axis.FormatterData); + if (axis.ShowDefaultTicks) { + float pixels = plot.GetBoxZoom() * plot.BoxScale[i]; + axis.Locator(axis.Ticker, axis.Range, pixels, axis.Formatter, axis.FormatterData); + } } // Render title @@ -2043,22 +2206,22 @@ struct ImPlot3DStyleVarInfo { static const ImPlot3DStyleVarInfo GPlot3DStyleVarInfo[] = { // Item style - {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlot3DStyle, LineWeight)}, // ImPlot3DStyleVar_LineWeight - {ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlot3DStyle, Marker)}, // ImPlot3DStyleVar_Marker - {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlot3DStyle, MarkerSize)}, // ImPlot3DStyleVar_MarkerSize - {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlot3DStyle, MarkerWeight)}, // ImPlot3DStyleVar_MarkerWeight - {ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlot3DStyle, FillAlpha)}, // ImPlot3DStyleVar_FillAlpha + {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, LineWeight)}, // ImPlot3DStyleVar_LineWeight + {ImGuiDataType_S32, 1, (ImU32)offsetof(ImPlot3DStyle, Marker)}, // ImPlot3DStyleVar_Marker + {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, MarkerSize)}, // ImPlot3DStyleVar_MarkerSize + {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, MarkerWeight)}, // ImPlot3DStyleVar_MarkerWeight + {ImGuiDataType_Float, 1, (ImU32)offsetof(ImPlot3DStyle, FillAlpha)}, // ImPlot3DStyleVar_FillAlpha // Plot style - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, PlotDefaultSize)}, // ImPlot3DStyleVar_Plot3DDefaultSize - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, PlotMinSize)}, // ImPlot3DStyleVar_Plot3DMinSize - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, PlotPadding)}, // ImPlot3DStyleVar_Plot3DPadding + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotDefaultSize)}, // ImPlot3DStyleVar_Plot3DDefaultSize + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotMinSize)}, // ImPlot3DStyleVar_Plot3DMinSize + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, PlotPadding)}, // ImPlot3DStyleVar_Plot3DPadding // Label style - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, LabelPadding)}, // ImPlot3DStyleVar_LabelPaddine - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, LegendPadding)}, // ImPlot3DStyleVar_LegendPadding - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, LegendInnerPadding)}, // ImPlot3DStyleVar_LegendInnerPadding - {ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImPlot3DStyle, LegendSpacing)}, // ImPlot3DStyleVar_LegendSpacing + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LabelPadding)}, // ImPlot3DStyleVar_LabelPaddine + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendPadding)}, // ImPlot3DStyleVar_LegendPadding + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendInnerPadding)}, // ImPlot3DStyleVar_LegendInnerPadding + {ImGuiDataType_Float, 2, (ImU32)offsetof(ImPlot3DStyle, LegendSpacing)}, // ImPlot3DStyleVar_LegendSpacing }; static const ImPlot3DStyleVarInfo* GetPlotStyleVarInfo(ImPlot3DStyleVar idx) { @@ -2624,8 +2787,8 @@ bool ImPlot3DBox::ClipLineSegment(const ImPlot3DPoint& p0, const ImPlot3DPoint& return false; // Far // Compute clipped points - p0_clipped = p0 + d * t0; - p1_clipped = p0 + d * t1; + p0_clipped = p0 + d * (float)t0; + p1_clipped = p0 + d * (float)t1; return true; } @@ -2649,11 +2812,11 @@ bool ImPlot3DRange::Contains(float value) const { ImPlot3DQuat::ImPlot3DQuat(float _angle, const ImPlot3DPoint& _axis) { float half_angle = _angle * 0.5f; - float s = std::sin(half_angle); + float s = ImSin(half_angle); x = s * _axis.x; y = s * _axis.y; z = s * _axis.z; - w = std::cos(half_angle); + w = ImCos(half_angle); } ImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3DPoint& v1) { @@ -2669,7 +2832,7 @@ ImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3 // Handle edge cases: if vectors are very close or identical const float epsilon = 1e-6f; - if (std::fabs(normalized_dot - 1.0f) < epsilon) { + if (ImFabs(normalized_dot - 1.0f) < epsilon) { // v0 and v1 are nearly identical; return an identity quaternion q.x = 0.0f; q.y = 0.0f; @@ -2679,10 +2842,10 @@ ImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3 } // Handle edge case: if vectors are opposite - if (std::fabs(normalized_dot + 1.0f) < epsilon) { + if (ImFabs(normalized_dot + 1.0f) < epsilon) { // v0 and v1 are opposite; choose an arbitrary orthogonal axis - ImPlot3DPoint arbitrary_axis = std::fabs(v0.x) > std::fabs(v0.z) ? ImPlot3DPoint(-v0.y, v0.x, 0.0f) - : ImPlot3DPoint(0.0f, -v0.z, v0.y); + ImPlot3DPoint arbitrary_axis = ImFabs(v0.x) > ImFabs(v0.z) ? ImPlot3DPoint(-v0.y, v0.x, 0.0f) + : ImPlot3DPoint(0.0f, -v0.z, v0.y); arbitrary_axis.Normalize(); q.x = arbitrary_axis.x; q.y = arbitrary_axis.y; @@ -2694,19 +2857,19 @@ ImPlot3DQuat ImPlot3DQuat::FromTwoVectors(const ImPlot3DPoint& v0, const ImPlot3 // General case ImPlot3DPoint axis = v0.Cross(v1); axis.Normalize(); - float angle = std::acos(normalized_dot); + float angle = ImAcos(normalized_dot); float half_angle = angle * 0.5f; - float s = std::sin(half_angle); + float s = ImSin(half_angle); q.x = s * axis.x; q.y = s * axis.y; q.z = s * axis.z; - q.w = std::cos(half_angle); + q.w = ImCos(half_angle); return q; } float ImPlot3DQuat::Length() const { - return std::sqrt(x * x + y * y + z * z + w * w); + return ImSqrt(x * x + y * y + z * z + w * w); } ImPlot3DQuat ImPlot3DQuat::Normalized() const { @@ -2785,12 +2948,12 @@ ImPlot3DQuat ImPlot3DQuat::Slerp(const ImPlot3DQuat& q1, const ImPlot3DQuat& q2, } // Compute the angle and the interpolation factors - float theta_0 = std::acos(dot); // Angle between input quaternions - float theta = theta_0 * t; // Interpolated angle - float sin_theta = std::sin(theta); // Sine of interpolated angle - float sin_theta_0 = std::sin(theta_0); // Sine of original angle + float theta_0 = ImAcos(dot); // Angle between input quaternions + float theta = theta_0 * t; // Interpolated angle + float sin_theta = ImSin(theta); // Sine of interpolated angle + float sin_theta_0 = ImSin(theta_0); // Sine of original angle - float s1 = std::cos(theta) - dot * sin_theta / sin_theta_0; + float s1 = ImCos(theta) - dot * sin_theta / sin_theta_0; float s2 = sin_theta / sin_theta_0; // Interpolate and return the result @@ -2884,7 +3047,6 @@ void ImDrawList3D::SortedMoveToImGuiDrawList() { // Copy indices with triangle sorting based on distance from viewer ImDrawIdx* idx_out = draw_list._IdxWritePtr; ImDrawIdx* idx_in = IdxBuffer.Data; - int triangles_added = 0; for (int i = 0; i < tri_count; i++) { int tri_i = tris[i].tri_idx; int base_idx = tri_i * 3; @@ -2901,7 +3063,6 @@ void ImDrawList3D::SortedMoveToImGuiDrawList() { idx_out[2] = (ImDrawIdx)(i2 + idx_offset); idx_out += 3; - triangles_added++; } draw_list._IdxWritePtr = idx_out; @@ -2945,14 +3106,6 @@ void ImPlot3DAxis::ApplyFit() { FitExtents.Max = -HUGE_VAL; } -float ImPlot3DAxis::PlotToNDC(float value) const { - return (value - Range.Min) / (Range.Max - Range.Min) - 0.5f; -} - -float ImPlot3DAxis::NDCToPlot(float value) const { - return Range.Min + (value + 0.5f) * (Range.Max - Range.Min); -} - //----------------------------------------------------------------------------- // [SECTION] ImPlot3DPlot //----------------------------------------------------------------------------- @@ -2985,6 +3138,10 @@ void ImPlot3DPlot::SetRange(const ImPlot3DPoint& min, const ImPlot3DPoint& max) Axes[2].SetRange(min.z, max.z); } +float ImPlot3DPlot::GetBoxZoom() const { + return ImMin(PlotRect.GetWidth(), PlotRect.GetHeight()) / 1.8f; +} + //----------------------------------------------------------------------------- // [SECTION] ImPlot3DStyle //----------------------------------------------------------------------------- diff --git a/lib/third_party/imgui/implot3d/source/implot3d_demo.cpp b/lib/third_party/imgui/implot3d/source/implot3d_demo.cpp index b4fb1a3ed..397816bad 100644 --- a/lib/third_party/imgui/implot3d/source/implot3d_demo.cpp +++ b/lib/third_party/imgui/implot3d/source/implot3d_demo.cpp @@ -15,11 +15,17 @@ // [SECTION] User Namespace // [SECTION] Helpers // [SECTION] Plots +// [SECTION] Axes // [SECTION] Custom // [SECTION] Demo Window // [SECTION] Style Editor // [SECTION] User Namespace Implementation +// We define this to avoid accidentally using the deprecated API +#ifndef IMPLOT_DISABLE_OBSOLETE_FUNCTIONS +#define IMPLOT_DISABLE_OBSOLETE_FUNCTIONS +#endif + #include "implot3d.h" #include "implot3d_internal.h" @@ -122,7 +128,7 @@ void DemoScatterPlots() { ImPlot3D::PlotScatter("Data 1", xs1, ys1, zs1, 100); ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.25f); ImPlot3D::SetNextMarkerStyle(ImPlot3DMarker_Square, 6, ImPlot3D::GetColormapColor(1), IMPLOT3D_AUTO, ImPlot3D::GetColormapColor(1)); - ImPlot3D::PlotScatter("Data 2", xs2, ys2, zs1, 50); + ImPlot3D::PlotScatter("Data 2", xs2, ys2, zs2, 50); ImPlot3D::PopStyleVar(); ImPlot3D::EndPlot(); } @@ -279,37 +285,91 @@ void DemoQuadPlots() { void DemoSurfacePlots() { constexpr int N = 20; static float xs[N * N], ys[N * N], zs[N * N]; + static float t = 0.0f; + t += ImGui::GetIO().DeltaTime; // Define the range for X and Y - constexpr float range_min = -5.0f; - constexpr float range_max = 5.0f; - constexpr float step = (range_max - range_min) / (N - 1); + constexpr float min_val = -1.0f; + constexpr float max_val = 1.0f; + constexpr float step = (max_val - min_val) / (N - 1); // Populate the xs, ys, and zs arrays for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int idx = i * N + j; - xs[idx] = range_min + j * step; // X values are constant along rows - ys[idx] = range_min + i * step; // Y values are constant along columns - zs[idx] = sinf(sqrt(xs[idx] * xs[idx] + ys[idx] * ys[idx])); // Z = sin(sqrt(X^2 + Y^2)) + xs[idx] = min_val + j * step; // X values are constant along rows + ys[idx] = min_val + i * step; // Y values are constant along columns + zs[idx] = ImSin(2 * t + ImSqrt((xs[idx] * xs[idx] + ys[idx] * ys[idx]))); // z = sin(2t + sqrt(x^2 + y^2)) } } + // Choose fill color + ImGui::Text("Fill color"); + static int selected_fill = 1; // Colormap by default + static ImVec4 solid_color = ImVec4(0.8f, 0.8f, 0.2f, 0.6f); + const char* colormaps[] = {"Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", + "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys"}; + static int sel_colormap = 5; // Jet by default + { + ImGui::Indent(); + + // Choose solid color + ImGui::RadioButton("Solid", &selected_fill, 0); + if (selected_fill == 0) { + ImGui::SameLine(); + ImGui::ColorEdit4("##SurfaceSolidColor", (float*)&solid_color); + } + + // Choose colormap + ImGui::RadioButton("Colormap", &selected_fill, 1); + if (selected_fill == 1) { + ImGui::SameLine(); + ImGui::Combo("##SurfaceColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps)); + } + ImGui::Unindent(); + } + + // Choose range + static bool custom_range = false; + static float range_min = -1.0f; + static float range_max = 1.0f; + ImGui::Checkbox("Custom range", &custom_range); + { + ImGui::Indent(); + + if (!custom_range) + ImGui::BeginDisabled(); + ImGui::SliderFloat("Range min", &range_min, -1.0f, range_max - 0.01f); + ImGui::SliderFloat("Range max", &range_max, range_min + 0.01f, 1.0f); + if (!custom_range) + ImGui::EndDisabled(); + + ImGui::Unindent(); + } + // Begin the plot - ImPlot3D::PushColormap("Hot"); - if (ImPlot3D::BeginPlot("Surface Plots")) { + if (selected_fill == 1) + ImPlot3D::PushColormap(colormaps[sel_colormap]); + if (ImPlot3D::BeginPlot("Surface Plots", ImVec2(-1, 400), ImPlot3DFlags_NoClip)) { // Set styles + ImPlot3D::SetupAxesLimits(-1, 1, -1, 1, -1.5, 1.5); ImPlot3D::PushStyleVar(ImPlot3DStyleVar_FillAlpha, 0.8f); + if (selected_fill == 0) + ImPlot3D::SetNextFillStyle(solid_color); ImPlot3D::SetNextLineStyle(ImPlot3D::GetColormapColor(1)); // Plot the surface - ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N); + if (custom_range) + ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N, (double)range_min, (double)range_max); + else + ImPlot3D::PlotSurface("Wave Surface", xs, ys, zs, N, N); // End the plot ImPlot3D::PopStyleVar(); ImPlot3D::EndPlot(); } - ImPlot3D::PopColormap(); + if (selected_fill == 1) + ImPlot3D::PopColormap(); } void DemoMeshPlots() { @@ -419,8 +479,8 @@ void DemoMarkersAndText() { // Filled markers for (int m = 0; m < ImPlot3DMarker_COUNT; ++m) { - xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5; - ys[1] = ys[0] + ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5; + xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f; + ys[1] = ys[0] + ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f; ImGui::PushID(m); ImPlot3D::SetNextMarkerStyle(m, mk_size, IMPLOT3D_AUTO_COL, mk_weight); @@ -437,8 +497,8 @@ void DemoMarkersAndText() { // Open markers for (int m = 0; m < ImPlot3DMarker_COUNT; ++m) { - xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5; - ys[1] = ys[0] - ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5; + xs[1] = xs[0] + ImCos(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f; + ys[1] = ys[0] - ImSin(zs[0] / float(ImPlot3DMarker_COUNT) * 2 * IM_PI) * 0.5f; ImGui::PushID(m); ImPlot3D::SetNextMarkerStyle(m, mk_size, ImVec4(0, 0, 0, 0), mk_weight); @@ -481,6 +541,53 @@ void DemoNaNValues() { } } +//----------------------------------------------------------------------------- +// [SECTION] Axes +//----------------------------------------------------------------------------- + +void DemoBoxScale() { + constexpr int N = 100; + float xs[N], ys[N], zs[N]; + for (int i = 0; i < N; ++i) { + float t = i / (float)(N - 1); + xs[i] = sinf(t * 2.0f * IM_PI); + ys[i] = cosf(t * 4.0f * IM_PI); + zs[i] = t * 2.0f - 1.0f; + } + + static float scale[3] = {1.0f, 1.0f, 1.0f}; + ImGui::SliderFloat3("Box Scale", scale, 0.1f, 2.0f, "%.2f"); + + if (ImPlot3D::BeginPlot("##BoxScale")) { + ImPlot3D::SetupBoxScale(scale[0], scale[1], scale[2]); + ImPlot3D::PlotLine("3D Curve", xs, ys, zs, N); + ImPlot3D::EndPlot(); + } +} + +void DemoTickLabels() { + static bool custom_ticks = false; + static bool custom_labels = true; + ImGui::Checkbox("Show Custom Ticks", &custom_ticks); + if (custom_ticks) { + ImGui::SameLine(); + ImGui::Checkbox("Show Custom Labels", &custom_labels); + } + const double pi = 3.14; + const char* pi_str[] = {"PI"}; + static double letters_ticks[] = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0}; + static const char* letters_labels[] = {"A", "B", "C", "D", "E", "F"}; + if (ImPlot3D::BeginPlot("##Ticks")) { + ImPlot3D::SetupAxesLimits(2, 5, 0, 1, 0, 1); + if (custom_ticks) { + ImPlot3D::SetupAxisTicks(ImAxis3D_X, &pi, 1, custom_labels ? pi_str : nullptr, true); + ImPlot3D::SetupAxisTicks(ImAxis3D_Y, letters_ticks, 6, custom_labels ? letters_labels : nullptr, false); + ImPlot3D::SetupAxisTicks(ImAxis3D_Z, 0, 1, 6, custom_labels ? letters_labels : nullptr, false); + } + ImPlot3D::EndPlot(); + } +} + //----------------------------------------------------------------------------- // [SECTION] Custom //----------------------------------------------------------------------------- @@ -635,7 +742,6 @@ void ShowDemoWindow(bool* p_open) { if (show_imgui_demo) ImGui::ShowDemoWindow(&show_imgui_demo); - ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(600, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot3D Demo", p_open, ImGuiWindowFlags_MenuBar); @@ -668,6 +774,11 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("NaN Values", DemoNaNValues); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Axes")) { + DemoHeader("Box Scale", DemoBoxScale); + DemoHeader("Tick Labels", DemoTickLabels); + ImGui::EndTabItem(); + } if (ImGui::BeginTabItem("Custom")) { DemoHeader("Custom Styles", DemoCustomStyles); DemoHeader("Custom Rendering", DemoCustomRendering); @@ -862,11 +973,11 @@ void ShowStyleEditor(ImPlot3DStyle* ref) { filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; - if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) - alpha_flags = ImGuiColorEditFlags_None; + if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) + alpha_flags = ImGuiColorEditFlags_AlphaOpaque; ImGui::SameLine(); - if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) - alpha_flags = ImGuiColorEditFlags_AlphaPreview; + if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) + alpha_flags = ImGuiColorEditFlags_None; ImGui::SameLine(); if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; diff --git a/lib/third_party/imgui/implot3d/source/implot3d_items.cpp b/lib/third_party/imgui/implot3d/source/implot3d_items.cpp index e5ce610bb..e8afd5496 100644 --- a/lib/third_party/imgui/implot3d/source/implot3d_items.cpp +++ b/lib/third_party/imgui/implot3d/source/implot3d_items.cpp @@ -266,8 +266,8 @@ void SetNextLineStyle(const ImVec4& col, float 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; + n.Colors[ImPlot3DCol_Fill] = col; + n.FillAlpha = alpha; } void SetNextMarkerStyle(ImPlot3DMarker marker, float size, const ImVec4& fill, float weight, const ImVec4& outline) { @@ -700,14 +700,14 @@ struct RendererQuadFill : RendererBase { 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), - UV({}), - Min(0.0f), - Max(0.0f), - XCount(x_count), - YCount(y_count), - Col(col) {} + RendererSurfaceFill(const _Getter& getter, int x_count, int y_count, ImU32 col, double scale_min, double scale_max) : RendererBase((x_count - 1) * (y_count - 1), 6, 4), + Getter(getter), + XCount(x_count), + YCount(y_count), + Col(col), + ScaleMin(scale_min), + ScaleMax(scale_max) {} + void Init(ImDrawList3D& draw_list_3d) const { UV = draw_list_3d._SharedData->TexUvWhitePixel; @@ -744,8 +744,14 @@ struct RendererSurfaceFill : RendererBase { const ImPlot3DNextItemData& n = GetItemData(); if (n.IsAutoFill) { float alpha = GImPlot3D->NextItemData.FillAlpha; + float min = Min; + float max = Max; + if (ScaleMin != 0.0 || ScaleMax != 0.0) { + min = (float)ScaleMin; + max = (float)ScaleMax; + } for (int i = 0; i < 4; i++) { - ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, Min, Max), 0.0f, 1.0f)); + ImVec4 col = SampleColormap(ImClamp(ImRemap01(p_plot[i].z, min, max), 0.0f, 1.0f)); col.w *= alpha; cols[i] = ImGui::ColorConvertFloat4ToU32(col); } @@ -810,6 +816,8 @@ struct RendererSurfaceFill : RendererBase { const int XCount; const int YCount; const ImU32 Col; + const double ScaleMin; + const double ScaleMax; }; //----------------------------------------------------------------------------- @@ -851,7 +859,7 @@ 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)); + return ImPlot3DPoint((float)IndexerX(idx), (float)IndexerY(idx), (float)IndexerZ(idx)); } const _IndexerX IndexerX; const _IndexerY IndexerY; @@ -1240,14 +1248,14 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() //----------------------------------------------------------------------------- template -void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, ImPlot3DSurfaceFlags flags) { +void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags) { if (BeginItemEx(label_id, getter, flags, ImPlot3DCol_Fill)) { const ImPlot3DNextItemData& n = GetItemData(); // Render fill if (getter.Count >= 4 && n.RenderFill) { const ImU32 col_fill = ImGui::GetColorU32(n.Colors[ImPlot3DCol_Fill]); - RenderPrimitives(getter, x_count, y_count, col_fill); + RenderPrimitives(getter, x_count, y_count, col_fill, scale_min, scale_max); } // Render lines @@ -1267,16 +1275,16 @@ void PlotSurfaceEx(const char* label_id, const _Getter& getter, int x_count, int } } -IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags, int offset, int stride) { +IMPLOT3D_TMP void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride) { int count = x_count * y_count; if (count < 4) return; GetterXYZ, IndexerIdx, IndexerIdx> getter(IndexerIdx(xs, count, offset, stride), IndexerIdx(ys, count, offset, stride), IndexerIdx(zs, count, offset, stride), count); - return PlotSurfaceEx(label_id, getter, x_count, y_count, flags); + return PlotSurfaceEx(label_id, getter, x_count, y_count, scale_min, scale_max, flags); } #define INSTANTIATE_MACRO(T) \ - template IMPLOT3D_API void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, ImPlot3DSurfaceFlags flags, int offset, int stride); + template IMPLOT3D_API void PlotSurface(const char* label_id, const T* xs, const T* ys, const T* zs, int x_count, int y_count, double scale_min, double scale_max, ImPlot3DSurfaceFlags flags, int offset, int stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO diff --git a/plugins/builtin/source/content/data_visualizers.cpp b/plugins/builtin/source/content/data_visualizers.cpp index d4fb5ccd9..bf1b2c4b4 100644 --- a/plugins/builtin/source/content/data_visualizers.cpp +++ b/plugins/builtin/source/content/data_visualizers.cpp @@ -234,9 +234,9 @@ namespace hex::plugin::builtin { std::ignore = upperCase; if (size == 4) - ImGui::ColorButton("##color", ImColor(data[0], data[1], data[2], data[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + ImGui::ColorButton("##color", ImColor(data[0], data[1], data[2], data[3]), ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); else - ImGui::ColorButton("##color", ImColor(0, 0, 0, 0xFF), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + ImGui::ColorButton("##color", ImColor(0, 0, 0, 0xFF), ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); } bool drawEditing(u64 address, u8 *data, size_t size, bool upperCase, bool startedEditing) override { @@ -250,7 +250,7 @@ namespace hex::plugin::builtin { ImGui::OpenPopup("##color_popup"); } - ImGui::ColorButton("##color", ImColor(m_currColor[0], m_currColor[1], m_currColor[2], m_currColor[3]), ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + ImGui::ColorButton("##color", ImColor(m_currColor[0], m_currColor[1], m_currColor[2], m_currColor[3]), ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); if (ImGui::BeginPopup("##color_popup")) { if (ImGui::ColorPicker4("##picker", m_currColor.data(), ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_InputRGB)) {