Make Studio's monitoring charts responsive and full-window
* **Dynamic Full-Window Scaling**: Charts in the Performance and Solver panels now expand to fill the entire pane, automatically arranging side-by-side or stacked based on window orientation to eliminate scrollbars. * **Responsive Decorators**: Added reusable C++ ImPlot helper functions that auto-hide titles, legends, and tick labels when the plot surface is small. * **Cleaner Visualization**: Refined chart titles (e.g., "CPU msec vs frame", "Convergence vs iter") and removed redundant axis labels for improved scannability. These changes were based on the plot behavior of //third_party/mujoco/src/experimental/py/sample/implot.py This is an incremental improvement, we can make the plots even nicer in future. PiperOrigin-RevId: 906351914 Change-Id: I8cb19c19bf807f0107c717066250a71bcf7e325f
This commit is contained in:
committed by
Copybara-Service
parent
2e3205229c
commit
9cd89eec2f
@@ -17,12 +17,11 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <implot.h>
|
#include <implot.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
|
#include "experimental/platform/ux/imgui_widgets.h"
|
||||||
|
|
||||||
namespace mujoco::platform {
|
namespace mujoco::platform {
|
||||||
|
|
||||||
SimProfiler::SimProfiler() {
|
SimProfiler::SimProfiler() { Clear(); }
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimProfiler::Clear() {
|
void SimProfiler::Clear() {
|
||||||
constexpr int kProfilerMaxFrames = 200;
|
constexpr int kProfilerMaxFrames = 200;
|
||||||
@@ -88,21 +87,21 @@ void SimProfiler::Update(const mjModel* model, const mjData* data) {
|
|||||||
// Solver diagnostics.
|
// Solver diagnostics.
|
||||||
mjtNum sqrt_nnz = 0;
|
mjtNum sqrt_nnz = 0;
|
||||||
int solver_niter = 0;
|
int solver_niter = 0;
|
||||||
const int nisland = data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
const int nisland =
|
||||||
for (int island=0; island < nisland; island++) {
|
data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
||||||
|
for (int island = 0; island < nisland; island++) {
|
||||||
sqrt_nnz += data->solver_nnz[island];
|
sqrt_nnz += data->solver_nnz[island];
|
||||||
solver_niter += data->solver_niter[island];
|
solver_niter += data->solver_niter[island];
|
||||||
}
|
}
|
||||||
sqrt_nnz = mju_sqrt(sqrt_nnz);
|
sqrt_nnz = mju_sqrt(sqrt_nnz);
|
||||||
|
|
||||||
dim_dof_.erase(dim_dof_.begin());
|
dim_dof_.erase(dim_dof_.begin());
|
||||||
int nv = (model->opt.enableflags & mjENBL_SLEEP) ? data->nv_awake
|
int nv = (model->opt.enableflags & mjENBL_SLEEP) ? data->nv_awake : model->nv;
|
||||||
: model->nv;
|
|
||||||
dim_dof_.push_back(nv);
|
dim_dof_.push_back(nv);
|
||||||
|
|
||||||
dim_body_.erase(dim_body_.begin());
|
dim_body_.erase(dim_body_.begin());
|
||||||
int nbody = (model->opt.enableflags & mjENBL_SLEEP) ? data->nbody_awake
|
int nbody = (model->opt.enableflags & mjENBL_SLEEP) ? data->nbody_awake
|
||||||
: model->nbody;
|
: model->nbody;
|
||||||
dim_body_.push_back(nbody);
|
dim_body_.push_back(nbody);
|
||||||
|
|
||||||
dim_constraint_.erase(dim_constraint_.begin());
|
dim_constraint_.erase(dim_constraint_.begin());
|
||||||
@@ -115,16 +114,17 @@ void SimProfiler::Update(const mjModel* model, const mjData* data) {
|
|||||||
dim_contact_.push_back(data->ncon);
|
dim_contact_.push_back(data->ncon);
|
||||||
|
|
||||||
dim_iteration_.erase(dim_iteration_.begin());
|
dim_iteration_.erase(dim_iteration_.begin());
|
||||||
dim_iteration_.push_back(static_cast<float>(solver_niter) / nisland);
|
dim_iteration_.push_back(static_cast<float>(solver_niter) /
|
||||||
|
mjMAX(1, nisland));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SimProfiler::CpuTimeGraph(ImVec2 plot_size) {
|
||||||
void SimProfiler::CpuTimeGraph() {
|
ImPlotFlags flags =
|
||||||
if (ImPlot::BeginPlot("CPU Time", ImVec2(-1, 0), ImPlotFlags_NoMouseText)) {
|
ImPlot_SetupPlotFlags(plot_size) | ImPlotFlags_NoMouseText;
|
||||||
|
if (ImPlot::BeginPlot("CPU msec vs frame", plot_size, flags)) {
|
||||||
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
||||||
ImPlot::SetupAxis(ImAxis_X1, "frame", ImPlotAxisFlags_AutoFit);
|
ImPlot_SetupTimeAxis(plot_size, "");
|
||||||
ImPlot::SetupAxis(ImAxis_Y1, "msec", ImPlotAxisFlags_AutoFit);
|
ImPlot_SetupValueAxis(plot_size, "", "%.2f");
|
||||||
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.2f");
|
|
||||||
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
||||||
ImPlot::SetupFinish();
|
ImPlot::SetupFinish();
|
||||||
|
|
||||||
@@ -143,12 +143,13 @@ void SimProfiler::CpuTimeGraph() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimProfiler::DimensionsGraph() {
|
void SimProfiler::DimensionsGraph(ImVec2 plot_size) {
|
||||||
if (ImPlot::BeginPlot("Dimensions", ImVec2(-1, 0), ImPlotFlags_NoMouseText)) {
|
ImPlotFlags flags =
|
||||||
|
ImPlot_SetupPlotFlags(plot_size) | ImPlotFlags_NoMouseText;
|
||||||
|
if (ImPlot::BeginPlot("Dimensions vs frame", plot_size, flags)) {
|
||||||
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
||||||
ImPlot::SetupAxis(ImAxis_X1, "frame", ImPlotAxisFlags_AutoFit);
|
ImPlot_SetupTimeAxis(plot_size, "");
|
||||||
ImPlot::SetupAxis(ImAxis_Y1, "count", ImPlotAxisFlags_AutoFit);
|
ImPlot_SetupValueAxis(plot_size, "", "%.0f");
|
||||||
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.0f");
|
|
||||||
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
||||||
ImPlot::SetupFinish();
|
ImPlot::SetupFinish();
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
|
|
||||||
namespace mujoco::platform {
|
namespace mujoco::platform {
|
||||||
@@ -33,8 +34,8 @@ class SimProfiler {
|
|||||||
void Update(const mjModel* model, const mjData* data);
|
void Update(const mjModel* model, const mjData* data);
|
||||||
|
|
||||||
// Displays the profiling data using ImPlot.
|
// Displays the profiling data using ImPlot.
|
||||||
void CpuTimeGraph();
|
void CpuTimeGraph(ImVec2 plot_size = ImVec2(-1, 0));
|
||||||
void DimensionsGraph();
|
void DimensionsGraph(ImVec2 plot_size = ImVec2(-1, 0));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<float> cpu_total_;
|
std::vector<float> cpu_total_;
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ bool CameraSelectionGui(const mjModel* model, mjData* data, mjvCamera& camera,
|
|||||||
|
|
||||||
auto select = [&](int type, int idx) {
|
auto select = [&](int type, int idx) {
|
||||||
if (ImGui::Selectable(GetCameraName(model, camera, type).c_str(),
|
if (ImGui::Selectable(GetCameraName(model, camera, type).c_str(),
|
||||||
(type == idx))) {
|
(type == idx))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1148,13 +1148,13 @@ static int GetPlotXLimit(const mjData* data) {
|
|||||||
return mjMAX(10, ((max_niter + 9) / 10) * 10);
|
return mjMAX(10, ((max_niter + 9) / 10) * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvergenceGui(const mjModel* model, mjData* data) {
|
void ConvergenceGui(const mjModel* model, mjData* data, ImVec2 plot_size) {
|
||||||
int xlim = GetPlotXLimit(data);
|
int xlim = GetPlotXLimit(data);
|
||||||
|
ImPlotFlags flags =
|
||||||
if (ImPlot::BeginPlot("Convergence (log 10)", ImVec2(-1, 0),
|
ImPlot_SetupPlotFlags(plot_size) | ImPlotFlags_NoMouseText;
|
||||||
ImPlotFlags_NoMouseText)) {
|
if (ImPlot::BeginPlot("Convergence (log 10) vs iter", plot_size, flags)) {
|
||||||
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
||||||
ImPlot::SetupAxis(ImAxis_X1, "iteration", ImPlotAxisFlags_AutoFit);
|
ImPlot::SetupAxis(ImAxis_X1, "", ImPlotAxisFlags_AutoFit);
|
||||||
ImPlot::SetupAxisLimits(ImAxis_X1, 0, xlim, ImPlotCond_Always);
|
ImPlot::SetupAxisLimits(ImAxis_X1, 0, xlim, ImPlotCond_Always);
|
||||||
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.1f");
|
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.1f");
|
||||||
ImPlot::SetupAxisLimits(ImAxis_Y1, -20, 5, ImPlotCond_Always);
|
ImPlot::SetupAxisLimits(ImAxis_Y1, -20, 5, ImPlotCond_Always);
|
||||||
@@ -1211,12 +1211,13 @@ void ConvergenceGui(const mjModel* model, mjData* data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CountsGui(const mjModel* model, mjData* data) {
|
void CountsGui(const mjModel* model, mjData* data, ImVec2 plot_size) {
|
||||||
int xlim = GetPlotXLimit(data);
|
int xlim = GetPlotXLimit(data);
|
||||||
|
ImPlotFlags flags =
|
||||||
if (ImPlot::BeginPlot("Counts", ImVec2(-1, 0), ImPlotFlags_NoMouseText)) {
|
ImPlot_SetupPlotFlags(plot_size) | ImPlotFlags_NoMouseText;
|
||||||
|
if (ImPlot::BeginPlot("Counts vs iter", plot_size, flags)) {
|
||||||
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 2.0f);
|
||||||
ImPlot::SetupAxis(ImAxis_X1, "iteration", ImPlotAxisFlags_AutoFit);
|
ImPlot::SetupAxis(ImAxis_X1, "", ImPlotAxisFlags_AutoFit);
|
||||||
ImPlot::SetupAxisLimits(ImAxis_X1, 0, xlim, ImPlotCond_Always);
|
ImPlot::SetupAxisLimits(ImAxis_X1, 0, xlim, ImPlotCond_Always);
|
||||||
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.0f");
|
ImPlot::SetupAxisFormat(ImAxis_Y1, "%.0f");
|
||||||
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 80, ImPlotCond_Always);
|
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 80, ImPlotCond_Always);
|
||||||
|
|||||||
@@ -138,10 +138,12 @@ void NoiseGui(const mjModel* model, const mjData* data, float& noise_scale,
|
|||||||
float& noise_rate);
|
float& noise_rate);
|
||||||
|
|
||||||
// UX for the solver convergence chart.
|
// UX for the solver convergence chart.
|
||||||
void ConvergenceGui(const mjModel* model, mjData* data);
|
void ConvergenceGui(const mjModel* model, mjData* data,
|
||||||
|
ImVec2 plot_size = ImVec2(-1, 0));
|
||||||
|
|
||||||
// UX for the solver counts chart.
|
// UX for the solver counts chart.
|
||||||
void CountsGui(const mjModel* model, mjData* data);
|
void CountsGui(const mjModel* model, mjData* data,
|
||||||
|
ImVec2 plot_size = ImVec2(-1, 0));
|
||||||
|
|
||||||
// UX for displaying basic simulation information. Note that the pause state and
|
// UX for displaying basic simulation information. Note that the pause state and
|
||||||
// FPS needs to be tracked by the caller and passed here to be displayed.
|
// FPS needs to be tracked by the caller and passed here to be displayed.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "experimental/platform/ux/imgui_widgets.h"
|
#include "experimental/platform/ux/imgui_widgets.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <implot.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
|
|
||||||
namespace mujoco::platform {
|
namespace mujoco::platform {
|
||||||
@@ -378,4 +380,72 @@ void MaybeSaveToClipboard(const std::string& contents) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImPlotFlags ImPlot_SetupPlotFlags(ImVec2 plot_size) {
|
||||||
|
ImPlotFlags flags = ImPlotFlags_None;
|
||||||
|
if (plot_size.x > 0 && plot_size.y > 0) {
|
||||||
|
const float min_dim = std::min(plot_size.x, plot_size.y);
|
||||||
|
if (min_dim < 300) {
|
||||||
|
flags |= ImPlotFlags_NoTitle;
|
||||||
|
}
|
||||||
|
if (min_dim < 200) {
|
||||||
|
flags |= ImPlotFlags_NoLegend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImPlot_SetupTimeAxis(ImVec2 plot_size, const char* label,
|
||||||
|
ImPlotAxisFlags extra_flags) {
|
||||||
|
ImPlotAxisFlags flags = extra_flags;
|
||||||
|
if (plot_size.x > 0 && plot_size.x < 300) {
|
||||||
|
flags |= ImPlotAxisFlags_NoTickLabels;
|
||||||
|
}
|
||||||
|
ImPlot::SetupAxis(ImAxis_X1, label, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImPlot_SetupValueAxis(ImVec2 plot_size, const char* label,
|
||||||
|
const char* format, ImPlotAxisFlags extra_flags) {
|
||||||
|
ImPlotAxisFlags flags = extra_flags;
|
||||||
|
if (plot_size.y > 0 && plot_size.y < 150) {
|
||||||
|
flags |= ImPlotAxisFlags_NoTickLabels;
|
||||||
|
}
|
||||||
|
ImPlot::SetupAxis(ImAxis_Y1, label, flags);
|
||||||
|
if (format) {
|
||||||
|
ImPlot::SetupAxisFormat(ImAxis_Y1, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImPlot_SetupFixedAxis(ImVec2 plot_size, double y_min, double y_max,
|
||||||
|
const char* label, const char* format,
|
||||||
|
const double* tick_values,
|
||||||
|
const char* const* tick_labels, int n_ticks) {
|
||||||
|
ImPlotAxisFlags flags = ImPlotAxisFlags_None;
|
||||||
|
if (plot_size.y > 0 && plot_size.y < 150) {
|
||||||
|
flags |= ImPlotAxisFlags_NoTickLabels;
|
||||||
|
}
|
||||||
|
ImPlot::SetupAxis(ImAxis_Y1, label, flags);
|
||||||
|
ImPlot::SetupAxisLimits(ImAxis_Y1, y_min, y_max, ImPlotCond_Always);
|
||||||
|
if (format) {
|
||||||
|
ImPlot::SetupAxisFormat(ImAxis_Y1, format);
|
||||||
|
}
|
||||||
|
if (tick_values && n_ticks > 0) {
|
||||||
|
ImPlot::SetupAxisTicks(ImAxis_Y1, tick_values, n_ticks, tick_labels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImPlotPairLayout ImPlot_ComputePairLayout() {
|
||||||
|
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||||
|
bool is_wide = avail.x > avail.y;
|
||||||
|
|
||||||
|
const float item_spacing = ImGui::GetStyle().ItemSpacing.y;
|
||||||
|
ImVec2 plot_size(is_wide ? (avail.x - item_spacing) * 0.5f : avail.x,
|
||||||
|
is_wide ? avail.y : (avail.y - item_spacing) * 0.5f);
|
||||||
|
|
||||||
|
return {
|
||||||
|
plot_size,
|
||||||
|
is_wide ? ImPlotLayoutDirection::kHorizontal
|
||||||
|
: ImPlotLayoutDirection::kVertical,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mujoco::platform
|
} // namespace mujoco::platform
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <misc/cpp/imgui_stdlib.h>
|
#include <misc/cpp/imgui_stdlib.h>
|
||||||
|
#include <implot.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "experimental/platform/ux/enum_utils.h"
|
#include "experimental/platform/ux/enum_utils.h"
|
||||||
|
|
||||||
@@ -566,6 +567,46 @@ inline void EndBoxSection() { ImGui::EndTable(); }
|
|||||||
// Saves the given contents to the clipboard if the clipboard is available.
|
// Saves the given contents to the clipboard if the clipboard is available.
|
||||||
void MaybeSaveToClipboard(const std::string& contents);
|
void MaybeSaveToClipboard(const std::string& contents);
|
||||||
|
|
||||||
|
// Returns plot flags with title/legend conditionally hidden when the plot
|
||||||
|
// area is too small. `plot_size` is the final rendered size of the plot.
|
||||||
|
ImPlotFlags ImPlot_SetupPlotFlags(ImVec2 plot_size);
|
||||||
|
|
||||||
|
// Sets up the X axis as a "time/frame" axis.
|
||||||
|
// Hides tick labels when the plot is narrow.
|
||||||
|
// Uses `label` as the axis label (empty string to hide) and auto-fit limits.
|
||||||
|
void ImPlot_SetupTimeAxis(
|
||||||
|
ImVec2 plot_size, const char* label = "",
|
||||||
|
ImPlotAxisFlags extra_flags = ImPlotAxisFlags_AutoFit);
|
||||||
|
|
||||||
|
// Sets up a Y axis with auto-fit limits.
|
||||||
|
// Hides tick labels when the plot is short.
|
||||||
|
void ImPlot_SetupValueAxis(
|
||||||
|
ImVec2 plot_size, const char* label = "", const char* format = nullptr,
|
||||||
|
ImPlotAxisFlags extra_flags = ImPlotAxisFlags_AutoFit);
|
||||||
|
|
||||||
|
// Sets up a Y axis with fixed limits and optional explicit ticks.
|
||||||
|
// Hides tick labels when the plot is short.
|
||||||
|
void ImPlot_SetupFixedAxis(ImVec2 plot_size, double y_min, double y_max,
|
||||||
|
const char* label = "", const char* format = nullptr,
|
||||||
|
const double* tick_values = nullptr,
|
||||||
|
const char* const* tick_labels = nullptr,
|
||||||
|
int n_ticks = 0);
|
||||||
|
|
||||||
|
enum class ImPlotLayoutDirection {
|
||||||
|
kHorizontal,
|
||||||
|
kVertical,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImPlotPairLayout {
|
||||||
|
ImVec2 plot_size; // Size for each individual plot.
|
||||||
|
ImPlotLayoutDirection direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Computes a responsive layout for two plots that share the available
|
||||||
|
// content region. When the region is wider than tall, the plots are placed
|
||||||
|
// side-by-side; otherwise they are stacked vertically.
|
||||||
|
ImPlotPairLayout ImPlot_ComputePairLayout();
|
||||||
|
|
||||||
} // namespace mujoco::platform
|
} // namespace mujoco::platform
|
||||||
|
|
||||||
#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_UX_IMGUI_WIDGETS_H_
|
#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_UX_IMGUI_WIDGETS_H_
|
||||||
|
|||||||
@@ -884,8 +884,12 @@ void App::BuildGui() {
|
|||||||
ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver);
|
||||||
if (ImGui::Begin("Performance", &tmp_.chart_performance)) {
|
if (ImGui::Begin("Performance", &tmp_.chart_performance)) {
|
||||||
profiler_.CpuTimeGraph();
|
auto layout = platform::ImPlot_ComputePairLayout();
|
||||||
profiler_.DimensionsGraph();
|
profiler_.CpuTimeGraph(layout.plot_size);
|
||||||
|
if (layout.direction == platform::ImPlotLayoutDirection::kHorizontal) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
profiler_.DimensionsGraph(layout.plot_size);
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
@@ -894,8 +898,12 @@ void App::BuildGui() {
|
|||||||
ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowPos(chart_pos, ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(chart_size, ImGuiCond_FirstUseEver);
|
||||||
if (ImGui::Begin("Solver", &tmp_.chart_solver)) {
|
if (ImGui::Begin("Solver", &tmp_.chart_solver)) {
|
||||||
platform::CountsGui(model(), data());
|
auto layout = platform::ImPlot_ComputePairLayout();
|
||||||
platform::ConvergenceGui(model(), data());
|
platform::CountsGui(model(), data(), layout.plot_size);
|
||||||
|
if (layout.direction == platform::ImPlotLayoutDirection::kHorizontal) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
platform::ConvergenceGui(model(), data(), layout.plot_size);
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
@@ -1452,7 +1460,6 @@ void App::ToolBarGui() {
|
|||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("%s", "Reset");
|
ImGui::SetItemTooltip("%s", "Reset");
|
||||||
|
|
||||||
|
|
||||||
// Combined (Normal Pause, Viscous Pause, Play) widget and Speed selection.
|
// Combined (Normal Pause, Viscous Pause, Play) widget and Speed selection.
|
||||||
ImGui::SameLine(0, separator_width);
|
ImGui::SameLine(0, separator_width);
|
||||||
platform::StepControlGui(model(), &step_control_, tmp_.speed_index);
|
platform::StepControlGui(model(), &step_control_, tmp_.speed_index);
|
||||||
|
|||||||
Reference in New Issue
Block a user