Reformat README.md for better readability.

Wrap long lines to 80 characters.

PiperOrigin-RevId: 877983563
Change-Id: I10db94397eb3c2db070734ae78073073eefa0184
This commit is contained in:
Google DeepMind
2026-03-03 09:20:14 -08:00
committed by Copybara-Service
parent c94a5f3bad
commit 0974612338
+49 -19
View File
@@ -123,8 +123,8 @@ joint using `data.jnt('myjoint')`.
For more details and examples of how to use named access, please refer to the [named access tests](tests/bindings_test.ts#L1876-L2378) and [documentation](https://mujoco.readthedocs.io/en/stable/python.html#named-access).
### Memory Management
Embind-wrapped C++ object handles created or returned into JavaScript live on the
WebAssembly heap and are **not** garbage-collected by the JS runtime.
Embind-wrapped C++ object handles created or returned into JavaScript live on
the WebAssembly heap and are **not** garbage-collected by the JS runtime.
Any heap-allocated C++ object exposed to JS (e.g. via `new Module.MyClass(...)`
or returned as a pointer/reference from a binding) must be explicitly freed
@@ -144,19 +144,26 @@ is an error). In JS code paths that may throw or return early, ensure
deletion happens in finally blocks or wrap lifetime management to avoid leaks.
> [!IMPORTANT]
> _Embind's documentation strongly recommends that JavaScript code explicitly deletes any C++ object handles it has received._
> _Embind's documentation strongly recommends that JavaScript code explicitly
> deletes any C++ object handles it has received._
### Copy vs. Reference
When interacting with MuJoCo objects through the WASM bindings, it's important to understand how data is accessed. Properties on objects like `MjModel` and `MjData` can expose data in two ways: by copy or by reference.
When interacting with MuJoCo objects through the WASM bindings, it's important
to understand how data is accessed. Properties on objects like `MjModel` and
`MjData` can expose data in two ways: by copy or by reference.
#### 1. By Copy (Value-based access)
Some properties return a copy of the data at the time of access. This is common for complex data structures that need to be marshalled from C++ to JavaScript.
Some properties return a copy of the data at the time of access. This is common
for complex data structures that need to be marshalled from C++ to JavaScript.
A key example is `MjData.contact`. When you access `data.contact`, you get an object containing a copy of the contacts at that specific moment in the simulation.
A key example is `MjData.contact`. When you access `data.contact`, you get an
object containing a copy of the contacts at that specific moment in the
simulation.
If you step the simulation forward, they will not be updated. You must access `data.contact` again to get the new contact information.
If you step the simulation forward, they will not be updated. You must access
`data.contact` again to get the new contact information.
The object you get is a JavaScript proxy interface generated by [Emscriptens Embind library](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) when you expose a `std::vector` using `register_vector<T>`. It is essentially a "bridge" object.
@@ -193,9 +200,14 @@ newContacts.delete();
#### 2. By Reference (View-based access)
Many properties, especially large numerical arrays, return a live view directly into the WebAssembly memory. This is highly efficient as it avoids copying large amounts of data.
Many properties, especially large numerical arrays, return a live view directly
into the WebAssembly memory. This is highly efficient as it avoids copying large
amounts of data.
A key example is `MjData.qpos` (joint positions). When you get a reference to this array, it points directly to the simulation's state data. Any changes in the simulation (e.g., after a call to `mj_step`) will be immediately reflected in this array.
A key example is `MjData.qpos` (joint positions). When you get a reference to
this array, it points directly to the simulation's state data. Any changes in
the simulation (e.g., after a call to `mj_step`) will be immediately reflected
in this array.
```typescript
// `qpos` is a live view into the simulation state.
@@ -215,9 +227,13 @@ data.delete();
### Data Layout: Row-Major Matrices
When a function from the MuJoCo C API returns a matrix (or needs a matrix as input), these are represented in the JavaScript bindings as flat, one-dimensional `TypedArray`'s. The elements are stored in row-major order.
When a function from the MuJoCo C API returns a matrix (or needs a matrix as
input), these are represented in the JavaScript bindings as flat,
one-dimensional `TypedArray`'s. The elements are stored in row-major order.
For example, a 3x10 matrix will be returned as a flat array with 30 elements. The first 10 elements represent the first row, the next 10 represent the second row, and so on.
For example, a 3x10 matrix will be returned as a flat array with 30 elements.
The first 10 elements represent the first row, the next 10 represent the second
row, and so on.
Example: Accessing an element at `(row, col)`
```typescript
@@ -232,13 +248,19 @@ const element = matrix[i * nCols + j];
### Working with Out Parameters
Many functions in the MuJoCo C API use "out parameters" to return data. This means instead of returning a value, they write the result into one of the arguments passed to them by reference (using pointers). In our JavaScript bindings, you'll need to handle these cases specifically.
Many functions in the MuJoCo C API use "out parameters" to return data. This
means instead of returning a value, they write the result into one of the
arguments passed to them by reference (using pointers). In our JavaScript
bindings, you'll need to handle these cases specifically.
There are two main scenarios you'll encounter:
#### 1. Array-like Out Parameters
When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`) to write an array of values, you need to pre-allocate memory for the result on the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`, `mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.
When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`)
to write an array of values, you need to pre-allocate memory for the result on
the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`,
`mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.
Here's how to use them:
@@ -249,7 +271,8 @@ Here's how to use them:
Example: Rotating a vector
The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and stores the result in the `res` out parameter.
The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and
stores the result in the `res` out parameter.
```typescript
// Create a buffer to hold the 3D vector result.
@@ -274,11 +297,14 @@ try {
#### 2. Struct Out Parameters (e.g., mjvCamera*, mjvScene*)
When a function modifies a struct passed by pointer, you should pass an instance of the corresponding JavaScript wrapper class. The underlying C++ struct will be modified in place.
When a function modifies a struct passed by pointer, you should pass an instance
of the corresponding JavaScript wrapper class. The underlying C++ struct will be
modified in place.
Example: Updating a scene
The function `mjv_updateScene` populates an `mjvScene` object with information from `mjModel` and `mjData`.
The function `mjv_updateScene` populates an `mjvScene` object with information
from `mjModel` and `mjData`.
```typescript
// Create instances of the necessary structs.
const model = mujoco.MjModel.loadFromXML(xmlContent);
@@ -312,7 +338,8 @@ data.delete();
model.delete();
```
As with buffers, you are responsible for managing the memory of these struct instances and must call `.delete()` on them when you are finished.
As with buffers, you are responsible for managing the memory of these struct
instances and must call `.delete()` on them when you are finished.
### Enums
Access via `.value`:
@@ -327,13 +354,16 @@ Scalar constants will be accessed the same way they are on python, simply:
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:
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:
```javascript
mujoco.get_mjFRAMESTRING()
```
This will return a javascript array representation of the values in MuJoCo `mjFRAMESTRING`.
This will return a javascript array representation of the values in MuJoCo
`mjFRAMESTRING`.
## Development