Add aliases for MjModel loading functions and expose string constants as properties.
Introduces `from_xml_path` and `from_binary_path` as more idiomatic aliases for `mj_loadXML` and `mj_loadModel` respectively. Refactors the binding of `mj*STRING` constants. Instead of exposing `get_mj*STRING` functions, these constants are now available as properties on the `Module` object. Runtime getters are added via EM_ASM to lazily call the underlying C++ functions. PiperOrigin-RevId: 882566011 Change-Id: Id3bd5c23704fd122af30a50aed089bd3143fbc96
This commit is contained in:
committed by
Copybara-Service
parent
a62501f305
commit
df0bc58578
+13
-5
@@ -348,23 +348,31 @@ mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
|
||||
```
|
||||
|
||||
### Constants
|
||||
Scalar constants will be accessed the same way they are on python, simply:
|
||||
Scalar constants can be accessed as properties:
|
||||
|
||||
```javascript
|
||||
mujoco.mjNEQDATA
|
||||
```
|
||||
|
||||
Due to Embind limitations, more complex constants that are not scalar, but are
|
||||
represented in more dimensions are exposed as functions. E.g. to use
|
||||
`mujoco.mjFRAMESTRING` you will need to call a function:
|
||||
Non-scalar constants like `mjFRAMESTRING` are also accessed as properties, and
|
||||
return JavaScript arrays:
|
||||
|
||||
```javascript
|
||||
mujoco.get_mjFRAMESTRING()
|
||||
mujoco.mjFRAMESTRING
|
||||
```
|
||||
|
||||
This will return a javascript array representation of the values in MuJoCo
|
||||
`mjFRAMESTRING`.
|
||||
|
||||
> [!NOTE]
|
||||
> You will notice constants like `mjFRAMESTRING` are typed as `any`. This is
|
||||
> because they are bound using `emscripten::val::array()` in C++, and Embind
|
||||
> maps `emscripten::val` to `any` in TypeScript definition files. While
|
||||
> `EMSCRIPTEN_DECLARE_VAL_TYPE(StringArray)` could be used to define
|
||||
> `StringArray` as an alias for `emscripten::val` and hint to Embind how to
|
||||
> handle conversions in function signatures or when using `.as()` it does not
|
||||
> change how `emscripten::constant` infers types for properties.
|
||||
|
||||
## Development
|
||||
|
||||
In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
|
||||
|
||||
@@ -11597,8 +11597,11 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.property("uselimit", &MjLROpt::uselimit, &MjLROpt::set_uselimit, reference());
|
||||
emscripten::class_<MjModel>("MjModel")
|
||||
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))
|
||||
.class_function("from_xml_path", emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))
|
||||
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string, const MjVFS&)>(&mj_loadXML_wrapper_2))
|
||||
.class_function("from_xml_path", emscripten::select_overload<std::unique_ptr<MjModel>(std::string, const MjVFS&)>(&mj_loadXML_wrapper_2))
|
||||
.class_function("mj_loadModel", &mj_loadModel_wrapper)
|
||||
.class_function("from_binary_path", &mj_loadModel_wrapper)
|
||||
.constructor<const MjModel &>()
|
||||
// Binds the functions on MjModel that return accessors.
|
||||
#define X_ACCESSOR(NAME, Name, OBJTYPE, field_name, nfield) \
|
||||
@@ -13409,6 +13412,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
||||
|
||||
emscripten::register_vector<std::string>("mjStringVec");
|
||||
emscripten::register_vector<std::vector<std::string>>("mjStringVecVec");
|
||||
emscripten::register_vector<int>("mjIntVec");
|
||||
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
||||
emscripten::register_vector<float>("mjFloatVec");
|
||||
@@ -13453,7 +13457,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
emscripten::constant("mjPI", mjPI);
|
||||
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
||||
|
||||
// These complex constants are bound using function() rather than constant()
|
||||
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
||||
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
||||
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
||||
@@ -13461,6 +13464,31 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
||||
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
||||
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
||||
// Bind these complex constants as properties on the module object.
|
||||
// We use emscripten::constant with emscripten::val::array() to type them
|
||||
// as `any` in TypeScript. At runtime, the EM_ASM block below overrides
|
||||
// these properties with getters that return native JavaScript arrays
|
||||
// (string[] or string[][]) via the get_ functions above, which is more
|
||||
// performant and idiomatic than vector wrappers.
|
||||
emscripten::constant("mjDISABLESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjENABLESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjFRAMESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjLABELSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjRNDSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjTIMERSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjVISSTRING", emscripten::val::array());
|
||||
EM_ASM({
|
||||
if (typeof Module !== "undefined") {
|
||||
"mjDISABLESTRING mjENABLESTRING mjFRAMESTRING mjLABELSTRING mjRNDSTRING mjTIMERSTRING mjVISSTRING".split(" ").forEach(function(name) {
|
||||
Object.defineProperty(Module, name, {
|
||||
get: function() { return Module["get_" + name](); },
|
||||
set: function(v) { },
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mujoco::wasm
|
||||
|
||||
@@ -553,15 +553,25 @@ def _build_struct_bindings(
|
||||
'.class_function("mj_loadXML",'
|
||||
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))"
|
||||
)
|
||||
builder.line(
|
||||
'.class_function("from_xml_path",'
|
||||
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))"
|
||||
)
|
||||
builder.line(
|
||||
'.class_function("mj_loadXML",'
|
||||
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string,"
|
||||
" const MjVFS&)>(&mj_loadXML_wrapper_2))"
|
||||
)
|
||||
builder.line(
|
||||
'.class_function("from_xml_path",'
|
||||
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string,"
|
||||
" const MjVFS&)>(&mj_loadXML_wrapper_2))"
|
||||
)
|
||||
f2 = common.wrapped_function_name(
|
||||
introspect_functions.FUNCTIONS["mj_loadModel"]
|
||||
)
|
||||
builder.line(f'.class_function("mj_loadModel", &{f2})')
|
||||
builder.line(f'.class_function("from_binary_path", &{f2})')
|
||||
builder.line(".constructor<const MjModel &>()")
|
||||
builder.line("""
|
||||
// Binds the functions on MjModel that return accessors.
|
||||
|
||||
@@ -875,6 +875,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
||||
|
||||
emscripten::register_vector<std::string>("mjStringVec");
|
||||
emscripten::register_vector<std::vector<std::string>>("mjStringVecVec");
|
||||
emscripten::register_vector<int>("mjIntVec");
|
||||
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
||||
emscripten::register_vector<float>("mjFloatVec");
|
||||
@@ -919,7 +920,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
emscripten::constant("mjPI", mjPI);
|
||||
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
||||
|
||||
// These complex constants are bound using function() rather than constant()
|
||||
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
||||
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
||||
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
||||
@@ -927,6 +927,31 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
||||
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
||||
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
||||
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
||||
// Bind these complex constants as properties on the module object.
|
||||
// We use emscripten::constant with emscripten::val::array() to type them
|
||||
// as `any` in TypeScript. At runtime, the EM_ASM block below overrides
|
||||
// these properties with getters that return native JavaScript arrays
|
||||
// (string[] or string[][]) via the get_ functions above, which is more
|
||||
// performant and idiomatic than vector wrappers.
|
||||
emscripten::constant("mjDISABLESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjENABLESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjFRAMESTRING", emscripten::val::array());
|
||||
emscripten::constant("mjLABELSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjRNDSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjTIMERSTRING", emscripten::val::array());
|
||||
emscripten::constant("mjVISSTRING", emscripten::val::array());
|
||||
EM_ASM({
|
||||
if (typeof Module !== "undefined") {
|
||||
"mjDISABLESTRING mjENABLESTRING mjFRAMESTRING mjLABELSTRING mjRNDSTRING mjTIMERSTRING mjVISSTRING".split(" ").forEach(function(name) {
|
||||
Object.defineProperty(Module, name, {
|
||||
get: function() { return Module["get_" + name](); },
|
||||
set: function(v) { },
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mujoco::wasm
|
||||
|
||||
@@ -683,24 +683,24 @@ describe('MuJoCo WASM Bindings', () => {
|
||||
|
||||
it('should check constants values', () => {
|
||||
expect(mujoco.mjNEQDATA).toBe(11);
|
||||
expect(mujoco.get_mjDISABLESTRING()).toEqual([
|
||||
expect(mujoco.mjDISABLESTRING).toEqual([
|
||||
'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact', 'Spring',
|
||||
'Damper', 'Gravity', 'Clampctrl', 'Warmstart', 'Filterparent',
|
||||
'Actuation', 'Refsafe', 'Sensor', 'Midphase', 'Eulerdamp', 'AutoReset',
|
||||
'NativeCCD', 'Island'
|
||||
]);
|
||||
expect(mujoco.get_mjRNDSTRING()).toEqual([
|
||||
expect(mujoco.mjRNDSTRING).toEqual([
|
||||
['Shadow', '1', 'S'], ['Wireframe', '0', 'W'], ['Reflection', '1', 'R'],
|
||||
['Additive', '0', 'L'], ['Skybox', '1', 'K'], ['Fog', '0', 'G'],
|
||||
['Haze', '1', '/'], ['Depth', '0', ''], ['Segment', '0', ','],
|
||||
['Id Color', '0', ''], ['Cull Face', '1', '']
|
||||
]);
|
||||
expect(mujoco.get_mjFRAMESTRING().length)
|
||||
.toEqual(mujoco.mjtFrame.mjNFRAME.value);
|
||||
expect(mujoco.get_mjVISSTRING().length)
|
||||
expect(mujoco.mjFRAMESTRING.length).toEqual(mujoco.mjtFrame.mjNFRAME.value);
|
||||
expect(mujoco.mjVISSTRING.length)
|
||||
.toEqual(mujoco.mjtVisFlag.mjNVISFLAG.value);
|
||||
expect(mujoco.get_mjVISSTRING()[mujoco.mjtVisFlag.mjVIS_INERTIA.value])
|
||||
.toEqual(['Inertia', '0', 'I']);
|
||||
expect(mujoco.mjVISSTRING[mujoco.mjtVisFlag.mjVIS_INERTIA.value]).toEqual([
|
||||
'Inertia', '0', 'I'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create a spec from XML', () => {
|
||||
|
||||
Reference in New Issue
Block a user