mujoco-react 7.0.1 → 8.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 +32 -9
- package/dist/index.d.ts +86 -27
- package/dist/index.js +35 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- 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/index.ts +11 -0
- package/src/types.ts +62 -13
package/package.json
CHANGED
|
@@ -9,13 +9,13 @@ import { useEffect, useRef } from 'react';
|
|
|
9
9
|
import * as THREE from 'three';
|
|
10
10
|
import { useMujocoContext, useAfterPhysicsStep } from '../core/MujocoSimProvider';
|
|
11
11
|
import { findBodyByName } from '../core/SceneLoader';
|
|
12
|
-
import type { BodyStateResult } from '../types';
|
|
12
|
+
import type { Bodies, BodyStateResult } from '../types';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Track a MuJoCo body's world position, quaternion, and velocities.
|
|
16
16
|
* All values are ref-based — updated every physics frame without re-renders.
|
|
17
17
|
*/
|
|
18
|
-
export function useBodyState(name:
|
|
18
|
+
export function useBodyState(name: Bodies): BodyStateResult {
|
|
19
19
|
const { mjModelRef, status } = useMujocoContext();
|
|
20
20
|
const bodyIdRef = useRef(-1);
|
|
21
21
|
const position = useRef(new THREE.Vector3());
|
package/src/hooks/useContacts.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { useCallback, useEffect, useRef } from 'react';
|
|
|
10
10
|
import { useMujocoContext, useAfterPhysicsStep } from '../core/MujocoSimProvider';
|
|
11
11
|
import { findBodyByName, getName } from '../core/SceneLoader';
|
|
12
12
|
import { getContact } from '../types';
|
|
13
|
-
import type { ContactInfo, MujocoModel } from '../types';
|
|
13
|
+
import type { Bodies, ContactInfo, MujocoModel } from '../types';
|
|
14
14
|
|
|
15
15
|
// Cache geom names per model to avoid cross-model id collisions.
|
|
16
16
|
const geomNameCacheByModel = new WeakMap<MujocoModel, Map<number, string>>();
|
|
@@ -36,7 +36,7 @@ function getGeomNameCached(model: MujocoModel, geomId: number): string {
|
|
|
36
36
|
* Reads `data.ncon` first to avoid allocating for zero contacts.
|
|
37
37
|
*/
|
|
38
38
|
export function useContacts(
|
|
39
|
-
bodyName?:
|
|
39
|
+
bodyName?: Bodies,
|
|
40
40
|
callback?: (contacts: ContactInfo[]) => void,
|
|
41
41
|
): React.RefObject<ContactInfo[]> {
|
|
42
42
|
const { mjModelRef, status } = useMujocoContext();
|
|
@@ -108,7 +108,7 @@ export function useContacts(
|
|
|
108
108
|
* onEnter/onExit callbacks on transitions.
|
|
109
109
|
*/
|
|
110
110
|
export function useContactEvents(
|
|
111
|
-
bodyName:
|
|
111
|
+
bodyName: Bodies,
|
|
112
112
|
handlers: {
|
|
113
113
|
onEnter?: (info: ContactInfo) => void;
|
|
114
114
|
onExit?: (info: ContactInfo) => void;
|
package/src/hooks/useCtrl.ts
CHANGED
|
@@ -2,39 +2,52 @@
|
|
|
2
2
|
* @license
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*
|
|
5
|
-
* useCtrl —
|
|
5
|
+
* useCtrl — handle-based read/write access to a named actuator's ctrl value (spec 3.1)
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { useEffect, useRef, useMemo } from 'react';
|
|
9
9
|
import { useMujocoContext } from '../core/MujocoSimProvider';
|
|
10
10
|
import { findActuatorByName } from '../core/SceneLoader';
|
|
11
|
+
import type { Actuators, CtrlHandle } from '../types';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Access a single actuator's control value by name.
|
|
14
15
|
*
|
|
15
|
-
* Returns
|
|
16
|
-
*
|
|
17
|
-
* - `setValue` writes directly to `data.ctrl[actuatorId]`.
|
|
16
|
+
* Returns a `CtrlHandle` with `read()` and `write()` methods that
|
|
17
|
+
* operate directly on `data.ctrl` without causing React re-renders.
|
|
18
18
|
*/
|
|
19
|
-
export function useCtrl(name:
|
|
19
|
+
export function useCtrl(name: Actuators): CtrlHandle {
|
|
20
20
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
21
21
|
const actuatorIdRef = useRef(-1);
|
|
22
|
-
const
|
|
22
|
+
const rangeRef = useRef<[number, number]>([0, 0]);
|
|
23
23
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
const model = mjModelRef.current;
|
|
26
26
|
if (!model || status !== 'ready') return;
|
|
27
|
-
|
|
27
|
+
const id = findActuatorByName(model, name);
|
|
28
|
+
actuatorIdRef.current = id;
|
|
29
|
+
if (id >= 0) {
|
|
30
|
+
rangeRef.current = [
|
|
31
|
+
model.actuator_ctrlrange[id * 2],
|
|
32
|
+
model.actuator_ctrlrange[id * 2 + 1],
|
|
33
|
+
];
|
|
34
|
+
}
|
|
28
35
|
}, [name, status, mjModelRef]);
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
return useMemo<CtrlHandle>(() => ({
|
|
38
|
+
read() {
|
|
39
|
+
const data = mjDataRef.current;
|
|
40
|
+
if (!data || actuatorIdRef.current < 0) return 0;
|
|
41
|
+
return data.ctrl[actuatorIdRef.current];
|
|
42
|
+
},
|
|
43
|
+
write(value: number) {
|
|
44
|
+
const data = mjDataRef.current;
|
|
45
|
+
if (!data || actuatorIdRef.current < 0) return;
|
|
46
|
+
data.ctrl[actuatorIdRef.current] = value;
|
|
47
|
+
},
|
|
48
|
+
name,
|
|
49
|
+
get range(): [number, number] {
|
|
50
|
+
return rangeRef.current;
|
|
51
|
+
},
|
|
52
|
+
}), [name, mjDataRef]);
|
|
40
53
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { useEffect, useRef } from 'react';
|
|
9
9
|
import { useMujocoContext, useAfterPhysicsStep } from '../core/MujocoSimProvider';
|
|
10
10
|
import { getName } from '../core/SceneLoader';
|
|
11
|
-
import type { JointStateResult } from '../types';
|
|
11
|
+
import type { Joints, JointStateResult } from '../types';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Track a MuJoCo joint's position and velocity by name.
|
|
@@ -18,7 +18,7 @@ import type { JointStateResult } from '../types';
|
|
|
18
18
|
* For ball joints, position is quat (4), velocity is angular vel (3).
|
|
19
19
|
* For free joints, position is pos+quat (7), velocity is lin+ang vel (6).
|
|
20
20
|
*/
|
|
21
|
-
export function useJointState(name:
|
|
21
|
+
export function useJointState(name: Joints): JointStateResult {
|
|
22
22
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
23
23
|
const jointIdRef = useRef(-1);
|
|
24
24
|
const qposAdrRef = useRef(0);
|
package/src/hooks/useSensor.ts
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
import { useEffect, useRef, useMemo } from 'react';
|
|
9
9
|
import { useMujocoContext, useAfterPhysicsStep } from '../core/MujocoSimProvider';
|
|
10
10
|
import { getName } from '../core/SceneLoader';
|
|
11
|
-
import type {
|
|
11
|
+
import type { Sensors, SensorHandle, SensorInfo } from '../types';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Access a single MuJoCo sensor by name. Returns a
|
|
15
|
-
*
|
|
14
|
+
* Access a single MuJoCo sensor by name. Returns a `SensorHandle` with
|
|
15
|
+
* `read()`, `dim`, and `name`. The backing array is updated every physics
|
|
16
|
+
* frame without causing React re-renders.
|
|
16
17
|
*/
|
|
17
|
-
export function useSensor(name:
|
|
18
|
+
export function useSensor(name: Sensors): SensorHandle {
|
|
18
19
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
19
20
|
const sensorIdRef = useRef(-1);
|
|
20
21
|
const sensorAdrRef = useRef(0);
|
|
@@ -47,7 +48,15 @@ export function useSensor(name: string): SensorResult {
|
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
return
|
|
51
|
+
return useMemo<SensorHandle>(() => ({
|
|
52
|
+
read() {
|
|
53
|
+
return valueRef.current;
|
|
54
|
+
},
|
|
55
|
+
get dim() {
|
|
56
|
+
return sensorDimRef.current;
|
|
57
|
+
},
|
|
58
|
+
name,
|
|
59
|
+
}), [name]);
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
/**
|
|
@@ -8,7 +8,7 @@ import { useFrame } from '@react-three/fiber';
|
|
|
8
8
|
import * as THREE from 'three';
|
|
9
9
|
import { useMujocoContext } from '../core/MujocoSimProvider';
|
|
10
10
|
import { findSiteByName } from '../core/SceneLoader';
|
|
11
|
-
import type { SitePositionResult } from '../types';
|
|
11
|
+
import type { Sites, SitePositionResult } from '../types';
|
|
12
12
|
|
|
13
13
|
// Preallocated temp for rotation matrix extraction
|
|
14
14
|
const _mat4 = new THREE.Matrix4();
|
|
@@ -17,7 +17,7 @@ const _mat4 = new THREE.Matrix4();
|
|
|
17
17
|
* Returns reactive refs for a MuJoCo site's world position and orientation.
|
|
18
18
|
* Refs are updated every frame without triggering React re-renders.
|
|
19
19
|
*/
|
|
20
|
-
export function useSitePosition(siteName:
|
|
20
|
+
export function useSitePosition(siteName: Sites): SitePositionResult {
|
|
21
21
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
22
22
|
const siteIdRef = useRef(-1);
|
|
23
23
|
const positionRef = useRef(new THREE.Vector3());
|
package/src/index.ts
CHANGED
|
@@ -115,8 +115,19 @@ export type {
|
|
|
115
115
|
// Hook return types
|
|
116
116
|
SitePositionResult,
|
|
117
117
|
SensorResult,
|
|
118
|
+
CtrlHandle,
|
|
119
|
+
SensorHandle,
|
|
118
120
|
BodyStateResult,
|
|
119
121
|
JointStateResult,
|
|
122
|
+
// Register (type-safe named resources)
|
|
123
|
+
Register,
|
|
124
|
+
Actuators,
|
|
125
|
+
Sensors,
|
|
126
|
+
Bodies,
|
|
127
|
+
Joints,
|
|
128
|
+
Sites,
|
|
129
|
+
Geoms,
|
|
130
|
+
Keyframes,
|
|
120
131
|
} from './types';
|
|
121
132
|
|
|
122
133
|
// Re-export MuJoCo types for convenience
|
package/src/types.ts
CHANGED
|
@@ -8,6 +8,34 @@ import type { ReactNode } from 'react';
|
|
|
8
8
|
import type { CanvasProps } from '@react-three/fiber';
|
|
9
9
|
import * as THREE from 'three';
|
|
10
10
|
|
|
11
|
+
// ---- Register (type-safe named resources) ----
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Module augmentation interface for type-safe resource names.
|
|
15
|
+
*
|
|
16
|
+
* Declare your model's resource names via module augmentation:
|
|
17
|
+
* ```ts
|
|
18
|
+
* declare module 'mujoco-react' {
|
|
19
|
+
* interface Register {
|
|
20
|
+
* actuators: 'joint1' | 'joint2' | 'gripper';
|
|
21
|
+
* sensors: 'force_sensor' | 'torque_sensor';
|
|
22
|
+
* bodies: 'link0' | 'link1' | 'hand';
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* When no augmentation is declared, all names fall back to `string`.
|
|
28
|
+
*/
|
|
29
|
+
export interface Register {}
|
|
30
|
+
|
|
31
|
+
export type Actuators = Register extends { actuators: infer T extends string } ? T : string;
|
|
32
|
+
export type Sensors = Register extends { sensors: infer T extends string } ? T : string;
|
|
33
|
+
export type Bodies = Register extends { bodies: infer T extends string } ? T : string;
|
|
34
|
+
export type Joints = Register extends { joints: infer T extends string } ? T : string;
|
|
35
|
+
export type Sites = Register extends { sites: infer T extends string } ? T : string;
|
|
36
|
+
export type Geoms = Register extends { geoms: infer T extends string } ? T : string;
|
|
37
|
+
export type Keyframes = Register extends { keyframes: infer T extends string } ? T : string;
|
|
38
|
+
|
|
11
39
|
// ---- MuJoCo WASM Types ----
|
|
12
40
|
|
|
13
41
|
/**
|
|
@@ -300,7 +328,7 @@ export interface SceneConfig {
|
|
|
300
328
|
|
|
301
329
|
export interface IkConfig {
|
|
302
330
|
/** MuJoCo site name for IK target. */
|
|
303
|
-
siteName:
|
|
331
|
+
siteName: Sites;
|
|
304
332
|
/** Number of joints to solve for. */
|
|
305
333
|
numJoints: number;
|
|
306
334
|
/** Custom IK solver. When omitted, uses built-in Damped Least-Squares solver. */
|
|
@@ -462,7 +490,7 @@ export interface TrajectoryData {
|
|
|
462
490
|
// ---- Keyboard Teleop (spec 12.1) ----
|
|
463
491
|
|
|
464
492
|
export interface KeyBinding {
|
|
465
|
-
actuator:
|
|
493
|
+
actuator: Actuators;
|
|
466
494
|
delta?: number;
|
|
467
495
|
toggle?: [number, number];
|
|
468
496
|
set?: number;
|
|
@@ -527,13 +555,13 @@ export interface SelectionHighlightProps {
|
|
|
527
555
|
}
|
|
528
556
|
|
|
529
557
|
export interface ContactListenerProps {
|
|
530
|
-
body:
|
|
558
|
+
body: Bodies;
|
|
531
559
|
onContactEnter?: (info: ContactInfo) => void;
|
|
532
560
|
onContactExit?: (info: ContactInfo) => void;
|
|
533
561
|
}
|
|
534
562
|
|
|
535
563
|
export interface BodyProps {
|
|
536
|
-
name:
|
|
564
|
+
name: Bodies;
|
|
537
565
|
type: 'box' | 'sphere' | 'cylinder';
|
|
538
566
|
size: [number, number, number];
|
|
539
567
|
position?: [number, number, number];
|
|
@@ -562,7 +590,7 @@ export interface MujocoSimAPI {
|
|
|
562
590
|
step(n?: number): void;
|
|
563
591
|
getTime(): number;
|
|
564
592
|
getTimestep(): number;
|
|
565
|
-
applyKeyframe(nameOrIndex:
|
|
593
|
+
applyKeyframe(nameOrIndex: Keyframes | number): void;
|
|
566
594
|
|
|
567
595
|
// State management (spec 4.1, 4.2, 4.3)
|
|
568
596
|
saveState(): StateSnapshot;
|
|
@@ -573,17 +601,17 @@ export interface MujocoSimAPI {
|
|
|
573
601
|
getQvel(): Float64Array;
|
|
574
602
|
|
|
575
603
|
// Actuator / control (spec 3.1)
|
|
576
|
-
setCtrl(nameOrValues:
|
|
604
|
+
setCtrl(nameOrValues: Actuators | Record<Actuators, number>, value?: number): void;
|
|
577
605
|
getCtrl(): Float64Array;
|
|
578
606
|
|
|
579
607
|
// Force application (spec 8.1)
|
|
580
|
-
applyForce(bodyName:
|
|
581
|
-
applyTorque(bodyName:
|
|
582
|
-
setExternalForce(bodyName:
|
|
608
|
+
applyForce(bodyName: Bodies, force: THREE.Vector3, point?: THREE.Vector3): void;
|
|
609
|
+
applyTorque(bodyName: Bodies, torque: THREE.Vector3): void;
|
|
610
|
+
setExternalForce(bodyName: Bodies, force: THREE.Vector3, torque: THREE.Vector3): void;
|
|
583
611
|
applyGeneralizedForce(values: Float64Array | number[]): void;
|
|
584
612
|
|
|
585
613
|
// Sensors (spec 2.1)
|
|
586
|
-
getSensorData(name:
|
|
614
|
+
getSensorData(name: Sensors): Float64Array | null;
|
|
587
615
|
|
|
588
616
|
// Contacts (spec 2.4)
|
|
589
617
|
getContacts(): ContactInfo[];
|
|
@@ -621,9 +649,9 @@ export interface MujocoSimAPI {
|
|
|
621
649
|
): { point: THREE.Vector3; bodyId: number; geomId: number } | null;
|
|
622
650
|
|
|
623
651
|
// Domain randomization (spec 10.3)
|
|
624
|
-
setBodyMass(name:
|
|
625
|
-
setGeomFriction(name:
|
|
626
|
-
setGeomSize(name:
|
|
652
|
+
setBodyMass(name: Bodies, mass: number): void;
|
|
653
|
+
setGeomFriction(name: Geoms, friction: [number, number, number]): void;
|
|
654
|
+
setGeomSize(name: Geoms, size: [number, number, number]): void;
|
|
627
655
|
|
|
628
656
|
// Internal refs for advanced use
|
|
629
657
|
readonly mjModelRef: React.RefObject<MujocoModel | null>;
|
|
@@ -659,11 +687,32 @@ export interface MujocoContextValue {
|
|
|
659
687
|
error: string | null;
|
|
660
688
|
}
|
|
661
689
|
|
|
690
|
+
/** @deprecated Use `SensorHandle` instead. */
|
|
662
691
|
export interface SensorResult {
|
|
663
692
|
value: React.RefObject<Float64Array>;
|
|
664
693
|
size: number;
|
|
665
694
|
}
|
|
666
695
|
|
|
696
|
+
export interface CtrlHandle {
|
|
697
|
+
/** Read the current ctrl value. */
|
|
698
|
+
read(): number;
|
|
699
|
+
/** Write a ctrl value (goes directly to data.ctrl). */
|
|
700
|
+
write(value: number): void;
|
|
701
|
+
/** Actuator name. */
|
|
702
|
+
name: Actuators;
|
|
703
|
+
/** Actuator control range [min, max]. */
|
|
704
|
+
range: [number, number];
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
export interface SensorHandle {
|
|
708
|
+
/** Read the current sensor data. */
|
|
709
|
+
read(): Float64Array;
|
|
710
|
+
/** Sensor dimensionality. */
|
|
711
|
+
dim: number;
|
|
712
|
+
/** Sensor name. */
|
|
713
|
+
name: Sensors;
|
|
714
|
+
}
|
|
715
|
+
|
|
667
716
|
export interface BodyStateResult {
|
|
668
717
|
position: React.RefObject<THREE.Vector3>;
|
|
669
718
|
quaternion: React.RefObject<THREE.Quaternion>;
|