mujoco-react 9.5.0 → 10.0.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 CHANGED
@@ -4,8 +4,6 @@
4
4
 
5
5
  # mujoco-react
6
6
 
7
- > **Beta** — This library is under active development. The API may change between minor versions until 10.0.
8
-
9
7
  Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper around the official [@mujoco/mujoco](https://www.npmjs.com/package/@mujoco/mujoco) WASM bindings. Load any MuJoCo model, step physics, render bodies, and write controllers as React components.
10
8
 
11
9
  [![npm](https://img.shields.io/npm/v/mujoco-react)](https://www.npmjs.com/package/mujoco-react)
@@ -20,7 +18,7 @@ npm install mujoco-react three @react-three/fiber @react-three/drei
20
18
 
21
19
  ## Vite Plugin and Type-Safe Names
22
20
 
23
- Use the Vite plugin to generate TanStack-style declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
21
+ Use the Vite plugin to generate declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
24
22
 
25
23
  ```ts
26
24
  // vite.config.ts
@@ -44,9 +42,9 @@ The plugin writes `src/mujoco-register.gen.ts` during dev and build. Commit that
44
42
  // src/mujoco-register.gen.ts
45
43
  // Auto-generated by mujoco-react. Do not edit.
46
44
 
47
- import { registerRobotResources } from "mujoco-react";
45
+ import { registerModelResources } from "mujoco-react";
48
46
 
49
- const generatedRobotResources = {
47
+ const generatedModelResources = {
50
48
  franka: {
51
49
  actuators: { joint1: "joint1", joint2: "joint2", joint3: "joint3", gripper: "gripper" },
52
50
  sensors: { force_sensor: "force_sensor", torque_sensor: "torque_sensor" },
@@ -58,11 +56,11 @@ const generatedRobotResources = {
58
56
  },
59
57
  };
60
58
 
61
- registerRobotResources(generatedRobotResources);
59
+ registerModelResources(generatedModelResources);
62
60
 
63
61
  declare module "mujoco-react" {
64
62
  interface Register {
65
- robots: {
63
+ models: {
66
64
  franka: {
67
65
  actuators: "joint1" | "joint2" | "joint3" | "gripper";
68
66
  sensors: "force_sensor" | "torque_sensor";
@@ -80,7 +78,7 @@ declare module "mujoco-react" {
80
78
  }
81
79
  ```
82
80
 
83
- Once generated, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` accept the global union. For robot-scoped code, use package exports such as `RobotActuators.franka.gripper`, `RobotSites.franka.tcp`, and `RobotBodies.franka.hand`. Generic type helpers like `RobotActuators<"franka">` are still available for reusable library code. When no `Register` augmentation is present, names fall back to `string`.
81
+ Once generated, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` accept the global union. For model-scoped code, use package exports such as `ModelActuators.franka.gripper`, `ModelSites.franka.tcp`, and `ModelBodies.franka.hand`. Generic type helpers like `ModelActuators<"franka">` are still available for reusable library code. When no `Register` augmentation is present, names fall back to `string`.
84
82
 
85
83
  Non-Vite projects can generate the same file with:
86
84
 
@@ -117,10 +115,10 @@ export function App() {
117
115
 
118
116
  ```tsx
119
117
  import { OrbitControls } from "@react-three/drei";
120
- import { IkGizmo, RobotSites, useIkController } from "mujoco-react";
118
+ import { IkGizmo, ModelSites, useIkController } from "mujoco-react";
121
119
 
122
120
  function PandaTools() {
123
- const ik = useIkController({ siteName: RobotSites.franka.tcp });
121
+ const ik = useIkController({ siteName: ModelSites.franka.tcp });
124
122
 
125
123
  return (
126
124
  <>
@@ -311,10 +309,10 @@ Tune live rendering and snapshots separately with `renderTuning` and
311
309
  ## Write Controllers
312
310
 
313
311
  ```tsx
314
- import { RobotActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
312
+ import { ModelActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
315
313
 
316
314
  function MyController() {
317
- const shoulder = useCtrl(RobotActuators.franka.actuator1);
315
+ const shoulder = useCtrl(ModelActuators.franka.actuator1);
318
316
 
319
317
  useBeforePhysicsStep(({ data }) => {
320
318
  shoulder.write(Math.sin(data.time));
@@ -329,29 +327,29 @@ Controllers are just React children that read sensors, write named controls, app
329
327
  With generated resource values, reusable controllers can be scoped to one robot without hand-typing names:
330
328
 
331
329
  ```tsx
332
- import { RobotActuators, RobotJoints, RobotSites, useCtrl, useIkController } from "mujoco-react";
330
+ import { ModelActuators, ModelJoints, ModelSites, useCtrl, useIkController } from "mujoco-react";
333
331
 
334
332
  function FrankaTypedControls() {
335
- const gripper = useCtrl(RobotActuators.franka.gripper);
333
+ const gripper = useCtrl(ModelActuators.franka.gripper);
336
334
  const ik = useIkController({
337
- siteName: RobotSites.franka.tcp,
335
+ siteName: ModelSites.franka.tcp,
338
336
  joints: [
339
- RobotJoints.franka.joint1,
340
- RobotJoints.franka.joint2,
341
- RobotJoints.franka.joint3,
342
- RobotJoints.franka.joint4,
343
- RobotJoints.franka.joint5,
344
- RobotJoints.franka.joint6,
345
- RobotJoints.franka.joint7,
337
+ ModelJoints.franka.joint1,
338
+ ModelJoints.franka.joint2,
339
+ ModelJoints.franka.joint3,
340
+ ModelJoints.franka.joint4,
341
+ ModelJoints.franka.joint5,
342
+ ModelJoints.franka.joint6,
343
+ ModelJoints.franka.joint7,
346
344
  ],
347
345
  actuators: [
348
- RobotActuators.franka.actuator1,
349
- RobotActuators.franka.actuator2,
350
- RobotActuators.franka.actuator3,
351
- RobotActuators.franka.actuator4,
352
- RobotActuators.franka.actuator5,
353
- RobotActuators.franka.actuator6,
354
- RobotActuators.franka.actuator7,
346
+ ModelActuators.franka.actuator1,
347
+ ModelActuators.franka.actuator2,
348
+ ModelActuators.franka.actuator3,
349
+ ModelActuators.franka.actuator4,
350
+ ModelActuators.franka.actuator5,
351
+ ModelActuators.franka.actuator6,
352
+ ModelActuators.franka.actuator7,
355
353
  ],
356
354
  });
357
355
 
@@ -378,14 +376,14 @@ Use control groups when a robot's actuator order does not match a simple `qpos[0
378
376
 
379
377
  ```tsx
380
378
  import { useRef } from "react";
381
- import { resolveControlGroup, RobotSites, useBeforePhysicsStep } from "mujoco-react";
379
+ import { resolveControlGroup, ModelSites, useBeforePhysicsStep } from "mujoco-react";
382
380
  import type { ControlGroupInfo } from "mujoco-react";
383
381
 
384
382
  function HoldTcpPose() {
385
383
  const armRef = useRef<ControlGroupInfo | null>(null);
386
384
 
387
385
  useBeforePhysicsStep(({ model, data }) => {
388
- armRef.current ??= resolveControlGroup(model, { siteName: RobotSites.franka.tcp });
386
+ armRef.current ??= resolveControlGroup(model, { siteName: ModelSites.franka.tcp });
389
387
  if (!armRef.current) return;
390
388
 
391
389
  armRef.current.writeCtrl(data, armRef.current.readQpos(data));
@@ -489,7 +487,7 @@ For reusable controllers with typed config, default merging, and children, use t
489
487
 
490
488
  ```tsx
491
489
  import {
492
- RobotActuators,
490
+ ModelActuators,
493
491
  createController,
494
492
  useBeforePhysicsStep,
495
493
  useCtrl,
@@ -498,7 +496,7 @@ import {
498
496
  export const MyController = createController<{ gain: number }>(
499
497
  { name: "MyController", defaultConfig: { gain: 1.0 } },
500
498
  ({ config, children }) => {
501
- const shoulder = useCtrl(RobotActuators.franka.actuator1);
499
+ const shoulder = useCtrl(ModelActuators.franka.actuator1);
502
500
 
503
501
  useBeforePhysicsStep(({ data }) => {
504
502
  shoulder.write(config.gain * Math.sin(data.time));
@@ -535,14 +533,14 @@ A `createControllerHook` factory is also available for the hook equivalent — s
535
533
  The built-in `useIkController()` uses Damped Least-Squares. Pass `ikSolveFn` to swap in your own solver (analytical, learned, etc.):
536
534
 
537
535
  ```tsx
538
- import { RobotSites } from "mujoco-react";
536
+ import { ModelSites } from "mujoco-react";
539
537
  import type { IKSolveFn } from "mujoco-react";
540
538
 
541
539
  const myIK: IKSolveFn = ({ position, currentQ }) => {
542
540
  return myAnalyticalSolver(position, currentQ); // return joint angles or null
543
541
  };
544
542
 
545
- const ik = useIkController({ siteName: RobotSites.franka.tcp, ikSolveFn: myIK });
543
+ const ik = useIkController({ siteName: ModelSites.franka.tcp, ikSolveFn: myIK });
546
544
  ```
547
545
 
548
546
  ### `useIkController(config | null)`
@@ -550,9 +548,9 @@ const ik = useIkController({ siteName: RobotSites.franka.tcp, ikSolveFn: myIK })
550
548
  Hook for interactive end-effector control. Pass `null` to disable IK (safe to call unconditionally):
551
549
 
552
550
  ```tsx
553
- import { IkGizmo, RobotSites, useIkController } from "mujoco-react";
551
+ import { IkGizmo, ModelSites, useIkController } from "mujoco-react";
554
552
 
555
- const ik = useIkController({ siteName: RobotSites.franka.tcp });
553
+ const ik = useIkController({ siteName: ModelSites.franka.tcp });
556
554
  return ik ? <IkGizmo controller={ik} /> : null;
557
555
  ```
558
556
 
@@ -573,31 +571,31 @@ Pass the returned value to `<IkGizmo controller={ik} />` or to your own controll
573
571
  By default the controller infers scalar hinge/slide joints by walking from the site body toward the model root. For robots where the MJCF control layout is not a simple chain, pass explicit names:
574
572
 
575
573
  ```tsx
576
- import { RobotActuators, RobotJoints, RobotSites } from "mujoco-react";
574
+ import { ModelActuators, ModelJoints, ModelSites } from "mujoco-react";
577
575
 
578
576
  const leftArmIk = useIkController({
579
- siteName: RobotSites.franka.tcp,
577
+ siteName: ModelSites.franka.tcp,
580
578
  joints: [
581
- RobotJoints.franka.joint1,
582
- RobotJoints.franka.joint2,
583
- RobotJoints.franka.joint3,
584
- RobotJoints.franka.joint4,
585
- RobotJoints.franka.joint5,
586
- RobotJoints.franka.joint6,
587
- RobotJoints.franka.joint7,
579
+ ModelJoints.franka.joint1,
580
+ ModelJoints.franka.joint2,
581
+ ModelJoints.franka.joint3,
582
+ ModelJoints.franka.joint4,
583
+ ModelJoints.franka.joint5,
584
+ ModelJoints.franka.joint6,
585
+ ModelJoints.franka.joint7,
588
586
  ],
589
587
  });
590
588
 
591
589
  const gripperIk = useIkController({
592
- siteName: RobotSites.franka.tcp,
590
+ siteName: ModelSites.franka.tcp,
593
591
  actuators: [
594
- RobotActuators.franka.actuator1,
595
- RobotActuators.franka.actuator2,
596
- RobotActuators.franka.actuator3,
597
- RobotActuators.franka.actuator4,
598
- RobotActuators.franka.actuator5,
599
- RobotActuators.franka.actuator6,
600
- RobotActuators.franka.actuator7,
592
+ ModelActuators.franka.actuator1,
593
+ ModelActuators.franka.actuator2,
594
+ ModelActuators.franka.actuator3,
595
+ ModelActuators.franka.actuator4,
596
+ ModelActuators.franka.actuator5,
597
+ ModelActuators.franka.actuator6,
598
+ ModelActuators.franka.actuator7,
601
599
  ],
602
600
  });
603
601
  ```
@@ -934,9 +932,9 @@ if (mujoco) {
934
932
  Run logic **before** `mj_step` each frame. Write to `data.ctrl`, apply forces, drive automation.
935
933
 
936
934
  ```tsx
937
- import { RobotActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
935
+ import { ModelActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
938
936
 
939
- const shoulder = useCtrl(RobotActuators.franka.actuator1);
937
+ const shoulder = useCtrl(ModelActuators.franka.actuator1);
940
938
 
941
939
  useBeforePhysicsStep(({ data }) => {
942
940
  shoulder.write(Math.sin(data.time));
@@ -971,9 +969,9 @@ await moveCameraTo(
971
969
  Read sensor values by name. Returns a `SensorHandle` with `read()`, `dim`, and `name`:
972
970
 
973
971
  ```tsx
974
- import { RobotSensors, useSensor } from "mujoco-react";
972
+ import { ModelSensors, useSensor } from "mujoco-react";
975
973
 
976
- const imu = useSensor(RobotSensors.g1["imu-torso-angular-velocity"]);
974
+ const imu = useSensor(ModelSensors.g1["imu-torso-angular-velocity"]);
977
975
  // imu.read() -> Float64Array, imu.dim -> number
978
976
  ```
979
977
 
@@ -998,9 +996,9 @@ const { position, velocity } = useJointState("joint1");
998
996
  Read/write actuator control by name. Returns a `CtrlHandle` with `read()`, `write()`, `name`, and `range`:
999
997
 
1000
998
  ```tsx
1001
- import { RobotActuators, useCtrl } from "mujoco-react";
999
+ import { ModelActuators, useCtrl } from "mujoco-react";
1002
1000
 
1003
- const gripper = useCtrl(RobotActuators.franka.gripper);
1001
+ const gripper = useCtrl(ModelActuators.franka.gripper);
1004
1002
  // gripper.read() -> number, gripper.write(0.04), gripper.range -> [min, max]
1005
1003
  ```
1006
1004
 
@@ -1020,13 +1018,13 @@ useContactEvents("block_1", {
1020
1018
  Map keyboard keys to actuators:
1021
1019
 
1022
1020
  ```tsx
1023
- import { RobotActuators, useKeyboardTeleop } from "mujoco-react";
1021
+ import { ModelActuators, useKeyboardTeleop } from "mujoco-react";
1024
1022
 
1025
1023
  useKeyboardTeleop({
1026
1024
  bindings: {
1027
1025
  "w": { actuator: "forward", delta: 0.1 },
1028
1026
  "s": { actuator: "forward", delta: -0.1 },
1029
- "v": { actuator: RobotActuators.franka.gripper, toggle: [0, 0.04] },
1027
+ "v": { actuator: ModelActuators.franka.gripper, toggle: [0, 0.04] },
1030
1028
  },
1031
1029
  });
1032
1030
  ```
@@ -4,7 +4,7 @@ import { useMemo, useEffect } from 'react';
4
4
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
5
 
6
6
  // src/types.ts
7
- var runtimeRobotResources = {};
7
+ var runtimeModelResources = {};
8
8
  var REGISTER_RESOURCE_KEYS = ["actuators", "sensors", "bodies", "joints", "sites", "geoms", "keyframes", "cameras"];
9
9
  function createEmptyRuntimeResources() {
10
10
  return {
@@ -18,51 +18,51 @@ function createEmptyRuntimeResources() {
18
18
  cameras: {}
19
19
  };
20
20
  }
21
- function registerRobotResources(resources) {
22
- for (const [robot, robotResources] of Object.entries(resources)) {
23
- const existing = runtimeRobotResources[robot] ?? createEmptyRuntimeResources();
21
+ function registerModelResources(resources) {
22
+ for (const [model, modelResources] of Object.entries(resources)) {
23
+ const existing = runtimeModelResources[model] ?? createEmptyRuntimeResources();
24
24
  for (const key of REGISTER_RESOURCE_KEYS) {
25
- existing[key] = { ...existing[key], ...robotResources[key] ?? {} };
25
+ existing[key] = { ...existing[key], ...modelResources[key] ?? {} };
26
26
  }
27
- runtimeRobotResources[robot] = existing;
27
+ runtimeModelResources[model] = existing;
28
28
  }
29
29
  }
30
30
  function createResourceCategory(key) {
31
31
  return new Proxy({}, {
32
- get(_target, robot) {
33
- if (typeof robot !== "string") return void 0;
34
- return runtimeRobotResources[robot]?.[key] ?? {};
32
+ get(_target, model) {
33
+ if (typeof model !== "string") return void 0;
34
+ return runtimeModelResources[model]?.[key] ?? {};
35
35
  },
36
36
  ownKeys() {
37
- return Reflect.ownKeys(runtimeRobotResources);
37
+ return Reflect.ownKeys(runtimeModelResources);
38
38
  },
39
- getOwnPropertyDescriptor(_target, robot) {
40
- if (typeof robot !== "string" || !(robot in runtimeRobotResources)) return void 0;
39
+ getOwnPropertyDescriptor(_target, model) {
40
+ if (typeof model !== "string" || !(model in runtimeModelResources)) return void 0;
41
41
  return { enumerable: true, configurable: true };
42
42
  }
43
43
  });
44
44
  }
45
- var RobotResources = new Proxy(runtimeRobotResources, {
46
- get(target, robot) {
47
- if (typeof robot !== "string") return void 0;
48
- return target[robot] ?? createEmptyRuntimeResources();
45
+ var ModelResources = new Proxy(runtimeModelResources, {
46
+ get(target, model) {
47
+ if (typeof model !== "string") return void 0;
48
+ return target[model] ?? createEmptyRuntimeResources();
49
49
  },
50
50
  ownKeys(target) {
51
51
  return Reflect.ownKeys(target);
52
52
  },
53
- getOwnPropertyDescriptor(target, robot) {
54
- if (typeof robot !== "string" || !(robot in target)) return void 0;
53
+ getOwnPropertyDescriptor(target, model) {
54
+ if (typeof model !== "string" || !(model in target)) return void 0;
55
55
  return { enumerable: true, configurable: true };
56
56
  }
57
57
  });
58
- var RobotActuators = createResourceCategory("actuators");
59
- var RobotSensors = createResourceCategory("sensors");
60
- var RobotBodies = createResourceCategory("bodies");
61
- var RobotJoints = createResourceCategory("joints");
62
- var RobotSites = createResourceCategory("sites");
63
- var RobotGeoms = createResourceCategory("geoms");
64
- var RobotKeyframes = createResourceCategory("keyframes");
65
- var RobotCameras = createResourceCategory("cameras");
58
+ var ModelActuators = createResourceCategory("actuators");
59
+ var ModelSensors = createResourceCategory("sensors");
60
+ var ModelBodies = createResourceCategory("bodies");
61
+ var ModelJoints = createResourceCategory("joints");
62
+ var ModelSites = createResourceCategory("sites");
63
+ var ModelGeoms = createResourceCategory("geoms");
64
+ var ModelKeyframes = createResourceCategory("keyframes");
65
+ var ModelCameras = createResourceCategory("cameras");
66
66
  function getContact(contacts, i) {
67
67
  try {
68
68
  return contacts.get(i);
@@ -1077,6 +1077,6 @@ function clamp01(value) {
1077
1077
  * Offscreen camera-frame capture for R3F/MuJoCo scenes.
1078
1078
  */
1079
1079
 
1080
- export { CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
1081
- //# sourceMappingURL=chunk-6MOK6ZWB.js.map
1082
- //# sourceMappingURL=chunk-6MOK6ZWB.js.map
1080
+ export { CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
1081
+ //# sourceMappingURL=chunk-QTCAVQS6.js.map
1082
+ //# sourceMappingURL=chunk-QTCAVQS6.js.map