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"};