Update themes to differentiate from default dear_imgui themes and add a classic theme that's similar to simulate.

PiperOrigin-RevId: 843313013
Change-Id: Id01841097f32a5b01acd8fcf59392a73acad8f27
This commit is contained in:
Haroon Qureshi
2025-12-11 11:45:08 -08:00
committed by Copybara-Service
parent 3c19b63ed3
commit e07adc387a
4 changed files with 450 additions and 168 deletions
+250 -64
View File
@@ -44,12 +44,163 @@ static ImVec2 GetFlexElementSize(int num_cols) {
void SetupTheme(GuiTheme theme) {
ImGuiStyle& s = ImGui::GetStyle();
ImVec4* c = s.Colors;
if (theme == GuiTheme::kDark) {
ImGui::StyleColorsDark(&s);
} else {
c[ImGuiCol_Text] = ImVec4(1.00, 1.00, 1.00, 1.00);
c[ImGuiCol_TextDisabled] = ImVec4(0.40, 0.40, 0.40, 1.00);
c[ImGuiCol_ChildBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
c[ImGuiCol_WindowBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
c[ImGuiCol_PopupBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
c[ImGuiCol_Border] = ImVec4(0.12, 0.12, 0.12, 0.71);
c[ImGuiCol_BorderShadow] = ImVec4(1.00, 1.00, 1.00, 0.06);
c[ImGuiCol_FrameBg] = ImVec4(0.42, 0.42, 0.42, 0.54);
c[ImGuiCol_FrameBgHovered] = ImVec4(0.42, 0.42, 0.42, 0.40);
c[ImGuiCol_FrameBgActive] = ImVec4(0.56, 0.56, 0.56, 0.67);
c[ImGuiCol_TitleBg] = ImVec4(0.19, 0.19, 0.19, 1.00);
c[ImGuiCol_TitleBgActive] = ImVec4(0.22, 0.22, 0.22, 1.00);
c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.17, 0.17, 0.17, 0.90);
c[ImGuiCol_MenuBarBg] = ImVec4(0.34, 0.34, 0.34, 1.00);
c[ImGuiCol_ScrollbarBg] = ImVec4(0.24, 0.24, 0.24, 0.53);
c[ImGuiCol_ScrollbarGrab] = ImVec4(0.41, 0.41, 0.41, 1.00);
c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.52, 0.52, 0.52, 1.00);
c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
c[ImGuiCol_CheckMark] = ImVec4(0.65, 0.65, 0.65, 1.00);
c[ImGuiCol_SliderGrab] = ImVec4(0.52, 0.52, 0.52, 1.00);
c[ImGuiCol_SliderGrabActive] = ImVec4(0.64, 0.64, 0.64, 1.00);
c[ImGuiCol_Button] = ImVec4(0.54, 0.54, 0.54, 0.35);
c[ImGuiCol_ButtonHovered] = ImVec4(0.52, 0.52, 0.52, 0.59);
c[ImGuiCol_ButtonActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
c[ImGuiCol_Header] = ImVec4(0.38, 0.38, 0.38, 1.00);
c[ImGuiCol_HeaderHovered] = ImVec4(0.47, 0.47, 0.47, 1.00);
c[ImGuiCol_HeaderActive] = ImVec4(0.76, 0.76, 0.76, 0.77);
c[ImGuiCol_Separator] = ImVec4(0.00, 0.00, 0.00, 0.18);
c[ImGuiCol_SeparatorHovered] = ImVec4(0.70, 0.67, 0.60, 0.29);
c[ImGuiCol_SeparatorActive] = ImVec4(0.70, 0.67, 0.60, 0.67);
c[ImGuiCol_ResizeGrip] = ImVec4(0.26, 0.59, 0.98, 0.25);
c[ImGuiCol_ResizeGripHovered] = ImVec4(0.26, 0.59, 0.98, 0.67);
c[ImGuiCol_ResizeGripActive] = ImVec4(0.26, 0.59, 0.98, 0.95);
c[ImGuiCol_PlotLines] = ImVec4(0.61, 0.61, 0.61, 1.00);
c[ImGuiCol_PlotLinesHovered] = ImVec4(1.00, 0.43, 0.35, 1.00);
c[ImGuiCol_PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00);
c[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00, 0.60, 0.00, 1.00);
c[ImGuiCol_TextSelectedBg] = ImVec4(0.73, 0.73, 0.73, 0.35);
c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80, 0.80, 0.80, 0.35);
c[ImGuiCol_DragDropTarget] = ImVec4(1.00, 1.00, 0.00, 0.90);
c[ImGuiCol_NavHighlight] = ImVec4(0.26, 0.59, 0.98, 1.00);
c[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00, 1.00, 1.00, 0.70);
c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80, 0.80, 0.80, 0.20);
c[ImGuiCol_DockingEmptyBg] = ImVec4(0.38, 0.38, 0.38, 1.00);
c[ImGuiCol_Tab] = ImVec4(0.25, 0.25, 0.25, 1.00);
c[ImGuiCol_TabHovered] = ImVec4(0.40, 0.40, 0.40, 1.00);
c[ImGuiCol_TabActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
c[ImGuiCol_TabUnfocused] = ImVec4(0.25, 0.25, 0.25, 1.00);
c[ImGuiCol_TabUnfocusedActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
c[ImGuiCol_DockingPreview] = ImVec4(0.85, 0.85, 0.85, 0.28);
c[ImGuiCol_WindowBg].w = 1.0f;
} else if (theme == GuiTheme::kLight) {
ImGui::StyleColorsLight(&s);
ImVec4 white = ImVec4(1.00, 1.00, 1.00, 1.00);
ImVec4 transparent = ImVec4(0.00, 0.00, 0.00, 0.00);
ImVec4 dark = ImVec4(0.00, 0.00, 0.00, 0.20);
ImVec4 darker = ImVec4(0.00, 0.00, 0.00, 0.50);
ImVec4 background = ImVec4(0.95, 0.95, 0.95, 1.00);
ImVec4 text = ImVec4(0.10, 0.10, 0.10, 1.00);
ImVec4 border = ImVec4(0.60, 0.60, 0.60, 1.00);
ImVec4 grab = ImVec4(0.69, 0.69, 0.69, 1.00);
ImVec4 header = ImVec4(0.86, 0.86, 0.86, 1.00);
ImVec4 active = ImVec4(0.00, 0.47, 0.84, 1.00);
ImVec4 hover = ImVec4(0.00, 0.47, 0.84, 0.20);
c[ImGuiCol_Text] = text;
c[ImGuiCol_WindowBg] = background;
c[ImGuiCol_ChildBg] = background;
c[ImGuiCol_PopupBg] = white;
c[ImGuiCol_Border] = border;
c[ImGuiCol_BorderShadow] = transparent;
c[ImGuiCol_Button] = header;
c[ImGuiCol_ButtonHovered] = hover;
c[ImGuiCol_ButtonActive] = active;
c[ImGuiCol_FrameBg] = white;
c[ImGuiCol_FrameBgHovered] = hover;
c[ImGuiCol_FrameBgActive] = active;
c[ImGuiCol_MenuBarBg] = header;
c[ImGuiCol_Header] = header;
c[ImGuiCol_HeaderHovered] = hover;
c[ImGuiCol_HeaderActive] = active;
c[ImGuiCol_CheckMark] = text;
c[ImGuiCol_SliderGrab] = grab;
c[ImGuiCol_SliderGrabActive] = darker;
c[ImGuiCol_ScrollbarBg] = header;
c[ImGuiCol_ScrollbarGrab] = grab;
c[ImGuiCol_ScrollbarGrabHovered] = dark;
c[ImGuiCol_ScrollbarGrabActive] = darker;
} else {
ImGui::StyleColorsDark(&s);
ImVec4 black = ImVec4(0.00, 0.00, 0.00, 1.0);
ImVec4 window = ImVec4(0.25, 0.25, 0.25, 1.0);
ImVec4 font_active = ImVec4(1.00, 1.00, 1.00, 1.0);
ImVec4 font_inactive = ImVec4(0.50, 0.50, 0.50, 1.0);
ImVec4 thumb = ImVec4(0.12, 0.12, 0.12, 1.0);
ImVec4 section = ImVec4(0.40, 0.15, 0.15, 1.0);
ImVec4 button = ImVec4(0.60, 0.40, 0.40, 1.0);
ImVec4 check = ImVec4(0.40, 0.40, 0.70, 1.0);
ImVec4 frame = ImVec4(0.40, 0.30, 0.40, 1.0);
ImVec4 slider = ImVec4(0.60, 0.40, 0.60, 1.0);
c[ImGuiCol_WindowBg] = window;
c[ImGuiCol_ChildBg] = black;
c[ImGuiCol_PopupBg] = window;
c[ImGuiCol_Text] = font_active;
c[ImGuiCol_TextDisabled] = font_inactive;
c[ImGuiCol_CheckMark] = font_active;
c[ImGuiCol_Header] = section;
c[ImGuiCol_HeaderHovered] = section;
c[ImGuiCol_HeaderActive] = section;
c[ImGuiCol_TitleBgActive] = window;
c[ImGuiCol_ScrollbarBg] = window;
c[ImGuiCol_ScrollbarGrab] = thumb;
c[ImGuiCol_ScrollbarGrabHovered] = thumb;
c[ImGuiCol_ScrollbarGrabActive] = thumb;
c[ImGuiCol_FrameBg] = frame;
c[ImGuiCol_FrameBgHovered] = frame;
c[ImGuiCol_FrameBgActive] = frame;
c[ImGuiCol_SliderGrab] = slider;
c[ImGuiCol_SliderGrabActive] = slider;
c[ImGuiCol_Button] = window;
c[ImGuiCol_ButtonHovered] = button;
c[ImGuiCol_ButtonActive] = button;
c[ImGuiCol_Tab] = window;
c[ImGuiCol_TabHovered] = check;
c[ImGuiCol_TabSelected] = check;
c[ImGuiCol_TabDimmed] = window;
c[ImGuiCol_TabDimmedSelected] = check;
}
s.FrameBorderSize = 1;
int hspacing = 4;
int vspacing = 6;
float rounding = 4.0f;
s.DisplaySafeAreaPadding = ImVec2(0, 0);
s.WindowPadding = ImVec2(hspacing, vspacing);
s.FramePadding = ImVec2(hspacing, vspacing);
s.ItemSpacing = ImVec2(hspacing, vspacing);
s.ItemInnerSpacing = ImVec2(hspacing, vspacing);
s.WindowRounding = rounding;
s.FrameRounding = rounding;
s.TabRounding = rounding;
s.ScrollbarRounding = rounding;
s.ChildRounding = rounding;
s.GrabRounding = rounding;
s.PopupRounding = rounding;
s.WindowBorderSize = 0.0f;
s.FrameBorderSize = 1.0f;
s.PopupBorderSize = 1.0f;
s.IndentSpacing = 20.0f;
s.ScrollbarSize = 12.0f;
s.GrabMinSize = 5.0f;
s.WindowMenuButtonPosition = ImGuiDir_None;
s.TabCloseButtonMinWidthSelected = 0.0f;
s.DockingNodeHasCloseButton = false;
}
ImVec4 ConfigureDockingLayout() {
@@ -112,37 +263,48 @@ ImVec4 ConfigureDockingLayout() {
ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
style.Var(ImGuiStyleVar_WindowBorderSize, 0.0f);
style.Var(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Dockspace", nullptr, kWorkspaceFlags);
style.Reset();
const ImGuiDockNodeFlags kDockSpaceFlags =
ImGuiDockNodeFlags_PassthruCentralNode |
ImGuiDockNodeFlags_NoDockingOverCentralNode |
ImGuiDockNodeFlags_AutoHideTabBar;
ImGui::DockSpace(root, ImVec2(0.0f, 0.0f), kDockSpaceFlags);
ImGui::End();
const ImGuiWindowFlags kFixedFlags = ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoDocking;
// Main workspace area in which we can dock other windows.
{
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
style.Var(ImGuiStyleVar_WindowBorderSize, 0.0f);
style.Var(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Dockspace", nullptr, kWorkspaceFlags);
const ImGuiDockNodeFlags kDockSpaceFlags =
ImGuiDockNodeFlags_PassthruCentralNode |
ImGuiDockNodeFlags_NoDockingOverCentralNode |
ImGuiDockNodeFlags_AutoHideTabBar;
ImGui::DockSpace(root, ImVec2(0.0f, 0.0f), kDockSpaceFlags);
ImGui::End();
}
// Toolbar is fixed at the top.
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight), ImGuiCond_Always);
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
ImGui::End();
{
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight), ImGuiCond_Always);
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
ImGui::End();
}
// StatusBar is fixed at the bottom.
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight), ImGuiCond_Always);
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
ImGui::End();
{
platform::ScopedStyle style;
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight), ImGuiCond_Always);
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
ImGui::End();
}
const int settings_width = dockspace_size.x * kOptionsRelWidth;
const int inspector_width = dockspace_size.x * kInspectorRelWidth;
@@ -154,6 +316,10 @@ ImVec4 ConfigureDockingLayout() {
}
void SensorGui(const mjModel* model, const mjData* data) {
if (model->nsensor == 0) {
return;
}
ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.1f, 0.1f));
if (ImPlot::BeginPlot("Sensors", ImVec2(-1, 0),
ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) {
@@ -356,6 +522,9 @@ void StateGui(const mjModel* model, mjData* data, std::vector<mjtNum>& state,
void WatchGui(const mjModel* model, const mjData* data, char* field_name,
int field_len, int& field_index) {
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
ImGui::InputText("Field", field_name, field_len);
ImGui::InputInt("Index", &field_index);
const mjtNum* value = static_cast<const mjtNum*>(
@@ -375,6 +544,8 @@ void WatchGui(const mjModel* model, const mjData* data, char* field_name,
ImGui::InputText("Value", buf, sizeof(buf), ImGuiInputTextFlags_ReadOnly);
ImGui::EndDisabled();
}
ImGui::PopItemWidth();
}
void PhysicsGui(mjModel* model, float min_width) {
@@ -383,6 +554,9 @@ void PhysicsGui(mjModel* model, float min_width) {
const int num_cols = std::clamp(
static_cast<int>(std::floor(available_width / min_width)), 1, 6);
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
auto& opt = model->opt;
const char* opts0[] = {"Euler", "RK4", "implicit", "implicitfast"};
@@ -397,21 +571,15 @@ void PhysicsGui(mjModel* model, float min_width) {
const char* opts3[] = {"PGS", "CG", "Newton"};
ImGui::Combo("Solver", &opt.solver, opts3, IM_ARRAYSIZE(opts3));
if (ImGui::TreeNodeEx("Disable Flags", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("##DisableFlagsTable", num_cols)) {
if (ImGui::TreeNodeEx("Flags", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("##PhysicsFlagsTable", num_cols)) {
const ImVec2 size = GetFlexElementSize(num_cols);
for (int i = 0; i < mjNDISABLE; ++i) {
ImGui::TableNextColumn();
ImGui_BitToggle(mjDISABLESTRING[i], &opt.disableflags, 1 << i, size);
int flipped = ~opt.disableflags;
ImGui_BitToggle(mjDISABLESTRING[i], &flipped, 1 << i, size);
opt.disableflags = ~flipped;
}
ImGui::EndTable();
}
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Enable Flags", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("##EnableFlagsTable", num_cols)) {
const ImVec2 size = GetFlexElementSize(num_cols);
for (int i = 0; i < mjNENABLE; ++i) {
ImGui::TableNextColumn();
ImGui_BitToggle(mjENABLESTRING[i], &opt.enableflags, 1 << i, size);
@@ -421,14 +589,16 @@ void PhysicsGui(mjModel* model, float min_width) {
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Actuator Group Disable")) {
if (ImGui::BeginTable("##EnableFlagsTable", num_cols)) {
if (ImGui::TreeNodeEx("Actuator Groups")) {
if (ImGui::BeginTable("##ActuatorGroupsTable", num_cols)) {
const ImVec2 size = GetFlexElementSize(num_cols);
for (int i = 0; i < 6; ++i) {
char label[64];
std::snprintf(label, sizeof(label), "Act Group %d", i);
ImGui::TableNextColumn();
ImGui_BitToggle(label, &opt.disableactuator, 1 << i, size);
int flipped = ~opt.disableactuator;
ImGui_BitToggle(label, &flipped, 1 << i, size);
opt.disableactuator = ~flipped;
}
ImGui::EndTable();
}
@@ -436,41 +606,40 @@ void PhysicsGui(mjModel* model, float min_width) {
};
if (ImGui::TreeNodeEx("Algorithmic Parameters")) {
float w = ImGui::GetWindowWidth() * .6f;
ImGui_Input("Timestep", &opt.timestep, {0, 1, 0.01, 0.1, w});
ImGui_Input("Iterations", &opt.iterations, {0, 1000, 1, 10, w});
ImGui_Input("Tolerance", &opt.tolerance, {0, 1, 1e-7, 1e-6, w});
ImGui_Input("LS Iter", &opt.ls_iterations, {0, 100, 1, 0.1, w});
ImGui_Input("LS Tol", &opt.ls_tolerance, {0, 0.1, 0.01, 0.1, w});
ImGui_Input("Noslip Iter", &opt.noslip_iterations, {0, 1000, 1, 100, w});
ImGui_Input("Noslip Tol", &opt.noslip_tolerance, {0, 1, 0.01, 0.1, w});
ImGui_Input("CCD Iter", &opt.ccd_iterations, {0, 1000, 1, 100, w});
ImGui_Input("CCD Tol", &opt.ccd_tolerance, {0, 1, 0.01, 0.1, w});
ImGui_Input("Sleep Tol", &opt.sleep_tolerance, {0, 1, 0.01, 0.1, w});
ImGui_Input("SDF Iter", &opt.sdf_iterations, {1, 20, 1, 10, w});
ImGui_Input("SDF Init", &opt.sdf_initpoints, {1, 100, 1, 10, w});
ImGui_Input("Timestep", &opt.timestep, {0, 1, 0.01, 0.1});
ImGui_Input("Iterations", &opt.iterations, {0, 1000, 1, 10});
ImGui_Input("Tolerance", &opt.tolerance, {0, 1, 1e-7, 1e-6});
ImGui_Input("LS Iter", &opt.ls_iterations, {0, 100, 1, 0.1});
ImGui_Input("LS Tol", &opt.ls_tolerance, {0, 0.1, 0.01, 0.1});
ImGui_Input("Noslip Iter", &opt.noslip_iterations, {0, 1000, 1, 100});
ImGui_Input("Noslip Tol", &opt.noslip_tolerance, {0, 1, 0.01, 0.1});
ImGui_Input("CCD Iter", &opt.ccd_iterations, {0, 1000, 1, 100});
ImGui_Input("CCD Tol", &opt.ccd_tolerance, {0, 1, 0.01, 0.1});
ImGui_Input("Sleep Tol", &opt.sleep_tolerance, {0, 1, 0.01, 0.1});
ImGui_Input("SDF Iter", &opt.sdf_iterations, {1, 20, 1, 10});
ImGui_Input("SDF Init", &opt.sdf_initpoints, {1, 100, 1, 10});
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Physical Parameters")) {
float w = ImGui::GetWindowWidth() * .6f;
ImGui_InputN("Gravity", opt.gravity, 3, {.width = w});
ImGui_InputN("Wind", opt.wind, 3, {.width = w});
ImGui_InputN("Magnetic", opt.magnetic, 3, {.width = w});
ImGui_Input("Density", &opt.density, {.min = .1, .max = 1, .width = w});
ImGui_Input("Viscosity", &opt.viscosity, {.min = .1, .max = 1, .width = w});
ImGui_Input("Imp Ratio", &opt.impratio, {.min = .1, .max = 1, .width = w});
ImGui_InputN("Gravity", opt.gravity, 3);
ImGui_InputN("Wind", opt.wind, 3);
ImGui_InputN("Magnetic", opt.magnetic, 3);
ImGui_Input("Density", &opt.density, {.min = .1, .max = 1});
ImGui_Input("Viscosity", &opt.viscosity, {.min = .1, .max = 1});
ImGui_Input("Imp Ratio", &opt.impratio, {.min = .1, .max = 1});
ImGui::TreePop();
};
if (ImGui::TreeNodeEx("Contact Override")) {
float w = ImGui::GetWindowWidth() * .6f;
ImGui_Input("Margin", &opt.o_margin, {.min = 0.1, .max = 1, .width = w});
ImGui_InputN("Sol Imp", opt.o_solimp, 5, {.width = w, .format = "%0.1f"});
ImGui_InputN("Sol Ref", opt.o_solref, 2, {.width = w, .format = "%0.1f"});
ImGui_InputN("Friction", opt.o_friction, 5, {.width = w, .format = "%.1f"});
ImGui_Input("Margin", &opt.o_margin, {.min = 0.1, .max = 1});
ImGui_InputN("Sol Imp", opt.o_solimp, 5, {.format = "%0.1f"});
ImGui_InputN("Sol Ref", opt.o_solref, 2, {.format = "%0.1f"});
ImGui_InputN("Friction", opt.o_friction, 5, {.format = "%.1f"});
ImGui::TreePop();
}
ImGui::PopItemWidth();
}
void VisualizationGui(mjModel* model, mjvOption* vis_options, mjvCamera* camera,
@@ -478,6 +647,9 @@ void VisualizationGui(mjModel* model, mjvOption* vis_options, mjvCamera* camera,
auto& vis = model->vis;
auto& stat = model->stat;
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
ImGui::SliderInt("Tree depth", &vis_options->bvh_depth, 0, 20);
ImGui::SliderInt("Flex layer", &vis_options->flex_layer, 0, 10);
@@ -574,6 +746,8 @@ void VisualizationGui(mjModel* model, mjvOption* vis_options, mjvCamera* camera,
ImGui::ColorEdit4("BV Active", vis.rgba.bvactive);
ImGui::TreePop();
}
ImGui::PopItemWidth();
}
void RenderingGui(const mjModel* model, mjvOption* vis_options,
@@ -659,12 +833,18 @@ void GroupsGui(const mjModel* model, mjvOption* vis_options, float min_width) {
void NoiseGui(const mjModel* model, const mjData* data, float& noise_scale,
float& noise_rate) {
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
ImGui::SliderFloat("Scale", &noise_scale, 0, 1);
ImGui::SliderFloat("Rate", &noise_rate, 0, 4);
ImGui::PopItemWidth();
}
void JointsGui(const mjModel* model, const mjData* data,
const mjvOption* vis_options) {
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
char name[100];
for (int i = 0; i < model->njnt; ++i) {
if (model->jnt_type[i] != mjJNT_HINGE &&
@@ -699,10 +879,15 @@ void JointsGui(const mjModel* model, const mjData* data,
const int data_adr = model->jnt_qposadr[i];
ImGui_Slider(name, &data->qpos[data_adr], min, max);
}
ImGui::PopItemWidth();
}
void ControlsGui(const mjModel* model, const mjData* data,
const mjvOption* vis_options) {
const float item_width = ImGui::GetWindowWidth() * .6f;
ImGui::PushItemWidth(item_width);
if (ImGui::Button("Clear All")) {
mju_zero(data->ctrl, model->nu);
}
@@ -733,8 +918,9 @@ void ControlsGui(const mjModel* model, const mjData* data,
}
ImGui_Slider(name, &data->ctrl[i], min, max);
}
}
ImGui::PopItemWidth();
}
void ConvergenceGui(const mjModel* model, mjData* data) {
if (ImPlot::BeginPlot("Convergence (log 10)", ImVec2(-1, 0),
+1
View File
@@ -34,6 +34,7 @@ namespace mujoco::platform {
enum class GuiTheme {
kLight,
kDark,
kClassic,
};
// Updates the ImGui internal style state to match the requested theme.
+30 -7
View File
@@ -22,11 +22,32 @@
#include <unordered_map>
#include <imgui.h>
#include <imgui_internal.h> // For ButtonEx and PressedOnClick
#include <imgui_internal.h>
#include <mujoco/mujoco.h>
namespace mujoco::platform {
// FontAwesome icon codes.
static constexpr const char ICON_FA_ARROWS[] = u8"\uf047";
static constexpr const char ICON_FA_CAMERA[] = u8"\uf03d";
static constexpr const char ICON_FA_CARET_LEFT[] = u8"\uf0d9";
static constexpr const char ICON_FA_CARET_RIGHT[] = u8"\uf0da";
static constexpr const char ICON_FA_CHECK_SQUARE_O[] = u8"\uf05d";
static constexpr const char ICON_FA_COMMENT[] = u8"\uf0e5";
static constexpr const char ICON_FA_COPY[] = u8"\uf0c5";
static constexpr const char ICON_FA_DIAMOND[] = u8"\uf219";
static constexpr const char ICON_FA_EJECT[] = u8"\uf052";
static constexpr const char ICON_FA_FAST_FORWARD[] = u8"\uf050";
static constexpr const char ICON_FA_MOON[] = u8"\uf186";
static constexpr const char ICON_FA_PAUSE[] = u8"\uf04c";
static constexpr const char ICON_FA_PLAY[] = u8"\uf04b";
static constexpr const char ICON_FA_REFRESH[] = u8"\uf021";
static constexpr const char ICON_FA_SQUARE_O[] = u8"\uf1db";
static constexpr const char ICON_FA_SUN[] = u8"\uf185";
static constexpr const char ICON_FA_TACHOMETER[] = u8"\uf0e4";
static constexpr const char ICON_FA_UNDO[] = u8"\uf0e2";
using KeyValues = std::unordered_map<std::string, std::string>;
// Appends key/value pairs to an Ini file.
@@ -107,13 +128,15 @@ bool ImGui_ButtonToggle(const char* label, T* boolean,
static_assert(std::is_integral_v<T>, "Toggle only supports integral types.");
ScopedStyle style;
if (!(*boolean)) {
ImColor button = ImGui::GetStyle().Colors[ImGuiCol_Button];
button.Value.w = 0.0f;
style.Color(ImGuiCol_Button, button);
}
const int color = *boolean ? ImGuiCol_TabSelected : ImGuiCol_WindowBg;
style.Color(ImGuiCol_Button, ImGui::GetStyle().Colors[color]);
style.Var(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f));
if (ImGui::Button(label, size)) {
const std::string txt =
std::string(" ") +
std::string(*boolean ? ICON_FA_SQUARE_O : ICON_FA_CHECK_SQUARE_O) + " " +
label;
if (ImGui::Button(txt.c_str(), size)) {
*boolean = !(*boolean);
return true;
}
+169 -97
View File
@@ -88,38 +88,22 @@ static void SelectParentPerturb(const mjModel* model, mjvPerturb& perturb) {
}
}
// FontAwesome icon codes.
static constexpr const char* ICON_FA_PLAY = "\xef\x81\x8b";
static constexpr const char* ICON_FA_PAUSE = "\xef\x81\x8c";
static constexpr const char* ICON_FA_COPY = "\xef\x83\x85";
static constexpr const char* ICON_FA_EJECT = "\xef\x81\x92";
static constexpr const char* ICON_FA_REFRESH = "\xef\x80\xa1";
static constexpr const char* ICON_FA_COMMENT = "\xef\x83\xa5";
static constexpr const char* ICON_FA_UNDO = "\xef\x83\xa2";
static constexpr const char* ICON_FA_ARROWS = "\xef\x81\x87";
static constexpr const char* ICON_FA_CAMERA = "\xef\x80\xbd";
static constexpr const char* ICON_FA_MOON = "\xef\x86\x86";
static constexpr const char* ICON_FA_SUN = "\xef\x86\x85";
static constexpr const char* ICON_FA_CARET_LEFT = "\xef\x83\x99";
static constexpr const char* ICON_FA_CARET_RIGHT = "\xef\x83\x9a";
static constexpr const char* ICON_FA_FAST_FORWARD = "\xef\x81\x90";
static constexpr const char* ICON_FA_TACHOMETER = "\xef\x83\xa4";
static constexpr const char* ICON_PLAY = ICON_FA_PLAY;
static constexpr const char* ICON_PAUSE = ICON_FA_PAUSE;
static constexpr const char* ICON_COPY_CAMERA = ICON_FA_COPY;
static constexpr const char* ICON_UNLOAD_MODEL = ICON_FA_EJECT;
static constexpr const char* ICON_RELOAD_MODEL = ICON_FA_REFRESH;
static constexpr const char* ICON_LABEL = ICON_FA_COMMENT;
static constexpr const char* ICON_RESET_MODEL = ICON_FA_UNDO;
static constexpr const char* ICON_FRAME = ICON_FA_ARROWS;
static constexpr const char* ICON_CAMERA = ICON_FA_CAMERA;
static constexpr const char* ICON_DARKMODE = ICON_FA_MOON;
static constexpr const char* ICON_LIGHTMODE = ICON_FA_SUN;
static constexpr const char* ICON_PREV_FRAME = ICON_FA_CARET_LEFT;
static constexpr const char* ICON_NEXT_FRAME = ICON_FA_CARET_RIGHT;
static constexpr const char* ICON_CURR_FRAME = ICON_FA_FAST_FORWARD;
static constexpr const char* ICON_SPEED = ICON_FA_TACHOMETER;
static constexpr const char* ICON_PLAY = platform::ICON_FA_PLAY;
static constexpr const char* ICON_PAUSE = platform::ICON_FA_PAUSE;
static constexpr const char* ICON_COPY_CAMERA = platform::ICON_FA_COPY;
static constexpr const char* ICON_UNLOAD_MODEL = platform::ICON_FA_EJECT;
static constexpr const char* ICON_RELOAD_MODEL = platform::ICON_FA_REFRESH;
static constexpr const char* ICON_LABEL = platform::ICON_FA_COMMENT;
static constexpr const char* ICON_RESET_MODEL = platform::ICON_FA_UNDO;
static constexpr const char* ICON_FRAME = platform::ICON_FA_ARROWS;
static constexpr const char* ICON_CAMERA = platform::ICON_FA_CAMERA;
static constexpr const char* ICON_DARKMODE = platform::ICON_FA_MOON;
static constexpr const char* ICON_LIGHTMODE = platform::ICON_FA_SUN;
static constexpr const char* ICON_CLASSICMODE = platform::ICON_FA_DIAMOND;
static constexpr const char* ICON_PREV_FRAME = platform::ICON_FA_CARET_LEFT;
static constexpr const char* ICON_NEXT_FRAME = platform::ICON_FA_CARET_RIGHT;
static constexpr const char* ICON_CURR_FRAME = platform::ICON_FA_FAST_FORWARD;
static constexpr const char* ICON_SPEED = platform::ICON_FA_TACHOMETER;
// UI labels for mjtLabel.
static constexpr const char* kLabelNames[] = {
@@ -145,7 +129,7 @@ App::App(int width, int height, std::string ini_path,
const platform::LoadAssetFn& load_asset_fn)
: ini_path_(std::move(ini_path)), load_asset_fn_(load_asset_fn) {
window_ = std::make_unique<platform::Window>("MuJoCo Studio", width, height,
kWindowConfig, load_asset_fn);
kWindowConfig, load_asset_fn);
ImPlot::CreateContext();
auto make_context_fn = [&](const mjModel* m, mjrContext* con) {
@@ -480,7 +464,7 @@ void App::HandleMouseEvents() {
platform::InitPerturb(model_, data_, &camera_, &perturb_, active);
}
platform::MovePerturb(model_, data_, &camera_, &perturb_, action,
mouse_dx, mouse_dy);
mouse_dx, mouse_dy);
}
}
// Handle camera movement actions.
@@ -515,7 +499,7 @@ void App::HandleMouseEvents() {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
platform::PickResult picked =
platform::Pick(model_, data_, &camera_, mouse_x, mouse_y,
window_->GetAspectRatio(), &vis_options_);
window_->GetAspectRatio(), &vis_options_);
if (picked.body >= 0) {
perturb_.select = picked.body;
perturb_.flexselect = picked.flex;
@@ -537,7 +521,7 @@ void App::HandleMouseEvents() {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
platform::PickResult picked =
platform::Pick(model_, data_, &camera_, mouse_x, mouse_y,
window_->GetAspectRatio(), &vis_options_);
window_->GetAspectRatio(), &vis_options_);
mju_copy3(camera_.lookat, picked.point);
if (picked.body > 0 && io.KeyCtrl) {
// Switch camera to tracking mode and track the selected body.
@@ -697,11 +681,14 @@ void App::HandleKeyboardEvents() {
ToggleFlag(vis_options_.geomgroup[5]);
} else if (model_) {
if (ImGui_IsChordJustPressed(ImGuiKey_Escape)) {
ui_.camera_idx = platform::SetCamera(model_, &camera_, platform::kTumbleCameraIdx);
ui_.camera_idx =
platform::SetCamera(model_, &camera_, platform::kTumbleCameraIdx);
} else if (ImGui_IsChordJustPressed(ImGuiKey_LeftBracket)) {
ui_.camera_idx = platform::SetCamera(model_, &camera_, ui_.camera_idx - 1);
ui_.camera_idx =
platform::SetCamera(model_, &camera_, ui_.camera_idx - 1);
} else if (ImGui_IsChordJustPressed(ImGuiKey_RightBracket)) {
ui_.camera_idx = platform::SetCamera(model_, &camera_, ui_.camera_idx + 1);
ui_.camera_idx =
platform::SetCamera(model_, &camera_, ui_.camera_idx + 1);
}
// WASD camera controls for free camera.
@@ -777,7 +764,8 @@ void App::SetSpeedIndex(int idx) {
step_control_.SetSpeed(speed);
}
void App::MoveCamera(platform::CameraMotion motion, mjtNum reldx, mjtNum reldy) {
void App::MoveCamera(platform::CameraMotion motion, mjtNum reldx,
mjtNum reldy) {
platform::MoveCamera(model_, data_, &camera_, motion, reldx, reldy);
}
@@ -937,25 +925,39 @@ void App::SetupTheme(platform::GuiTheme theme) {
void App::ModelOptionsGui() {
const float min_width = GetExpectedLabelWidth();
const ImGuiTreeNodeFlags flags =
const ImGuiChildFlags child_flags =
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize;
const ImGuiTreeNodeFlags node_flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed;
if (ImGui::TreeNodeEx("Physics Settings", flags)) {
ImGui::BeginChild("PhysicsGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Physics Settings", node_flags)) {
platform::PhysicsGui(model_, min_width);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Rendering Settings", flags)) {
platform::RenderingGui(model_, &vis_options_, renderer_->GetRenderFlags(), min_width);
ImGui::EndChild();
ImGui::BeginChild("RenderingGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Rendering Settings", node_flags)) {
platform::RenderingGui(model_, &vis_options_, renderer_->GetRenderFlags(),
min_width);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Visibility Groups", flags)) {
platform::GroupsGui(model_, &vis_options_, min_width);
ImGui::EndChild();
ImGui::BeginChild("GroupsGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Visibility Groups", node_flags)) {
platform::GroupsGui(model_, &vis_options_, min_width);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Visualization", flags)) {
ImGui::EndChild();
ImGui::BeginChild("VisualizationGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Visualization", node_flags)) {
platform::VisualizationGui(model_, &vis_options_, &camera_, min_width);
ImGui::TreePop();
}
ImGui::EndChild();
}
void App::DataInspectorGui() {
@@ -965,10 +967,13 @@ void App::DataInspectorGui() {
}
const float min_width = GetExpectedLabelWidth();
const ImGuiTreeNodeFlags flags =
const ImGuiChildFlags child_flags =
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize;
const ImGuiTreeNodeFlags node_flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed;
if (ImGui::TreeNodeEx("Noise", flags)) {
ImGui::BeginChild("NoiseGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Noise", node_flags)) {
float noise_scale = 0;
float noise_rate = 0;
step_control_.GetNoiseParameters(noise_scale, noise_rate);
@@ -976,27 +981,43 @@ void App::DataInspectorGui() {
step_control_.SetNoiseParameters(noise_scale, noise_rate);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Joints", flags)) {
ImGui::EndChild();
ImGui::BeginChild("JointsGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Joints", node_flags)) {
platform::JointsGui(model_, data_, &vis_options_);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Controls", flags)) {
ImGui::EndChild();
ImGui::BeginChild("ControlsGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Controls", node_flags)) {
platform::ControlsGui(model_, data_, &vis_options_);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Sensor", flags)) {
ImGui::EndChild();
ImGui::BeginChild("SensorGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Sensor", node_flags)) {
platform::SensorGui(model_, data_);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Watch", flags)) {
ImGui::EndChild();
ImGui::BeginChild("WatchGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("Watch", node_flags)) {
platform::WatchGui(model_, data_, ui_.watch_field, sizeof(ui_.watch_field),
ui_.watch_index);
ui_.watch_index);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("State", flags)) {
ImGui::EndChild();
ImGui::BeginChild("StateGui", {0, 0}, child_flags);
if (ImGui::TreeNodeEx("State", node_flags)) {
platform::StateGui(model_, data_, tmp_.state, tmp_.state_sig, min_width);
ImGui::TreePop();
}
ImGui::EndChild();
}
void DisplayElementTree(mjsElement* element) {
@@ -1014,7 +1035,8 @@ void App::SpecExplorerGui() {
return;
}
const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanAvailWidth;
const ImGuiTreeNodeFlags flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed;
auto display_group = [this](mjtObj type, const std::string& prefix) {
mjsElement* element = mjs_firstElement(spec_, type);
@@ -1036,7 +1058,6 @@ void App::SpecExplorerGui() {
}
};
if (ImGui::TreeNodeEx("Bodies", flags)) {
// We don't use `display_group` here because we do additional selection
// logic tied to the `perturb_` field.
@@ -1055,7 +1076,8 @@ void App::SpecExplorerGui() {
tmp_.element = element;
tmp_.element_id = id;
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
if (ImGui::IsItemHovered() &&
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
perturb_.select = id;
}
@@ -1096,8 +1118,7 @@ void App::PropertiesGui() {
case mjOBJ_SITE:
ImGui::Text("Site");
ImGui::Separator();
platform::SitePropertiesGui(model_, data_, tmp_.element,
tmp_.element_id);
platform::SitePropertiesGui(model_, data_, tmp_.element, tmp_.element_id);
break;
default:
// ignore other types
@@ -1211,26 +1232,34 @@ void App::HelpGui() {
void App::ToolBarGui() {
if (ImGui::BeginTable("##ToolBarTable", 2)) {
platform::ScopedStyle style;
const ImColor red(220, 40, 40, 255);
const ImColor green(40, 180, 40, 255);
const ImColor yellow(250, 230, 10, 255);
const int combo_flags = ImGuiComboFlags_NoArrowButton;
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 560);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 520);
ImGui::TableNextColumn();
ImGui::Text("%s", " ");
// Reset/Reload/Unload.
style.Color(ImGuiCol_ButtonHovered, ImColor(220, 40, 40, 255));
// Unload button.
ImGui::SameLine();
style.Color(ImGuiCol_ButtonHovered, red);
if (ImGui::Button(ICON_UNLOAD_MODEL, ImVec2(48, 32))) {
RequestModelLoad("");
}
ImGui::SetItemTooltip("%s", "Unload");
style.Reset();
// Reload button.
ImGui::SameLine();
if (ImGui::Button(ICON_RELOAD_MODEL, ImVec2(48, 32))) {
RequestModelLoad(model_name_);
}
ImGui::SetItemTooltip("%s", "Reload");
// Reset button.
ImGui::SameLine();
if (ImGui::Button(ICON_RESET_MODEL, ImVec2(48, 32))) {
ResetPhysics();
@@ -1240,8 +1269,7 @@ void App::ToolBarGui() {
// Play/pause button.
ImGui::SameLine();
const bool paused = step_control_.IsPaused();
style.Color(ImGuiCol_Button,
paused ? ImColor(250, 230, 10, 255) : ImColor(40, 180, 40, 255));
style.Color(ImGuiCol_Button, paused ? yellow : green);
if (ImGui::Button(paused ? ICON_PLAY : ICON_PAUSE, ImVec2(120, 32))) {
step_control_.TogglePause();
}
@@ -1257,25 +1285,36 @@ void App::ToolBarGui() {
ImGui::SetItemTooltip("%s", "Playback Speed");
ImGui::SameLine();
ImGui::SetNextItemWidth(80);
int speed_index = tmp_.speed_index;
if (ImGui::Combo("##Speed", &speed_index, kPercentRealTime.data(),
kPercentRealTime.size())) {
SetSpeedIndex(speed_index);
ImGui::SetNextItemWidth(50);
if (ImGui::BeginCombo("##Speed", kPercentRealTime[tmp_.speed_index],
combo_flags)) {
for (int n = 0; n < kPercentRealTime.size(); n++) {
if (ImGui::Selectable(kPercentRealTime[n], (tmp_.speed_index == n))) {
SetSpeedIndex(n);
}
}
ImGui::EndCombo();
}
ImGui::SetItemTooltip("%s", "Playback Speed");
// Camera selection.
std::vector<const char*> cameras = GetCameraNames();
ImGui::TableNextColumn();
ImGui::Text("%s", ICON_CAMERA);
ImGui::SetItemTooltip("%s", "Camera");
ImGui::SameLine();
std::vector<const char*> cameras = GetCameraNames();
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
int camera_idx = ui_.camera_idx - platform::kTumbleCameraIdx;
if (ImGui::Combo("##Camera", &camera_idx, cameras.data(), cameras.size())) {
ui_.camera_idx = ::mujoco::platform::SetCamera(
model_, &camera_, camera_idx + platform::kTumbleCameraIdx);
if (ImGui::BeginCombo("##Camera", cameras[camera_idx], combo_flags)) {
for (int n = 0; n < cameras.size(); n++) {
if (ImGui::Selectable(cameras[n], (camera_idx == n))) {
ui_.camera_idx = ::mujoco::platform::SetCamera(
model_, &camera_, camera_idx + platform::kTumbleCameraIdx);
}
}
ImGui::EndCombo();
}
ImGui::SetItemTooltip("%s", "Camera");
ImGui::SameLine();
if (ImGui::Button(ICON_COPY_CAMERA)) {
std::string camera_string = platform::CameraToString(data_, &camera_);
@@ -1293,8 +1332,16 @@ void App::ToolBarGui() {
ImGui::SameLine();
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
ImGui::Combo("##Label", &vis_options_.label, kLabelNames,
IM_ARRAYSIZE(kLabelNames));
if (ImGui::BeginCombo("##Label", kLabelNames[vis_options_.label],
combo_flags)) {
for (int n = 0; n < IM_ARRAYSIZE(kLabelNames); n++) {
if (ImGui::Selectable(kLabelNames[n], (vis_options_.label == n))) {
vis_options_.label = n;
}
}
ImGui::EndCombo();
}
ImGui::SetItemTooltip("%s", "Label");
ImGui::SameLine();
ImGui::Text("%s", " |");
@@ -1306,24 +1353,42 @@ void App::ToolBarGui() {
ImGui::SameLine();
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
ImGui::Combo("##Frame", &vis_options_.frame, kFrameNames,
IM_ARRAYSIZE(kFrameNames));
if (ImGui::BeginCombo("##Frame", kFrameNames[vis_options_.frame],
combo_flags)) {
for (int n = 0; n < IM_ARRAYSIZE(kFrameNames); n++) {
if (ImGui::Selectable(kFrameNames[n], (vis_options_.frame == n))) {
vis_options_.frame = n;
}
}
ImGui::EndCombo();
}
ImGui::SetItemTooltip("%s", "Frame");
ImGui::SameLine();
ImGui::Text("%s", " |");
// Style selection.
ImGui::SameLine();
if (ImGui::Button(ui_.theme == platform::GuiTheme::kDark
? ICON_DARKMODE
: ICON_LIGHTMODE)) {
if (ui_.theme == platform::GuiTheme::kDark) {
SetupTheme(platform::GuiTheme::kLight);
} else {
SetupTheme(platform::GuiTheme::kDark);
}
switch (ui_.theme) {
case platform::GuiTheme::kLight:
if (ImGui::Button(ICON_LIGHTMODE)) {
SetupTheme(platform::GuiTheme::kDark);
}
ImGui::SetItemTooltip("%s", "Switch to Dark Mode");
break;
case platform::GuiTheme::kDark:
if (ImGui::Button(ICON_DARKMODE)) {
SetupTheme(platform::GuiTheme::kClassic);
}
ImGui::SetItemTooltip("%s", "Switch to Classic Mode");
break;
case platform::GuiTheme::kClassic:
if (ImGui::Button(ICON_CLASSICMODE)) {
SetupTheme(platform::GuiTheme::kLight);
}
ImGui::SetItemTooltip("%s", "Switch to Light Mode");
break;
}
ImGui::SetItemTooltip("%s", "Switch Style");
ImGui::EndTable();
}
@@ -1345,10 +1410,12 @@ void App::StatusBarGui() {
} else {
const float desired_realtime = step_control_.GetSpeed();
const float measured_realtime = step_control_.GetSpeedMeasured();
const float realtime_offset = mju_abs(measured_realtime - desired_realtime);
const float realtime_offset =
mju_abs(measured_realtime - desired_realtime);
const bool misaligned = realtime_offset > 0.1 * desired_realtime;
if (misaligned) {
ImGui::Text("Running: %g%% (%-4.1f%%)", desired_realtime, measured_realtime);
ImGui::Text("Running: %g%% (%-4.1f%%)", desired_realtime,
measured_realtime);
} else {
ImGui::Text("Running: %g%%", desired_realtime);
}
@@ -1476,8 +1543,8 @@ void App::MainMenuGui() {
}
ImGui::Separator();
if (ImGui::MenuItem(
tmp_.options_panel ? "Hide Options" : "Show Left UI", "Tab")) {
if (ImGui::MenuItem(tmp_.options_panel ? "Hide Options" : "Show Left UI",
"Tab")) {
tmp_.options_panel = !tmp_.options_panel;
}
if (ImGui::MenuItem(
@@ -1530,19 +1597,22 @@ void App::FileDialogGui() {
if (tmp_.load_popup) {
ImGui::OpenPopup("LoadModel");
tmp_.load_popup = false;
strncpy(tmp_.filename, tmp_.last_load_file.c_str(), tmp_.last_load_file.size());
strncpy(tmp_.filename, tmp_.last_load_file.c_str(),
tmp_.last_load_file.size());
tmp_.filename[tmp_.last_load_file.size()] = 0;
}
if (tmp_.save_xml_popup) {
ImGui::OpenPopup("SaveXML");
tmp_.save_xml_popup = false;
strncpy(tmp_.filename, tmp_.last_save_xml_file.c_str(), tmp_.last_save_xml_file.size());
strncpy(tmp_.filename, tmp_.last_save_xml_file.c_str(),
tmp_.last_save_xml_file.size());
tmp_.filename[tmp_.last_save_xml_file.size()] = 0;
}
if (tmp_.save_mjb_popup) {
ImGui::OpenPopup("SaveMJB");
tmp_.save_mjb_popup = false;
strncpy(tmp_.filename, tmp_.last_save_mjb_file.c_str(), tmp_.last_save_mjb_file.size());
strncpy(tmp_.filename, tmp_.last_save_mjb_file.c_str(),
tmp_.last_save_mjb_file.size());
tmp_.filename[tmp_.last_save_mjb_file.size()] = 0;
}
if (tmp_.save_screenshot_popup) {
@@ -1555,13 +1625,15 @@ void App::FileDialogGui() {
if (tmp_.print_model_popup) {
ImGui::OpenPopup("PrintModel");
tmp_.print_model_popup = false;
strncpy(tmp_.filename, tmp_.last_print_model_file.c_str(), tmp_.last_print_model_file.size());
strncpy(tmp_.filename, tmp_.last_print_model_file.c_str(),
tmp_.last_print_model_file.size());
tmp_.filename[tmp_.last_print_model_file.size()] = 0;
}
if (tmp_.print_data_popup) {
ImGui::OpenPopup("PrintData");
tmp_.print_data_popup = false;
strncpy(tmp_.filename, tmp_.last_print_data_file.c_str(), tmp_.last_print_data_file.size());
strncpy(tmp_.filename, tmp_.last_print_data_file.c_str(),
tmp_.last_print_data_file.size());
tmp_.filename[tmp_.last_print_data_file.size()] = 0;
}
@@ -1571,7 +1643,7 @@ void App::FileDialogGui() {
ImGui::IsPopupOpen("PrintModel") || ImGui::IsPopupOpen("PrintData");
if (ImGui::BeginPopupModal("LoadModel", NULL,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGuiWindowFlags_AlwaysAutoResize)) {
if (platform::ImGui_FileDialog(tmp_.filename, sizeof(tmp_.filename))) {
RequestModelLoad(tmp_.filename);
tmp_.last_load_file = tmp_.filename;