mujoco-react 7.0.1 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -60
- package/dist/index.d.ts +112 -33
- package/dist/index.js +160 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/TrajectoryPlayer.tsx +16 -2
- package/src/hooks/useBodyState.ts +2 -2
- package/src/hooks/useContacts.ts +3 -3
- package/src/hooks/useCtrl.ts +31 -18
- package/src/hooks/useJointState.ts +2 -2
- package/src/hooks/useSensor.ts +14 -5
- package/src/hooks/useSitePosition.ts +2 -2
- package/src/hooks/useTrajectoryPlayer.ts +152 -29
- package/src/index.ts +13 -0
- package/src/types.ts +71 -14
package/README.md
CHANGED
|
@@ -24,18 +24,18 @@ import {
|
|
|
24
24
|
MujocoCanvas,
|
|
25
25
|
useIkController,
|
|
26
26
|
IkGizmo,
|
|
27
|
-
} from
|
|
28
|
-
import type { SceneConfig } from
|
|
29
|
-
import { OrbitControls } from
|
|
27
|
+
} from "mujoco-react";
|
|
28
|
+
import type { SceneConfig } from "mujoco-react";
|
|
29
|
+
import { OrbitControls } from "@react-three/drei";
|
|
30
30
|
|
|
31
31
|
const config: SceneConfig = {
|
|
32
|
-
src:
|
|
33
|
-
sceneFile:
|
|
32
|
+
src: "https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/franka_emika_panda/",
|
|
33
|
+
sceneFile: "scene.xml",
|
|
34
34
|
homeJoints: [1.707, -1.754, 0.003, -2.702, 0.003, 0.951, 2.490],
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
function Scene() {
|
|
38
|
-
const ik = useIkController({ siteName:
|
|
38
|
+
const ik = useIkController({ siteName: "tcp", numJoints: 7 });
|
|
39
39
|
return (
|
|
40
40
|
<>
|
|
41
41
|
<OrbitControls enableDamping makeDefault />
|
|
@@ -53,7 +53,7 @@ function App() {
|
|
|
53
53
|
config={config}
|
|
54
54
|
camera={{ position: [2, -1.5, 2.5], up: [0, 0, 1], fov: 45 }}
|
|
55
55
|
shadows
|
|
56
|
-
style={{ width:
|
|
56
|
+
style={{ width: "100%", height: "100vh" }}
|
|
57
57
|
>
|
|
58
58
|
<Scene />
|
|
59
59
|
</MujocoCanvas>
|
|
@@ -67,7 +67,7 @@ function App() {
|
|
|
67
67
|
Inside `<MujocoCanvas>` or `<MujocoPhysics>`, `useMujoco()` gives you the simulation API, refs to the live model/data, and status:
|
|
68
68
|
|
|
69
69
|
```tsx
|
|
70
|
-
import { useMujoco } from
|
|
70
|
+
import { useMujoco } from "mujoco-react";
|
|
71
71
|
|
|
72
72
|
function MyComponent() {
|
|
73
73
|
const { isPending, isError, error, api, mjModelRef } = useMujoco();
|
|
@@ -85,15 +85,19 @@ function MyComponent() {
|
|
|
85
85
|
|
|
86
86
|
## Writing a Controller
|
|
87
87
|
|
|
88
|
-
A controller is a React component that
|
|
88
|
+
A controller is a React component that uses handle-based hooks for type-safe actuator and sensor access:
|
|
89
89
|
|
|
90
90
|
```tsx
|
|
91
|
-
import { useBeforePhysicsStep } from
|
|
91
|
+
import { useCtrl, useSensor, useBeforePhysicsStep } from "mujoco-react";
|
|
92
92
|
|
|
93
93
|
function MyController() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
const shoulder = useCtrl("shoulder");
|
|
95
|
+
const elbow = useCtrl("elbow");
|
|
96
|
+
const force = useSensor("force_sensor");
|
|
97
|
+
|
|
98
|
+
useBeforePhysicsStep(() => {
|
|
99
|
+
shoulder.write(Math.sin(Date.now() / 1000));
|
|
100
|
+
elbow.write(force.read()[0] * -0.5);
|
|
97
101
|
});
|
|
98
102
|
return null;
|
|
99
103
|
}
|
|
@@ -110,10 +114,10 @@ Drop it into the tree:
|
|
|
110
114
|
The `createController<TConfig>()` factory adds typed config and default merging for reusable plugins:
|
|
111
115
|
|
|
112
116
|
```tsx
|
|
113
|
-
import { createController, useBeforePhysicsStep } from
|
|
117
|
+
import { createController, useBeforePhysicsStep } from "mujoco-react";
|
|
114
118
|
|
|
115
119
|
export const MyController = createController<{ gain: number }>(
|
|
116
|
-
{ name:
|
|
120
|
+
{ name: "MyController", defaultConfig: { gain: 1.0 } },
|
|
117
121
|
({ config }) => {
|
|
118
122
|
useBeforePhysicsStep((_model, data) => {
|
|
119
123
|
data.ctrl[0] = config.gain * Math.sin(data.time);
|
|
@@ -145,13 +149,13 @@ export const MyController = createController<{ gain: number }>(
|
|
|
145
149
|
The built-in `useIkController()` uses Damped Least-Squares. Pass `ikSolveFn` to swap in your own solver (analytical, learned, etc.):
|
|
146
150
|
|
|
147
151
|
```tsx
|
|
148
|
-
import type { IKSolveFn } from
|
|
152
|
+
import type { IKSolveFn } from "mujoco-react";
|
|
149
153
|
|
|
150
154
|
const myIK: IKSolveFn = (pos, quat, currentQ) => {
|
|
151
155
|
return myAnalyticalSolver(pos, currentQ); // return joint angles or null
|
|
152
156
|
};
|
|
153
157
|
|
|
154
|
-
const ik = useIkController({ siteName:
|
|
158
|
+
const ik = useIkController({ siteName: "tcp", numJoints: 7, ikSolveFn: myIK });
|
|
155
159
|
```
|
|
156
160
|
|
|
157
161
|
### `useIkController(config | null)`
|
|
@@ -159,7 +163,7 @@ const ik = useIkController({ siteName: 'tcp', numJoints: 7, ikSolveFn: myIK });
|
|
|
159
163
|
Hook for interactive end-effector control. Pass `null` to disable IK (safe to call unconditionally):
|
|
160
164
|
|
|
161
165
|
```tsx
|
|
162
|
-
const ik = useIkController({ siteName:
|
|
166
|
+
const ik = useIkController({ siteName: "tcp", numJoints: 7 });
|
|
163
167
|
return ik ? <IkGizmo controller={ik} /> : null;
|
|
164
168
|
```
|
|
165
169
|
|
|
@@ -175,6 +179,23 @@ Returns `IkContextValue | null` with methods like `setIkEnabled`, `moveTarget`,
|
|
|
175
179
|
|
|
176
180
|
Pass the returned value to `<IkGizmo controller={ik} />` or to your own controller as a prop.
|
|
177
181
|
|
|
182
|
+
## Type-Safe Resource Names
|
|
183
|
+
|
|
184
|
+
Use TypeScript module augmentation to get autocomplete and type checking for actuator, sensor, body, joint, site, geom, and keyframe names:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// e.g. in src/mujoco-register.d.ts
|
|
188
|
+
declare module "mujoco-react" {
|
|
189
|
+
interface Register {
|
|
190
|
+
actuators: "joint1" | "joint2" | "joint3" | "gripper";
|
|
191
|
+
sensors: "force_sensor" | "torque_sensor";
|
|
192
|
+
bodies: "link0" | "link1" | "hand";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Once declared, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` will only accept the declared names. When no `Register` augmentation is provided, all names fall back to `string`.
|
|
198
|
+
|
|
178
199
|
## Loading Models
|
|
179
200
|
|
|
180
201
|
The loader fetches `src + sceneFile`, parses the XML for dependencies (meshes, textures, includes), recursively fetches those too, and writes everything to MuJoCo's in-memory WASM filesystem.
|
|
@@ -182,14 +203,14 @@ The loader fetches `src + sceneFile`, parses the XML for dependencies (meshes, t
|
|
|
182
203
|
```tsx
|
|
183
204
|
// MuJoCo Menagerie
|
|
184
205
|
const franka: SceneConfig = {
|
|
185
|
-
src:
|
|
186
|
-
sceneFile:
|
|
206
|
+
src: "https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/franka_emika_panda/",
|
|
207
|
+
sceneFile: "scene.xml",
|
|
187
208
|
};
|
|
188
209
|
|
|
189
210
|
// Any URL
|
|
190
211
|
const custom: SceneConfig = {
|
|
191
|
-
src:
|
|
192
|
-
sceneFile:
|
|
212
|
+
src: "http://localhost:3000/models/my_model/",
|
|
213
|
+
sceneFile: "model.xml",
|
|
193
214
|
};
|
|
194
215
|
```
|
|
195
216
|
|
|
@@ -198,7 +219,7 @@ const custom: SceneConfig = {
|
|
|
198
219
|
```ts
|
|
199
220
|
interface SceneConfig {
|
|
200
221
|
src: string; // Base URL for model files
|
|
201
|
-
sceneFile: string; // Entry XML file, e.g.
|
|
222
|
+
sceneFile: string; // Entry XML file, e.g. "scene.xml"
|
|
202
223
|
sceneObjects?: SceneObject[]; // Objects injected into scene XML at load time
|
|
203
224
|
homeJoints?: number[]; // Initial joint positions
|
|
204
225
|
xmlPatches?: XmlPatch[]; // Patches applied to XML files during loading
|
|
@@ -210,12 +231,12 @@ interface SceneConfig {
|
|
|
210
231
|
|
|
211
232
|
```tsx
|
|
212
233
|
const config: SceneConfig = {
|
|
213
|
-
src:
|
|
214
|
-
sceneFile:
|
|
234
|
+
src: "https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/franka_emika_panda/",
|
|
235
|
+
sceneFile: "scene.xml",
|
|
215
236
|
sceneObjects: [
|
|
216
|
-
{ name:
|
|
237
|
+
{ name: "ball", type: "sphere", size: [0.03, 0.03, 0.03],
|
|
217
238
|
position: [0.5, 0, 0.1], rgba: [1, 0, 0, 1], mass: 0.1, freejoint: true },
|
|
218
|
-
{ name:
|
|
239
|
+
{ name: "platform", type: "box", size: [0.2, 0.2, 0.01],
|
|
219
240
|
position: [0.4, 0.3, 0], rgba: [0.5, 0.5, 0.5, 1] },
|
|
220
241
|
],
|
|
221
242
|
};
|
|
@@ -225,10 +246,10 @@ const config: SceneConfig = {
|
|
|
225
246
|
|
|
226
247
|
```tsx
|
|
227
248
|
xmlPatches: [{
|
|
228
|
-
target:
|
|
229
|
-
replace: [
|
|
230
|
-
inject:
|
|
231
|
-
injectAfter:
|
|
249
|
+
target: "panda.xml",
|
|
250
|
+
replace: ["name=\"actuator8\"", "name=\"gripper\""],
|
|
251
|
+
inject: "<site name=\"tcp\" pos=\"0 0 0.1\" size=\"0.01\"/>",
|
|
252
|
+
injectAfter: "<body name=\"hand\"",
|
|
232
253
|
}]
|
|
233
254
|
```
|
|
234
255
|
|
|
@@ -352,7 +373,7 @@ InstancedMesh showing MuJoCo contact points for debugging.
|
|
|
352
373
|
|------|------|---------|-------------|
|
|
353
374
|
| `maxContacts` | `number?` | `100` | Max contacts to display |
|
|
354
375
|
| `radius` | `number?` | `0.005` | Marker sphere radius |
|
|
355
|
-
| `color` | `string?` | `
|
|
376
|
+
| `color` | `string?` | `"#4f46e5"` | Marker color |
|
|
356
377
|
| `visible` | `boolean?` | `true` | Toggle visibility |
|
|
357
378
|
|
|
358
379
|
### `<SceneLights />`
|
|
@@ -372,10 +393,10 @@ Visualization overlays:
|
|
|
372
393
|
| `showCOM` | `boolean?` | `false` | Center of mass markers |
|
|
373
394
|
| `showInertia` | `boolean?` | `false` | Inertia ellipsoids |
|
|
374
395
|
| `showTendons` | `boolean?` | `false` | Tendon paths |
|
|
375
|
-
| `geomColor` | `string?` | `
|
|
376
|
-
| `siteColor` | `string?` | `
|
|
377
|
-
| `contactColor` | `string?` | `
|
|
378
|
-
| `comColor` | `string?` | `
|
|
396
|
+
| `geomColor` | `string?` | `"#00ff00"` | Color for wireframe geoms |
|
|
397
|
+
| `siteColor` | `string?` | `"#ff00ff"` | Color for site markers |
|
|
398
|
+
| `contactColor` | `string?` | `"#ff4444"` | Color for contact force arrows |
|
|
399
|
+
| `comColor` | `string?` | `"#ff0000"` | Color for COM markers |
|
|
379
400
|
|
|
380
401
|
### `<TendonRenderer />`
|
|
381
402
|
|
|
@@ -392,8 +413,8 @@ Component wrapper for contact events:
|
|
|
392
413
|
```tsx
|
|
393
414
|
<ContactListener
|
|
394
415
|
body="block_1"
|
|
395
|
-
onContactEnter={(info) => console.log(
|
|
396
|
-
onContactExit={(info) => console.log(
|
|
416
|
+
onContactEnter={(info) => console.log("contact!", info)}
|
|
417
|
+
onContactExit={(info) => console.log("released", info)}
|
|
397
418
|
/>
|
|
398
419
|
```
|
|
399
420
|
|
|
@@ -419,12 +440,12 @@ if (sim.isReady) {
|
|
|
419
440
|
Access the raw WASM module lifecycle from any child of `<MujocoProvider>`. Most users won't need this — `useMujoco()` and hooks like `useBeforePhysicsStep` handle the model/data lifecycle for you.
|
|
420
441
|
|
|
421
442
|
```tsx
|
|
422
|
-
import { useMujocoWasm } from
|
|
443
|
+
import { useMujocoWasm } from "mujoco-react";
|
|
423
444
|
|
|
424
445
|
const { mujoco, status } = useMujocoWasm();
|
|
425
446
|
|
|
426
447
|
if (mujoco) {
|
|
427
|
-
const model = mujoco.MjModel.loadFromXML(
|
|
448
|
+
const model = mujoco.MjModel.loadFromXML("/path/to/scene.xml");
|
|
428
449
|
const data = new mujoco.MjData(model);
|
|
429
450
|
mujoco.mj_step(model, data);
|
|
430
451
|
console.log(data.qpos); // joint positions after one step
|
|
@@ -466,10 +487,11 @@ await moveCameraTo(
|
|
|
466
487
|
|
|
467
488
|
### `useSensor(name)` / `useSensors()`
|
|
468
489
|
|
|
469
|
-
Read sensor values by name (
|
|
490
|
+
Read sensor values by name. Returns a `SensorHandle` with `read()`, `dim`, and `name`:
|
|
470
491
|
|
|
471
492
|
```tsx
|
|
472
|
-
const
|
|
493
|
+
const force = useSensor("force_sensor_1");
|
|
494
|
+
// force.read() -> Float64Array, force.dim -> number
|
|
473
495
|
```
|
|
474
496
|
|
|
475
497
|
### `useBodyState(name)`
|
|
@@ -477,7 +499,7 @@ const { value, size, type } = useSensor('force_sensor_1');
|
|
|
477
499
|
Position, quaternion, linear/angular velocity of a body (ref-based):
|
|
478
500
|
|
|
479
501
|
```tsx
|
|
480
|
-
const { position, quaternion, linearVelocity, angularVelocity } = useBodyState(
|
|
502
|
+
const { position, quaternion, linearVelocity, angularVelocity } = useBodyState("block_1");
|
|
481
503
|
```
|
|
482
504
|
|
|
483
505
|
### `useJointState(name)`
|
|
@@ -485,15 +507,16 @@ const { position, quaternion, linearVelocity, angularVelocity } = useBodyState('
|
|
|
485
507
|
Joint position and velocity:
|
|
486
508
|
|
|
487
509
|
```tsx
|
|
488
|
-
const { position, velocity } = useJointState(
|
|
510
|
+
const { position, velocity } = useJointState("joint1");
|
|
489
511
|
```
|
|
490
512
|
|
|
491
513
|
### `useCtrl(name)`
|
|
492
514
|
|
|
493
|
-
Read/write actuator control by name
|
|
515
|
+
Read/write actuator control by name. Returns a `CtrlHandle` with `read()`, `write()`, `name`, and `range`:
|
|
494
516
|
|
|
495
517
|
```tsx
|
|
496
|
-
const
|
|
518
|
+
const gripper = useCtrl("gripper");
|
|
519
|
+
// gripper.read() -> number, gripper.write(0.04), gripper.range -> [min, max]
|
|
497
520
|
```
|
|
498
521
|
|
|
499
522
|
### `useContacts(bodyName?)` / `useContactEvents(bodyName, handlers)`
|
|
@@ -501,9 +524,9 @@ const [value, setValue] = useCtrl('gripper');
|
|
|
501
524
|
Query contacts or subscribe to enter/exit events:
|
|
502
525
|
|
|
503
526
|
```tsx
|
|
504
|
-
useContactEvents(
|
|
505
|
-
onEnter: (info) => console.log(
|
|
506
|
-
onExit: (info) => console.log(
|
|
527
|
+
useContactEvents("block_1", {
|
|
528
|
+
onEnter: (info) => console.log("contact!", info),
|
|
529
|
+
onExit: (info) => console.log("released", info),
|
|
507
530
|
});
|
|
508
531
|
```
|
|
509
532
|
|
|
@@ -514,9 +537,9 @@ Map keyboard keys to actuators:
|
|
|
514
537
|
```tsx
|
|
515
538
|
useKeyboardTeleop({
|
|
516
539
|
bindings: {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
540
|
+
"w": { actuator: "forward", delta: 0.1 },
|
|
541
|
+
"s": { actuator: "forward", delta: -0.1 },
|
|
542
|
+
"v": { actuator: "gripper", toggle: [0, 0.04] },
|
|
520
543
|
},
|
|
521
544
|
});
|
|
522
545
|
```
|
|
@@ -527,8 +550,8 @@ Map gamepad axes/buttons to actuators:
|
|
|
527
550
|
|
|
528
551
|
```tsx
|
|
529
552
|
useGamepad({
|
|
530
|
-
axes: { 0:
|
|
531
|
-
buttons: { 0:
|
|
553
|
+
axes: { 0: "joint1", 1: "joint2" },
|
|
554
|
+
buttons: { 0: "gripper" },
|
|
532
555
|
deadzone: 0.1,
|
|
533
556
|
});
|
|
534
557
|
```
|
|
@@ -550,11 +573,23 @@ const { step, isRunning } = usePolicy({
|
|
|
550
573
|
Record and play back simulation trajectories:
|
|
551
574
|
|
|
552
575
|
```tsx
|
|
553
|
-
|
|
554
|
-
|
|
576
|
+
// Record
|
|
577
|
+
const recorder = useTrajectoryRecorder({ fields: ["qpos", "ctrl"] });
|
|
578
|
+
recorder.start();
|
|
579
|
+
// ... interact with simulation ...
|
|
580
|
+
recorder.stop();
|
|
555
581
|
|
|
556
|
-
|
|
557
|
-
|
|
582
|
+
// Play back recorded frames directly (no conversion needed)
|
|
583
|
+
const player = useTrajectoryPlayer(recorder.frames, {
|
|
584
|
+
fps: 30,
|
|
585
|
+
speed: 1.0, // 0.5x, 1x, 2x, etc.
|
|
586
|
+
loop: true,
|
|
587
|
+
mode: "kinematic", // or "physics" to replay ctrl through the sim
|
|
588
|
+
onComplete: () => console.log("done"),
|
|
589
|
+
});
|
|
590
|
+
// player.play(), player.pause(), player.seek(42), player.setSpeed(2)
|
|
591
|
+
// player.state → "idle" | "playing" | "paused" | "completed"
|
|
592
|
+
// player.progress → 0-1
|
|
558
593
|
```
|
|
559
594
|
|
|
560
595
|
### `useVideoRecorder(config)`
|
|
@@ -562,7 +597,7 @@ const player = useTrajectoryPlayer(trajectory, { fps: 30, loop: true });
|
|
|
562
597
|
Record the canvas as video:
|
|
563
598
|
|
|
564
599
|
```tsx
|
|
565
|
-
const video = useVideoRecorder({ fps: 30, mimeType:
|
|
600
|
+
const video = useVideoRecorder({ fps: 30, mimeType: "video/webm" });
|
|
566
601
|
// video.start(), video.stop() -> returns Blob
|
|
567
602
|
```
|
|
568
603
|
|
|
@@ -601,7 +636,7 @@ const meshes = useBodyMeshes(selectedBodyId);
|
|
|
601
636
|
Convenience wrapper around `useBodyMeshes` that applies an emissive highlight:
|
|
602
637
|
|
|
603
638
|
```tsx
|
|
604
|
-
useSelectionHighlight(selectedBodyId, { color:
|
|
639
|
+
useSelectionHighlight(selectedBodyId, { color: "#00ff00", emissiveIntensity: 0.5 });
|
|
605
640
|
```
|
|
606
641
|
|
|
607
642
|
### `useSceneLights(intensity?)`
|
|
@@ -732,6 +767,7 @@ Features planned but not yet implemented:
|
|
|
732
767
|
| **Physics interpolation** | P1 | Smooth rendering between physics ticks for very high refresh displays |
|
|
733
768
|
| **Instanced geom rendering** | P2 | `<InstancedGeomRenderer />` for particle/granular sims |
|
|
734
769
|
| **Web Worker physics** | P2 | Run `mj_step` off main thread via SharedArrayBuffer |
|
|
770
|
+
| **Register codegen** | P2 | CLI to auto-generate `Register` type augmentation from MJCF XML |
|
|
735
771
|
|
|
736
772
|
### WASM Limitations (mujoco-js 0.0.7)
|
|
737
773
|
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,45 @@ import * as THREE from 'three';
|
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Module augmentation interface for type-safe resource names.
|
|
14
|
+
*
|
|
15
|
+
* Declare your model's resource names via module augmentation:
|
|
16
|
+
* ```ts
|
|
17
|
+
* declare module 'mujoco-react' {
|
|
18
|
+
* interface Register {
|
|
19
|
+
* actuators: 'joint1' | 'joint2' | 'gripper';
|
|
20
|
+
* sensors: 'force_sensor' | 'torque_sensor';
|
|
21
|
+
* bodies: 'link0' | 'link1' | 'hand';
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* When no augmentation is declared, all names fall back to `string`.
|
|
27
|
+
*/
|
|
28
|
+
interface Register {
|
|
29
|
+
}
|
|
30
|
+
type Actuators = Register extends {
|
|
31
|
+
actuators: infer T extends string;
|
|
32
|
+
} ? T : string;
|
|
33
|
+
type Sensors = Register extends {
|
|
34
|
+
sensors: infer T extends string;
|
|
35
|
+
} ? T : string;
|
|
36
|
+
type Bodies = Register extends {
|
|
37
|
+
bodies: infer T extends string;
|
|
38
|
+
} ? T : string;
|
|
39
|
+
type Joints = Register extends {
|
|
40
|
+
joints: infer T extends string;
|
|
41
|
+
} ? T : string;
|
|
42
|
+
type Sites = Register extends {
|
|
43
|
+
sites: infer T extends string;
|
|
44
|
+
} ? T : string;
|
|
45
|
+
type Geoms = Register extends {
|
|
46
|
+
geoms: infer T extends string;
|
|
47
|
+
} ? T : string;
|
|
48
|
+
type Keyframes = Register extends {
|
|
49
|
+
keyframes: infer T extends string;
|
|
50
|
+
} ? T : string;
|
|
12
51
|
/**
|
|
13
52
|
* A single MuJoCo contact from the WASM module.
|
|
14
53
|
* Accessed via `data.contact.get(i)`.
|
|
@@ -245,7 +284,7 @@ interface SceneConfig {
|
|
|
245
284
|
}
|
|
246
285
|
interface IkConfig {
|
|
247
286
|
/** MuJoCo site name for IK target. */
|
|
248
|
-
siteName:
|
|
287
|
+
siteName: Sites;
|
|
249
288
|
/** Number of joints to solve for. */
|
|
250
289
|
numJoints: number;
|
|
251
290
|
/** Custom IK solver. When omitted, uses built-in Damped Least-Squares solver. */
|
|
@@ -364,8 +403,9 @@ interface TrajectoryData {
|
|
|
364
403
|
frames: TrajectoryFrame[];
|
|
365
404
|
fps: number;
|
|
366
405
|
}
|
|
406
|
+
type PlaybackState = 'idle' | 'playing' | 'paused' | 'completed';
|
|
367
407
|
interface KeyBinding {
|
|
368
|
-
actuator:
|
|
408
|
+
actuator: Actuators;
|
|
369
409
|
delta?: number;
|
|
370
410
|
toggle?: [number, number];
|
|
371
411
|
set?: number;
|
|
@@ -402,20 +442,25 @@ interface SceneLightsProps {
|
|
|
402
442
|
/** Override intensity for all MJCF lights. Default: 1.0. */
|
|
403
443
|
intensity?: number;
|
|
404
444
|
}
|
|
445
|
+
type TrajectoryInput = TrajectoryFrame[] | number[][];
|
|
405
446
|
interface TrajectoryPlayerProps {
|
|
406
|
-
trajectory:
|
|
447
|
+
trajectory: TrajectoryInput;
|
|
407
448
|
fps?: number;
|
|
449
|
+
speed?: number;
|
|
408
450
|
loop?: boolean;
|
|
409
451
|
playing?: boolean;
|
|
452
|
+
mode?: 'kinematic' | 'physics';
|
|
410
453
|
onFrame?: (frameIdx: number) => void;
|
|
454
|
+
onComplete?: () => void;
|
|
455
|
+
onStateChange?: (state: PlaybackState) => void;
|
|
411
456
|
}
|
|
412
457
|
interface ContactListenerProps {
|
|
413
|
-
body:
|
|
458
|
+
body: Bodies;
|
|
414
459
|
onContactEnter?: (info: ContactInfo) => void;
|
|
415
460
|
onContactExit?: (info: ContactInfo) => void;
|
|
416
461
|
}
|
|
417
462
|
interface BodyProps {
|
|
418
|
-
name:
|
|
463
|
+
name: Bodies;
|
|
419
464
|
type: 'box' | 'sphere' | 'cylinder';
|
|
420
465
|
size: [number, number, number];
|
|
421
466
|
position?: [number, number, number];
|
|
@@ -438,20 +483,20 @@ interface MujocoSimAPI {
|
|
|
438
483
|
step(n?: number): void;
|
|
439
484
|
getTime(): number;
|
|
440
485
|
getTimestep(): number;
|
|
441
|
-
applyKeyframe(nameOrIndex:
|
|
486
|
+
applyKeyframe(nameOrIndex: Keyframes | number): void;
|
|
442
487
|
saveState(): StateSnapshot;
|
|
443
488
|
restoreState(snapshot: StateSnapshot): void;
|
|
444
489
|
setQpos(values: Float64Array | number[]): void;
|
|
445
490
|
setQvel(values: Float64Array | number[]): void;
|
|
446
491
|
getQpos(): Float64Array;
|
|
447
492
|
getQvel(): Float64Array;
|
|
448
|
-
setCtrl(nameOrValues:
|
|
493
|
+
setCtrl(nameOrValues: Actuators | Record<Actuators, number>, value?: number): void;
|
|
449
494
|
getCtrl(): Float64Array;
|
|
450
|
-
applyForce(bodyName:
|
|
451
|
-
applyTorque(bodyName:
|
|
452
|
-
setExternalForce(bodyName:
|
|
495
|
+
applyForce(bodyName: Bodies, force: THREE.Vector3, point?: THREE.Vector3): void;
|
|
496
|
+
applyTorque(bodyName: Bodies, torque: THREE.Vector3): void;
|
|
497
|
+
setExternalForce(bodyName: Bodies, force: THREE.Vector3, torque: THREE.Vector3): void;
|
|
453
498
|
applyGeneralizedForce(values: Float64Array | number[]): void;
|
|
454
|
-
getSensorData(name:
|
|
499
|
+
getSensorData(name: Sensors): Float64Array | null;
|
|
455
500
|
getContacts(): ContactInfo[];
|
|
456
501
|
getBodies(): BodyInfo[];
|
|
457
502
|
getJoints(): JointInfo[];
|
|
@@ -472,9 +517,9 @@ interface MujocoSimAPI {
|
|
|
472
517
|
bodyId: number;
|
|
473
518
|
geomId: number;
|
|
474
519
|
} | null;
|
|
475
|
-
setBodyMass(name:
|
|
476
|
-
setGeomFriction(name:
|
|
477
|
-
setGeomSize(name:
|
|
520
|
+
setBodyMass(name: Bodies, mass: number): void;
|
|
521
|
+
setGeomFriction(name: Geoms, friction: [number, number, number]): void;
|
|
522
|
+
setGeomSize(name: Geoms, size: [number, number, number]): void;
|
|
478
523
|
readonly mjModelRef: React__default.RefObject<MujocoModel | null>;
|
|
479
524
|
readonly mjDataRef: React__default.RefObject<MujocoData | null>;
|
|
480
525
|
}
|
|
@@ -499,10 +544,29 @@ interface MujocoContextValue {
|
|
|
499
544
|
status: 'loading' | 'ready' | 'error';
|
|
500
545
|
error: string | null;
|
|
501
546
|
}
|
|
547
|
+
/** @deprecated Use `SensorHandle` instead. */
|
|
502
548
|
interface SensorResult {
|
|
503
549
|
value: React__default.RefObject<Float64Array>;
|
|
504
550
|
size: number;
|
|
505
551
|
}
|
|
552
|
+
interface CtrlHandle {
|
|
553
|
+
/** Read the current ctrl value. */
|
|
554
|
+
read(): number;
|
|
555
|
+
/** Write a ctrl value (goes directly to data.ctrl). */
|
|
556
|
+
write(value: number): void;
|
|
557
|
+
/** Actuator name. */
|
|
558
|
+
name: Actuators;
|
|
559
|
+
/** Actuator control range [min, max]. */
|
|
560
|
+
range: [number, number];
|
|
561
|
+
}
|
|
562
|
+
interface SensorHandle {
|
|
563
|
+
/** Read the current sensor data. */
|
|
564
|
+
read(): Float64Array;
|
|
565
|
+
/** Sensor dimensionality. */
|
|
566
|
+
dim: number;
|
|
567
|
+
/** Sensor name. */
|
|
568
|
+
name: Sensors;
|
|
569
|
+
}
|
|
506
570
|
interface BodyStateResult {
|
|
507
571
|
position: React__default.RefObject<THREE.Vector3>;
|
|
508
572
|
quaternion: React__default.RefObject<THREE.Quaternion>;
|
|
@@ -840,7 +904,7 @@ declare function ContactListener({ body, onContactEnter, onContactExit, }: Conta
|
|
|
840
904
|
* Component wrapper for useTrajectoryPlayer.
|
|
841
905
|
* Provides declarative trajectory playback controlled via props.
|
|
842
906
|
*/
|
|
843
|
-
declare function TrajectoryPlayer({ trajectory, fps, loop, playing, onFrame, }: TrajectoryPlayerProps): null;
|
|
907
|
+
declare function TrajectoryPlayer({ trajectory, fps, speed, loop, playing, mode, onFrame, onComplete, onStateChange, }: TrajectoryPlayerProps): null;
|
|
844
908
|
|
|
845
909
|
/**
|
|
846
910
|
* @license
|
|
@@ -862,7 +926,7 @@ declare function useActuators(): ActuatorInfo[];
|
|
|
862
926
|
* Returns reactive refs for a MuJoCo site's world position and orientation.
|
|
863
927
|
* Refs are updated every frame without triggering React re-renders.
|
|
864
928
|
*/
|
|
865
|
-
declare function useSitePosition(siteName:
|
|
929
|
+
declare function useSitePosition(siteName: Sites): SitePositionResult;
|
|
866
930
|
|
|
867
931
|
/**
|
|
868
932
|
* @license
|
|
@@ -885,10 +949,11 @@ declare function useGravityCompensation(enabled?: boolean): void;
|
|
|
885
949
|
*/
|
|
886
950
|
|
|
887
951
|
/**
|
|
888
|
-
* Access a single MuJoCo sensor by name. Returns a
|
|
889
|
-
*
|
|
952
|
+
* Access a single MuJoCo sensor by name. Returns a `SensorHandle` with
|
|
953
|
+
* `read()`, `dim`, and `name`. The backing array is updated every physics
|
|
954
|
+
* frame without causing React re-renders.
|
|
890
955
|
*/
|
|
891
|
-
declare function useSensor(name:
|
|
956
|
+
declare function useSensor(name: Sensors): SensorHandle;
|
|
892
957
|
/**
|
|
893
958
|
* Enumerate all sensors in the loaded MuJoCo model.
|
|
894
959
|
* Returns a stable array recomputed only when the model changes.
|
|
@@ -910,7 +975,7 @@ declare function useSensors(): SensorInfo[];
|
|
|
910
975
|
* For ball joints, position is quat (4), velocity is angular vel (3).
|
|
911
976
|
* For free joints, position is pos+quat (7), velocity is lin+ang vel (6).
|
|
912
977
|
*/
|
|
913
|
-
declare function useJointState(name:
|
|
978
|
+
declare function useJointState(name: Joints): JointStateResult;
|
|
914
979
|
|
|
915
980
|
/**
|
|
916
981
|
* @license
|
|
@@ -923,22 +988,22 @@ declare function useJointState(name: string): JointStateResult;
|
|
|
923
988
|
* Track a MuJoCo body's world position, quaternion, and velocities.
|
|
924
989
|
* All values are ref-based — updated every physics frame without re-renders.
|
|
925
990
|
*/
|
|
926
|
-
declare function useBodyState(name:
|
|
991
|
+
declare function useBodyState(name: Bodies): BodyStateResult;
|
|
927
992
|
|
|
928
993
|
/**
|
|
929
994
|
* @license
|
|
930
995
|
* SPDX-License-Identifier: Apache-2.0
|
|
931
996
|
*
|
|
932
|
-
* useCtrl —
|
|
997
|
+
* useCtrl — handle-based read/write access to a named actuator's ctrl value (spec 3.1)
|
|
933
998
|
*/
|
|
999
|
+
|
|
934
1000
|
/**
|
|
935
1001
|
* Access a single actuator's control value by name.
|
|
936
1002
|
*
|
|
937
|
-
* Returns
|
|
938
|
-
*
|
|
939
|
-
* - `setValue` writes directly to `data.ctrl[actuatorId]`.
|
|
1003
|
+
* Returns a `CtrlHandle` with `read()` and `write()` methods that
|
|
1004
|
+
* operate directly on `data.ctrl` without causing React re-renders.
|
|
940
1005
|
*/
|
|
941
|
-
declare function useCtrl(name:
|
|
1006
|
+
declare function useCtrl(name: Actuators): CtrlHandle;
|
|
942
1007
|
|
|
943
1008
|
/**
|
|
944
1009
|
* @license
|
|
@@ -953,13 +1018,13 @@ declare function useCtrl(name: string): [React.RefObject<number>, (value: number
|
|
|
953
1018
|
* Calls the callback every physics frame with current contact list.
|
|
954
1019
|
* Reads `data.ncon` first to avoid allocating for zero contacts.
|
|
955
1020
|
*/
|
|
956
|
-
declare function useContacts(bodyName?:
|
|
1021
|
+
declare function useContacts(bodyName?: Bodies, callback?: (contacts: ContactInfo[]) => void): React.RefObject<ContactInfo[]>;
|
|
957
1022
|
/**
|
|
958
1023
|
* Contact enter/exit events for a specific body (spec 2.5).
|
|
959
1024
|
* Tracks which geom pairs are in contact frame-to-frame and fires
|
|
960
1025
|
* onEnter/onExit callbacks on transitions.
|
|
961
1026
|
*/
|
|
962
|
-
declare function useContactEvents(bodyName:
|
|
1027
|
+
declare function useContactEvents(bodyName: Bodies, handlers: {
|
|
963
1028
|
onEnter?: (info: ContactInfo) => void;
|
|
964
1029
|
onExit?: (info: ContactInfo) => void;
|
|
965
1030
|
}): void;
|
|
@@ -1011,24 +1076,38 @@ declare function usePolicy(config: PolicyConfig): {
|
|
|
1011
1076
|
*
|
|
1012
1077
|
* useTrajectoryPlayer — trajectory playback/scrubbing (spec 13.2)
|
|
1013
1078
|
*/
|
|
1079
|
+
|
|
1014
1080
|
interface TrajectoryPlayerOptions {
|
|
1015
1081
|
fps?: number;
|
|
1082
|
+
speed?: number;
|
|
1016
1083
|
loop?: boolean;
|
|
1084
|
+
mode?: 'kinematic' | 'physics';
|
|
1085
|
+
onComplete?: () => void;
|
|
1086
|
+
onStateChange?: (state: PlaybackState) => void;
|
|
1017
1087
|
}
|
|
1018
1088
|
/**
|
|
1019
|
-
* Play back a
|
|
1089
|
+
* Play back a trajectory, overriding simulation state.
|
|
1090
|
+
*
|
|
1091
|
+
* Accepts either `TrajectoryFrame[]` (from useTrajectoryRecorder) or
|
|
1092
|
+
* `number[][]` (raw qpos arrays).
|
|
1093
|
+
*
|
|
1094
|
+
* In `kinematic` mode (default), the simulation is paused and qpos is
|
|
1095
|
+
* set directly each frame with mj_forward for rendering.
|
|
1020
1096
|
*
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1097
|
+
* In `physics` mode, the simulation keeps running and ctrl values from
|
|
1098
|
+
* the trajectory are applied each physics step via useBeforePhysicsStep.
|
|
1023
1099
|
*/
|
|
1024
|
-
declare function useTrajectoryPlayer(trajectory:
|
|
1100
|
+
declare function useTrajectoryPlayer(trajectory: TrajectoryInput, options?: TrajectoryPlayerOptions): {
|
|
1025
1101
|
play: () => void;
|
|
1026
1102
|
pause: () => void;
|
|
1027
1103
|
seek: (frameIdx: number) => void;
|
|
1028
1104
|
reset: () => void;
|
|
1105
|
+
setSpeed: (s: number) => void;
|
|
1106
|
+
readonly state: PlaybackState;
|
|
1029
1107
|
readonly frame: number;
|
|
1030
1108
|
readonly playing: boolean;
|
|
1031
1109
|
readonly totalFrames: number;
|
|
1110
|
+
readonly progress: number;
|
|
1032
1111
|
};
|
|
1033
1112
|
|
|
1034
1113
|
/**
|
|
@@ -1195,4 +1274,4 @@ interface CameraAnimationAPI {
|
|
|
1195
1274
|
*/
|
|
1196
1275
|
declare function useCameraAnimation(): CameraAnimationAPI;
|
|
1197
1276
|
|
|
1198
|
-
export { type ActuatorInfo, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControllerComponent, type ControllerOptions, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, type JointInfo, type JointStateResult, type KeyBinding, type KeyboardTeleopConfig, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoSimAPI, MujocoSimProvider, type PhysicsConfig, type PhysicsStepCallback, type PolicyConfig, type RayHit, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorInfo, type SensorResult, type SiteInfo, type SitePositionResult, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, createController, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getContact, getName, loadScene, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
1277
|
+
export { type ActuatorInfo, type Actuators, type Bodies, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControllerComponent, type ControllerOptions, type CtrlHandle, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type Geoms, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, type JointInfo, type JointStateResult, type Joints, type KeyBinding, type KeyboardTeleopConfig, type Keyframes, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoSimAPI, MujocoSimProvider, type PhysicsConfig, type PhysicsStepCallback, type PlaybackState, type PolicyConfig, type RayHit, type Register, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorHandle, type SensorInfo, type SensorResult, type Sensors, type SiteInfo, type SitePositionResult, type Sites, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, type TrajectoryInput, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, createController, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getContact, getName, loadScene, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|