From e9197e1e9df7593b07ff14f5e44bbea5983ca555 Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Fri, 31 Jul 2026 05:05:05 -0700 Subject: [PATCH] Use logarithmic sliders in the object launcher. Projectile parameters are positive scale factors, best adjusted multiplicatively. Replace the input boxes with logarithmic sliders spanning 0.1x to 10x the seeded default, which sits at the geometric midpoint of the range; mass spans 0.1x to 100x since heavy projectiles are a common want. Ctrl+Click still allows typing values outside the slider range, and each slider gets a reset button restoring its default. Adds an ImGui_SliderLog widget operating on mjtNum. PiperOrigin-RevId: 957071873 Change-Id: Id46eae28f73308d98681ccbeb2764c949f98f9d9 --- src/experimental/platform/ux/imgui_widgets.cc | 8 ++++++ src/experimental/platform/ux/imgui_widgets.h | 4 +++ .../platform/ux/object_launcher_plugin.cc | 27 +++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/experimental/platform/ux/imgui_widgets.cc b/src/experimental/platform/ux/imgui_widgets.cc index 30115dfd..3698addb 100644 --- a/src/experimental/platform/ux/imgui_widgets.cc +++ b/src/experimental/platform/ux/imgui_widgets.cc @@ -343,6 +343,14 @@ bool ImGui_Slider(const char* name, mjtNum* value, mjtNum min, mjtNum max) { return res; } +bool ImGui_SliderLog(const char* name, mjtNum* value, mjtNum min, mjtNum max) { + constexpr ImGuiDataType type = sizeof(mjtNum) == sizeof(double) + ? ImGuiDataType_Double + : ImGuiDataType_Float; + return ImGui::SliderScalar(name, type, value, &min, &max, "%.3g", + ImGuiSliderFlags_Logarithmic); +} + bool ImGui_ResetButton(const char* id, const char* icon, const char* tooltip) { const float size = ImGui::GetFrameHeight(); diff --git a/src/experimental/platform/ux/imgui_widgets.h b/src/experimental/platform/ux/imgui_widgets.h index a13ed207..ed398308 100644 --- a/src/experimental/platform/ux/imgui_widgets.h +++ b/src/experimental/platform/ux/imgui_widgets.h @@ -476,6 +476,10 @@ void ImGui_EndHSplit(bool open); // ImGui Slider that supports both float and double types. bool ImGui_Slider(const char* name, mjtNum* value, mjtNum min, mjtNum max); +// Logarithmic slider; the geometric mean of min and max sits mid-slider. +// Ctrl+Click values typed by the user may exceed [min, max]. +bool ImGui_SliderLog(const char* name, mjtNum* value, mjtNum min, mjtNum max); + // Small right-aligned reset button on the current line; returns true when // clicked. Use after a widget to give it a visible reset affordance. bool ImGui_ResetButton(const char* id, const char* icon = ICON_FA_UNDO, diff --git a/src/experimental/platform/ux/object_launcher_plugin.cc b/src/experimental/platform/ux/object_launcher_plugin.cc index e8bb4d7f..5bb51253 100644 --- a/src/experimental/platform/ux/object_launcher_plugin.cc +++ b/src/experimental/platform/ux/object_launcher_plugin.cc @@ -33,15 +33,26 @@ class ObjectLauncher { ObjectLauncher() : rng_(std::random_device{}()) {} void UpdateGui() { - using platform::ImGui_Input; + using platform::ImGui_ResetButton; + using platform::ImGui_SliderLog; - ImGui_Input("Size", &size_, - {size_seed_ * 1e-3, size_seed_ * 1e4, size_seed_ * 0.1, size_seed_}); - ImGui_Input("Speed", &speed_, - {speed_seed_ * 1e-3, speed_seed_ * 1e4, speed_seed_ * 0.1, speed_seed_}); - ImGui_Input("Mass", &mass_, - {mass_seed_ * 1e-3, mass_seed_ * 1e4, mass_seed_ * 0.1, mass_seed_}); - ImGui_Input("Life", &lifetime_, {0.0f, 60.0f, 0.1, 1.0}); + constexpr mjtNum kLifeDefault = 5.0; + ImGui_SliderLog("Size", &size_, size_seed_ * 0.1, size_seed_ * 10); + ImGui::BeginDisabled(size_ == size_seed_); + if (ImGui_ResetButton("Size")) size_ = size_seed_; + ImGui::EndDisabled(); + ImGui_SliderLog("Speed", &speed_, speed_seed_ * 0.1, speed_seed_ * 10); + ImGui::BeginDisabled(speed_ == speed_seed_); + if (ImGui_ResetButton("Speed")) speed_ = speed_seed_; + ImGui::EndDisabled(); + ImGui_SliderLog("Mass", &mass_, mass_seed_ * 0.1, mass_seed_ * 100); + ImGui::BeginDisabled(mass_ == mass_seed_); + if (ImGui_ResetButton("Mass")) mass_ = mass_seed_; + ImGui::EndDisabled(); + ImGui_SliderLog("Life", &lifetime_, 0.5, 50.0); + ImGui::BeginDisabled(lifetime_ == kLifeDefault); + if (ImGui_ResetButton("Life")) lifetime_ = kLifeDefault; + ImGui::EndDisabled(); int shape = type_ == mjGEOM_BOX ? 0 : 1; const char* names[] = {"Box", "Sphere"};