imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Logarithmic ```SliderFloat``` issue when using a scientific notation format

Open virmodoetiae opened this issue 11 months ago • 2 comments

Version/Branch of Dear ImGui:

Version 1.91-5, Branch: docking

Back-ends:

imgui_impl_opengl3.cpp

Compiler, OS:

GCC v12.2.0 (via MinGW) on Windows 10

Full config/build information:

Dear ImGui 1.91.5 (19150)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=201703
define: _WIN32
define: _WIN64
define: __MINGW32__
define: __MINGW64__
define: __GNUC__=12
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_glfw
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000481
 NavEnableKeyboard
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigDockingTransparentPayload
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 1024,2048
io.DisplaySize: 512.00,512.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

When using a logarithmic SliderFloat (i.e., with the ImGuiSliderFlags_Logarithmic flag) with a scientific notation format (e.g., "%.2e"), I have noticed that the slider cannot be slid in any direction if the minimum value bound is below 10.

Having a look at the code in imgui_widgets.cpp, in the definition of bool ImGui::SliderBehaviorT(...), I saw this:

if (is_logarithmic)
{
    // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
    const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;
    logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
    zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);
}

I have noticed that ImParseFormatPrecision(format, ...) always returns -1 for scientific notation format. In this case however, this means that logarithmic_zero_epsilon will always end up being 0.1^-1 = 10.0 when scientific notation is used. This variable also appears to precisely determine the minimum possible value (in absolute terms, i.e., the one closest to 0) that can be represented. Hence, the slider ends up not functioning properly if the lower bound is less than 10.

This same issue exists with bool ImGui::DragBehaviorT(...). Also, after checking the code in the latest -docking branch version (1.91-9), this issue still persists. In both these cases I am talking purely from a code perspective, I have not actually tested it.

Regardless, given this issue, I would propose this fix:

if (is_logarithmic)
{
    float decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;
    if ((int)decimal_precision == -1)
    {
        if (v_min*v_max > 0)
        {
            logarithmic_zero_epsilon = std::max(std::abs((float)v_min), 1e-24f);
        }
        else
        {
            float decade_range = 3; // <- Ideally this could be an additional slider parameter, or 
                                    // maybe passed within the format string and parsed accordingly?
            decimal_precision = decade_range-log10f(std::max((float)(v_max-v_min), 1e-24f));
            logarithmic_zero_epsilon = ImPow(0.1f, decimal_precision);
        }
    }
    else
    {
        logarithmic_zero_epsilon = ImPow(0.1f, decimal_precision);
    }
    zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);
}

The idea is that for exponential notation (i.e., if (int)decimal_precision == -1) if the value bounds do not cross 0 (i.e., if v_min*v_max > 0), then the closest possible value to 0 is just the absolute value of the lower bound (limited to some arbitrary value, in this case 1e-24f just for the sake of the example). Conversely, if the value bounds do cross 0 (e.g., v_min = -100 and v_max = 250), then the idea is to set the closest possible zero value such that the longest same-sign domain on either side of the 0 (e.g., in this case the one between 0 and 250) spans exactly 3 decades. The choice of 3 decades is arbitrary, and ideally it should be settable from above (e.g., passable as yet another parameter?). The idea of specifying decades instead of a closest 0 value directly is that it automatically scales with the range of the domain (i.e., v_max-v_min).

I have tested this fix and it works as described.

Either way, this is the issue. I would like to know what you think of it, what you think of my proposed fix, and whether it might be feasible to introduce this approach with a user-specifiable decade_range.

Thank you for your time!

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

ImGui::Begin("SlideFloat Issue");
static float value = 0.5;
ImGui::SliderFloat("##sliderFloat", &value,  0.f, 1.f, "%.2e", ImGuiSliderFlags_Logarithmic);
ImGui::End();

virmodoetiae avatar Mar 15 '25 02:03 virmodoetiae

Linking to #8299

ocornut avatar Mar 15 '25 12:03 ocornut

In case it might be of interest to anyone, this is the final fix to ImGui I ended adopting in my project: https://github.com/virmodoetiae/shaderthing/commit/a3ae831bc414d75fde3fd528de8457afb6d4487d

virmodoetiae avatar Mar 18 '25 22:03 virmodoetiae