Add Visualization tab to simulate.

Also:
- Increased label width in mjUI tight theme spacing.
- Added `mjITEM_EDITFLOAT` for editing float values.
- Move mjUI numpad key codes to header file.
PiperOrigin-RevId: 535839953
Change-Id: Ibdc0a9a20be514caa5b487211212ed6f5b8a6ba9
This commit is contained in:
Yuval Tassa
2023-05-27 05:07:38 -07:00
committed by Copybara-Service
parent ea6789b376
commit d40c395917
10 changed files with 164 additions and 31 deletions
+32 -12
View File
@@ -25,10 +25,41 @@ Bug fixes
which affects the behaviour of the :ref:`implicit and implicitfast integrators<geIntegration>`.
- Fixes to :ref:`mj_ray`, in line with geom visualisation conventions:
- Planes and height-fields respect the `geom_group` and `flg_static` arguments. Before this change, rays would
- Planes and height-fields respect the ``geom_group`` and ``flg_static`` arguments. Before this change, rays would
intersect planes and height-fields unconditionally.
- ``flg_static`` now apllies to all static geoms, not just those which are direct children of the world body.
.. youtube:: hqIMTNGaLF4
:align: right
:width: 240px
Plugins
^^^^^^^
- Added touch-grid sensor plugin. See `documentation <https://github.com/deepmind/mujoco/blob/main/plugin/sensor/README.md>`_
for details, and associated `touch_grid.xml <https://github.com/deepmind/mujoco/blob/main/model/plugin/touch_grid.xml>`_
example model. The plugin includes `in-scene visualisation <https://youtu.be/0LOJ3WMnqeA>`_.
Simulate
^^^^^^^^
.. youtube:: mXVPbppGk5I
:align: right
:width: 240px
- Added Visualization tab to simulate UI, corresponding to elements of the :ref:`visual<visual>` MJCF element. After
modifying values in the GUI, a saved XML will contain the new values. The modifyable members of
:ref:`mjStatistic` (:ref:`extent<statistic-extent>`, :ref:`meansize<statistic-meansize>` and
:ref:`center<statistic-center>`) are computed by the compiler and therefore do not have defaults. In order for these
attributes to appear in the saved XML, a value must be specified in the loaded XML.
.. image:: images/changelog/simulate_text_width.png
:align: right
:width: 380px
:alt: Before / After
- Increased text width for UI elements in the default spacing. [before / after]:
General
^^^^^^^
@@ -65,17 +96,6 @@ General
- Removed ``pstack`` and ``parena`` from the output of ``mj_printData``, since these are implementation details of the
``mjData`` allocators that are affected by diagnostic paddings in instrumented builds.
.. youtube:: hqIMTNGaLF4
:align: right
:width: 240px
Plugins
^^^^^^^
- Added touch-grid sensor plugin. See `documentation <https://github.com/deepmind/mujoco/blob/main/plugin/sensor/README.md>`_
for details, and associated `touch_grid.xml <https://github.com/deepmind/mujoco/blob/main/model/plugin/touch_grid.xml>`_
example model. The plugin includes `in-scene visualisation <https://youtu.be/0LOJ3WMnqeA>`_.
Version 2.3.5 (April 25, 2023)
------------------------------
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

+1
View File
@@ -1402,6 +1402,7 @@ typedef enum mjtItem_ { // UI item type
mjITEM_SLIDERNUM, // slider, mjtNum value
mjITEM_EDITINT, // editable array, int values
mjITEM_EDITNUM, // editable array, mjtNum values
mjITEM_EDITFLOAT, // editable array, float values
mjITEM_EDITTXT, // editable text
mjNITEM // number of item types
+3
View File
@@ -55,6 +55,8 @@
#define mjKEY_F10 299
#define mjKEY_F11 300
#define mjKEY_F12 301
#define mjKEY_NUMPAD_0 320
#define mjKEY_NUMPAD_9 329
//---------------------------------- primitive types (mjt) -----------------------------------------
@@ -97,6 +99,7 @@ typedef enum mjtItem_ { // UI item type
mjITEM_SLIDERNUM, // slider, mjtNum value
mjITEM_EDITINT, // editable array, int values
mjITEM_EDITNUM, // editable array, mjtNum values
mjITEM_EDITFLOAT, // editable array, float values
mjITEM_EDITTXT, // editable text
mjNITEM // number of item types
+3 -2
View File
@@ -654,8 +654,9 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjITEM_SLIDERNUM', 9),
('mjITEM_EDITINT', 10),
('mjITEM_EDITNUM', 11),
('mjITEM_EDITTXT', 12),
('mjNITEM', 13),
('mjITEM_EDITFLOAT', 12),
('mjITEM_EDITTXT', 13),
('mjNITEM', 14),
]),
)),
])
+67 -1
View File
@@ -102,6 +102,7 @@ enum {
SECT_WATCH,
SECT_PHYSICS,
SECT_RENDERING,
SECT_VISUALIZATION,
SECT_GROUP,
NSECT0,
@@ -565,7 +566,7 @@ void UpdateWatch(mj::Simulate* sim, const mjModel* m, const mjData* d) {
if (!mju::strcmp_arr(#NAME, sim->field) && \
!mju::strcmp_arr(#TYPE, "mjtNum")) { \
if (sim->index >= 0 && sim->index < m->NR * NC) { \
PrintField(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0],d->NAME + sim->index); \
PrintField(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0], d->NAME + sim->index); \
} else { \
mju::strcpy_arr(sim->ui0.sect[SECT_WATCH].item[2].multi.name[0], "invalid index"); \
} \
@@ -768,7 +769,64 @@ void MakeRenderingSection(mj::Simulate* sim, const mjModel* m, int oldstate) {
}
}
// make visualization section of UI
void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m, int oldstate) {
mjStatistic* stat = sim->fully_managed_ ? &sim->m_->stat : &sim->scnstate_.model.stat;
mjVisual* vis = sim->fully_managed_ ? &sim->m_->vis : &sim->scnstate_.model.vis;
mjuiDef defVisualization[] = {
{mjITEM_SECTION, "Visualization", oldstate, nullptr, "AV"},
{mjITEM_SEPARATOR, "Headlight", 1},
{mjITEM_RADIO, "Active", 5, &(vis->headlight.active), "Off\nOn"},
{mjITEM_EDITFLOAT, "Ambient", 2, &(vis->headlight.ambient), "3"},
{mjITEM_EDITFLOAT, "Diffuse", 2, &(vis->headlight.diffuse), "3"},
{mjITEM_EDITFLOAT, "Specular", 2, &(vis->headlight.specular), "3"},
{mjITEM_SEPARATOR, "Initial Free Camera", 1},
{mjITEM_EDITNUM, "Center", 2, &(stat->center), "3"},
{mjITEM_EDITFLOAT, "Azimuth", 2, &(vis->global.azimuth), "1"},
{mjITEM_EDITFLOAT, "Elevation", 2, &(vis->global.elevation), "1"},
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
{mjITEM_SEPARATOR, "Global", 1},
{mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"},
{mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"},
{mjITEM_RADIO, "Inertia", 5, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"},
{mjITEM_SEPARATOR, "Map", 1},
{mjITEM_EDITFLOAT, "Stiffness", 2, &(vis->map.stiffness), "1"},
{mjITEM_EDITFLOAT, "Rot stiffness", 2, &(vis->map.stiffnessrot), "1"},
{mjITEM_EDITFLOAT, "Force", 2, &(vis->map.force), "1"},
{mjITEM_EDITFLOAT, "Torque", 2, &(vis->map.torque), "1"},
{mjITEM_EDITFLOAT, "Alpha", 2, &(vis->map.alpha), "1"},
{mjITEM_EDITFLOAT, "Fog start", 2, &(vis->map.fogstart), "1"},
{mjITEM_EDITFLOAT, "Fog end", 2, &(vis->map.fogend), "1"},
{mjITEM_EDITFLOAT, "Z near", 2, &(vis->map.znear), "1"},
{mjITEM_EDITFLOAT, "Z far", 2, &(vis->map.zfar), "1"},
{mjITEM_EDITFLOAT, "Haze", 2, &(vis->map.haze), "1"},
{mjITEM_EDITFLOAT, "Shadow clip", 2, &(vis->map.shadowclip), "1"},
{mjITEM_EDITFLOAT, "Shadow scale", 2, &(vis->map.shadowscale), "1"},
{mjITEM_SEPARATOR, "Scale", 1},
{mjITEM_EDITNUM, "All [meansize]", 2, &(stat->meansize), "1"},
{mjITEM_EDITFLOAT, "Force width", 2, &(vis->scale.forcewidth), "1"},
{mjITEM_EDITFLOAT, "Contact width", 2, &(vis->scale.contactwidth), "1"},
{mjITEM_EDITFLOAT, "Contact height", 2, &(vis->scale.contactheight), "1"},
{mjITEM_EDITFLOAT, "Connect", 2, &(vis->scale.connect), "1"},
{mjITEM_EDITFLOAT, "Com", 2, &(vis->scale.com), "1"},
{mjITEM_EDITFLOAT, "Camera", 2, &(vis->scale.camera), "1"},
{mjITEM_EDITFLOAT, "Light", 2, &(vis->scale.light), "1"},
{mjITEM_EDITFLOAT, "Select point", 2, &(vis->scale.selectpoint), "1"},
{mjITEM_EDITFLOAT, "Joint length", 2, &(vis->scale.jointlength), "1"},
{mjITEM_EDITFLOAT, "Joint width", 2, &(vis->scale.jointwidth), "1"},
{mjITEM_EDITFLOAT, "Actuator length", 2, &(vis->scale.actuatorlength), "1"},
{mjITEM_EDITFLOAT, "Actuator width", 2, &(vis->scale.actuatorwidth), "1"},
{mjITEM_EDITFLOAT, "Frame length", 2, &(vis->scale.framelength), "1"},
{mjITEM_EDITFLOAT, "Frame width", 2, &(vis->scale.framewidth), "1"},
{mjITEM_EDITFLOAT, "Constraint", 2, &(vis->scale.constraint), "1"},
{mjITEM_EDITFLOAT, "Slider-crank", 2, &(vis->scale.slidercrank), "1"},
{mjITEM_END}
};
// add rendering standard
mjui_add(&sim->ui0, defVisualization);
}
// make group section of UI
void MakeGroupSection(mj::Simulate* sim, int oldstate) {
@@ -952,6 +1010,7 @@ void MakeUiSections(mj::Simulate* sim, const mjModel* m, const mjData* d) {
// make
MakePhysicsSection(sim, oldstate0[SECT_PHYSICS]);
MakeRenderingSection(sim, m, oldstate0[SECT_RENDERING]);
MakeVisualizationSection(sim, m, oldstate0[SECT_VISUALIZATION]);
MakeGroupSection(sim, oldstate0[SECT_GROUP]);
MakeJointSection(sim, oldstate1[SECT_JOINT]);
MakeControlSection(sim, oldstate1[SECT_CONTROL]);
@@ -1284,6 +1343,13 @@ void UiEvent(mjuiState* state) {
}
}
// visualization section
else if (it && it->sectionid==SECT_VISUALIZATION) {
if (!mju::strcmp_arr(it->name, "Align")) {
sim->pending_.align = true;
}
}
// group section
else if (it && it->sectionid==SECT_GROUP) {
// remake joint section if joint group changed
+29 -16
View File
@@ -28,7 +28,7 @@
static const mjuiThemeSpacing themeSpacing0 = {
270, // int total;
15, // int scroll;
100, // int label;
120, // int label;
8, // int section;
4, // int itemside;
4, // int itemmid;
@@ -743,7 +743,9 @@ static int checkedit(const char* text, const mjuiItem* it) {
}
// check type
if (it->type!=mjITEM_EDITINT && it->type!=mjITEM_EDITNUM) {
if (it->type!=mjITEM_EDITINT &&
it->type!=mjITEM_EDITNUM &&
it->type!=mjITEM_EDITFLOAT) {
mju_error("Internal error: expected edit control");
}
@@ -791,7 +793,7 @@ static int text2array(const char* text, const mjuiItem* it) {
}
// check type
if (it->type!=mjITEM_EDITINT && it->type!=mjITEM_EDITNUM) {
if (it->type!=mjITEM_EDITINT && it->type!=mjITEM_EDITNUM && it->type!=mjITEM_EDITFLOAT) {
mju_error("Internal error: expected edit control");
}
@@ -829,9 +831,16 @@ static int text2array(const char* text, const mjuiItem* it) {
pdata[i] = (int)val[i];
}
} else {
mjtNum* pdata = (mjtNum*)it->pdata;
for (int i=0; i<n; i++) {
pdata[i] = (mjtNum)val[i];
if (it->type==mjITEM_EDITNUM) {
mjtNum* pdata = (mjtNum*)it->pdata;
for (int i=0; i<n; i++) {
pdata[i] = (mjtNum)val[i];
}
} else {
float* pdata = (float*)it->pdata;
for (int i=0; i<n; i++) {
pdata[i] = (float)val[i];
}
}
}
@@ -851,7 +860,7 @@ static void array2text(char* text, const mjuiItem* it) {
}
// check type
if (it->type!=mjITEM_EDITINT && it->type!=mjITEM_EDITNUM) {
if (it->type!=mjITEM_EDITINT && it->type!=mjITEM_EDITNUM && it->type!=mjITEM_EDITFLOAT) {
mju_error("Internal error: expected edit control");
}
@@ -862,8 +871,10 @@ static void array2text(char* text, const mjuiItem* it) {
for (int i=0; i<n; i++) {
if (it->type==mjITEM_EDITINT) {
mjSNPRINTF(buf, "%d", ((int*)it->pdata)[i]);
} else {
} else if (it->type==mjITEM_EDITNUM) {
mjSNPRINTF(buf, "%.4g", ((mjtNum*)it->pdata)[i]);
} else {
mjSNPRINTF(buf, "%.4g", ((float*)it->pdata)[i]);
}
strncat(text, buf, mjMAXUITEXT - strlen(text) - 1);
if (i<n-1) {
@@ -872,8 +883,6 @@ static void array2text(char* text, const mjuiItem* it) {
}
}
static const int mjNUMPAD_0 = 320; // integer value of '0' key on the number pad
static const int mjNUMPAD_9 = 329; // integer value of '9' key on the number pad
// return (remapped) key if valid, 0 if not valid
static int validkey(int key, int sz, int type, const mjuiState* state) {
@@ -919,11 +928,12 @@ static int validkey(int key, int sz, int type, const mjuiState* state) {
}
// numeric
else if (type==mjITEM_EDITINT || type==mjITEM_EDITNUM) {
else if (type==mjITEM_EDITINT || type==mjITEM_EDITNUM || type==mjITEM_EDITFLOAT) {
if (sz<(mjMAXUITEXT-1) &&
(key==' ' || key=='+' || key=='=' || key=='-' || (key>='0' && key<='9') ||
(key>=mjNUMPAD_0 && key<=mjNUMPAD_9) ||
((key=='e' || key=='E' || key=='.') && type==mjITEM_EDITNUM))) {
(key>=mjKEY_NUMPAD_0 && key<=mjKEY_NUMPAD_9) ||
((key=='e' || key=='E' || key=='.') &&
(type==mjITEM_EDITNUM || type==mjITEM_EDITFLOAT)))) {
// remap '=' to '+'
if (key=='=') {
@@ -936,8 +946,8 @@ static int validkey(int key, int sz, int type, const mjuiState* state) {
}
// remap numberpad to top row
if (key>=mjNUMPAD_0 && key<=mjNUMPAD_9) {
key = key - mjNUMPAD_0 + '0';
if (key>=mjKEY_NUMPAD_0 && key<=mjKEY_NUMPAD_9) {
key = key - mjKEY_NUMPAD_0 + '0';
}
return key;
@@ -1271,7 +1281,7 @@ void mjui_add(mjUI* ui, const mjuiDef* def) {
}
// parse edit numeric
else if (it->type==mjITEM_EDITINT || it->type==mjITEM_EDITNUM) {
else if (it->type==mjITEM_EDITINT || it->type==mjITEM_EDITNUM || it->type==mjITEM_EDITFLOAT) {
// check mjMAXUIEDIT
if (mjMAXUIEDIT>7) {
mju_error("internal error: mjMAXUIEDIT bigger than 7");
@@ -1989,6 +1999,7 @@ void mjui_update(int section, int item, const mjUI* ui,
case mjITEM_EDITINT:
case mjITEM_EDITNUM:
case mjITEM_EDITFLOAT:
case mjITEM_EDITTXT:
if (state) {
rgbdecor = ui->color.edit;
@@ -2165,6 +2176,7 @@ mjuiItem* mjui_event(mjUI* ui, mjuiState* state, const mjrContext* con) {
// move edit
else if (it_rec->type==mjITEM_EDITINT ||
it_rec->type==mjITEM_EDITNUM ||
it_rec->type==mjITEM_EDITFLOAT ||
it_rec->type==mjITEM_EDITTXT) {
setcursor(it_rec->rect, ui, state, con);
}
@@ -2326,6 +2338,7 @@ mjuiItem* mjui_event(mjUI* ui, mjuiState* state, const mjrContext* con) {
case mjITEM_EDITINT:
case mjITEM_EDITNUM:
case mjITEM_EDITFLOAT:
case mjITEM_EDITTXT:
// set edit text, clear scroll
array2text(ui->edittext, it_cur);
+5
View File
@@ -2897,6 +2897,11 @@ bool mjCModel::CopyBack(const mjModel* m) {
option = m->opt;
visual = m->vis;
// runtime-modifiable members of mjStatistic
meansize = m->stat.meansize;
extent = m->stat.extent;
mju_copy3(center, m->stat.center);
// qpos0, qpos_spring
for (int i=0; i<njnt; i++) {
switch (joints[i]->type) {
+22
View File
@@ -1068,5 +1068,27 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
}
}
// ---------------- test CopyBack functionality (decompiler) ------------------
using DecompilerTest = MujocoTest;
TEST_F(XMLWriterTest, SavesStatitics) {
static constexpr char xml[] = R"(
<mujoco>
<statistic meansize="2" extent="3" center="4 5 6"/>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, NotNull());
model->stat.meansize = 7;
model->stat.extent = 8;
model->stat.center[0] = 9;
model->stat.center[1] = 10;
model->stat.center[2] = 11;
std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("meansize=\"7\""));
EXPECT_THAT(saved_xml, HasSubstr("extent=\"8\""));
EXPECT_THAT(saved_xml, HasSubstr("center=\"9 10 11\""));
mj_deleteModel(model);
}
} // namespace
} // namespace mujoco
+2
View File
@@ -94,6 +94,8 @@ public const int mjKEY_F9 = 298;
public const int mjKEY_F10 = 299;
public const int mjKEY_F11 = 300;
public const int mjKEY_F12 = 301;
public const int mjKEY_NUMPAD_0 = 320;
public const int mjKEY_NUMPAD_9 = 329;
public const bool THIRD_PARTY_MUJOCO_MJVISUALIZE_H_ = true;
public const int mjNGROUP = 6;
public const int mjMAXLIGHT = 100;