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:
committed by
Copybara-Service
parent
ea6789b376
commit
d40c395917
+32
-12
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user