mujoco-react 0.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.
Files changed (42) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +510 -0
  3. package/dist/index.d.ts +1080 -0
  4. package/dist/index.js +3518 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +64 -0
  7. package/src/components/ContactListener.tsx +26 -0
  8. package/src/components/ContactMarkers.tsx +81 -0
  9. package/src/components/Debug.tsx +227 -0
  10. package/src/components/DragInteraction.tsx +227 -0
  11. package/src/components/FlexRenderer.tsx +102 -0
  12. package/src/components/IkGizmo.tsx +146 -0
  13. package/src/components/SceneLights.tsx +131 -0
  14. package/src/components/SceneRenderer.tsx +104 -0
  15. package/src/components/SelectionHighlight.tsx +69 -0
  16. package/src/components/TendonRenderer.tsx +84 -0
  17. package/src/components/TrajectoryPlayer.tsx +44 -0
  18. package/src/core/GenericIK.ts +339 -0
  19. package/src/core/MujocoCanvas.tsx +72 -0
  20. package/src/core/MujocoProvider.tsx +78 -0
  21. package/src/core/MujocoSimProvider.tsx +1201 -0
  22. package/src/core/SceneLoader.ts +275 -0
  23. package/src/hooks/useActuators.ts +36 -0
  24. package/src/hooks/useBodyState.ts +56 -0
  25. package/src/hooks/useContacts.ts +125 -0
  26. package/src/hooks/useCtrl.ts +40 -0
  27. package/src/hooks/useCtrlNoise.ts +59 -0
  28. package/src/hooks/useGamepad.ts +77 -0
  29. package/src/hooks/useGravityCompensation.ts +22 -0
  30. package/src/hooks/useJointState.ts +64 -0
  31. package/src/hooks/useKeyboardTeleop.ts +97 -0
  32. package/src/hooks/usePolicy.ts +56 -0
  33. package/src/hooks/useSensor.ts +83 -0
  34. package/src/hooks/useSitePosition.ts +62 -0
  35. package/src/hooks/useTrajectoryPlayer.ts +105 -0
  36. package/src/hooks/useTrajectoryRecorder.ts +97 -0
  37. package/src/hooks/useVideoRecorder.ts +82 -0
  38. package/src/index.ts +108 -0
  39. package/src/rendering/CapsuleGeometry.ts +35 -0
  40. package/src/rendering/GeomBuilder.ts +140 -0
  41. package/src/rendering/Reflector.ts +225 -0
  42. package/src/types.ts +619 -0
package/src/types.ts ADDED
@@ -0,0 +1,619 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import type { CanvasProps } from '@react-three/fiber';
7
+ import * as THREE from 'three';
8
+
9
+ // ---- MuJoCo WASM Types ----
10
+
11
+ /**
12
+ * Minimal interface for MuJoCo Model to avoid 'any'.
13
+ */
14
+ export interface MujocoModel {
15
+ // Counts
16
+ nbody: number;
17
+ ngeom: number;
18
+ nsite: number;
19
+ nu: number;
20
+ njnt: number;
21
+ nq: number;
22
+ nv: number;
23
+ nkey: number;
24
+ nsensor: number;
25
+ nsensordata: number;
26
+ nlight: number;
27
+ ntendon: number;
28
+ nflex: number;
29
+ nmesh: number;
30
+ nmat: number;
31
+
32
+ // Name tables
33
+ names: Int8Array;
34
+ name_bodyadr: Int32Array;
35
+ name_jntadr: Int32Array;
36
+ name_geomadr: Int32Array;
37
+ name_siteadr: Int32Array;
38
+ name_actuatoradr: Int32Array;
39
+ name_keyadr: Int32Array;
40
+ name_sensoradr: Int32Array;
41
+ name_tendonadr: Int32Array;
42
+
43
+ // Body
44
+ body_mass: Float64Array;
45
+ body_parentid: Int32Array;
46
+ body_jntnum: Int32Array;
47
+ body_jntadr: Int32Array;
48
+ body_pos: Float64Array;
49
+ body_quat: Float64Array;
50
+ body_geomnum: Int32Array;
51
+ body_geomadr: Int32Array;
52
+ body_inertia: Float64Array;
53
+
54
+ // Joint
55
+ jnt_qposadr: Int32Array;
56
+ jnt_dofadr: Int32Array;
57
+ jnt_type: Int32Array;
58
+ jnt_range: Float64Array;
59
+ jnt_bodyid: Int32Array;
60
+ jnt_limited: Uint8Array;
61
+
62
+ // Geom
63
+ geom_group: Int32Array;
64
+ geom_type: Int32Array;
65
+ geom_size: Float64Array;
66
+ geom_pos: Float64Array;
67
+ geom_quat: Float64Array;
68
+ geom_matid: Int32Array;
69
+ geom_rgba: Float32Array;
70
+ geom_dataid: Int32Array;
71
+ geom_bodyid: Int32Array;
72
+ geom_contype: Int32Array;
73
+ geom_conaffinity: Int32Array;
74
+ geom_friction: Float64Array;
75
+
76
+ // Material
77
+ mat_rgba: Float32Array;
78
+
79
+ // Mesh
80
+ mesh_vertadr: Int32Array;
81
+ mesh_vertnum: Int32Array;
82
+ mesh_faceadr: Int32Array;
83
+ mesh_facenum: Int32Array;
84
+ mesh_vert: Float32Array;
85
+ mesh_face: Int32Array;
86
+ mesh_normal: Float32Array;
87
+
88
+ // Site
89
+ site_bodyid: Int32Array;
90
+
91
+ // Actuator
92
+ actuator_trnid: Int32Array;
93
+ actuator_ctrlrange: Float64Array;
94
+ actuator_trntype: Int32Array;
95
+ actuator_gainprm: Float64Array;
96
+ actuator_biasprm: Float64Array;
97
+
98
+ // Sensor
99
+ sensor_type: Int32Array;
100
+ sensor_dim: Int32Array;
101
+ sensor_adr: Int32Array;
102
+ sensor_objtype: Int32Array;
103
+ sensor_objid: Int32Array;
104
+
105
+ // Keyframe
106
+ key_qpos: Float64Array;
107
+ key_ctrl: Float64Array;
108
+ key_time: Float64Array;
109
+ key_qvel: Float64Array;
110
+
111
+ // Light
112
+ light_pos: Float64Array;
113
+ light_dir: Float64Array;
114
+ light_diffuse: Float32Array;
115
+ light_specular: Float32Array;
116
+ light_type: Int32Array;
117
+ light_active: Uint8Array;
118
+ light_castshadow: Uint8Array;
119
+ light_attenuation: Float32Array;
120
+ light_cutoff: Float32Array;
121
+ light_exponent: Float32Array;
122
+ light_intensity: Float32Array;
123
+
124
+ // Tendon
125
+ ten_wrapadr: Int32Array;
126
+ ten_wrapnum: Int32Array;
127
+ ten_range: Float64Array;
128
+ ten_rgba: Float32Array;
129
+ ten_width: Float64Array;
130
+
131
+ // Flex
132
+ flex_vertadr: Int32Array;
133
+ flex_vertnum: Int32Array;
134
+ flex_faceadr: Int32Array;
135
+ flex_facenum: Int32Array;
136
+ flex_face: Int32Array;
137
+ flex_rgba: Float32Array;
138
+
139
+ // Model options
140
+ opt: {
141
+ timestep: number;
142
+ gravity: Float64Array;
143
+ integrator: number;
144
+ [key: string]: unknown;
145
+ };
146
+
147
+ delete: () => void;
148
+ [key: string]: unknown;
149
+ }
150
+
151
+ /**
152
+ * Minimal interface for MuJoCo Data to avoid 'any'.
153
+ */
154
+ export interface MujocoData {
155
+ time: number;
156
+ qpos: Float64Array;
157
+ qvel: Float64Array;
158
+ ctrl: Float64Array;
159
+ act: Float64Array;
160
+ xpos: Float64Array;
161
+ xquat: Float64Array;
162
+ xfrc_applied: Float64Array;
163
+ qfrc_applied: Float64Array;
164
+ qfrc_bias: Float64Array;
165
+ site_xpos: Float64Array;
166
+ site_xmat: Float64Array;
167
+ sensordata: Float64Array;
168
+ ncon: number;
169
+ contact: unknown;
170
+ cvel: Float64Array;
171
+ cfrc_ext: Float64Array;
172
+ ten_length: Float64Array;
173
+ wrap_xpos: Float64Array;
174
+ ten_wrapadr: Int32Array;
175
+ flexvert_xpos: Float64Array;
176
+ geom_xpos: Float64Array;
177
+ geom_xmat: Float64Array;
178
+ delete: () => void;
179
+ [key: string]: unknown;
180
+ }
181
+
182
+ /**
183
+ * Minimal interface for the MuJoCo WASM Module.
184
+ */
185
+ export interface MujocoModule {
186
+ MjModel: { loadFromXML: (path: string) => MujocoModel; [key: string]: unknown };
187
+ MjData: new (model: MujocoModel) => MujocoData;
188
+ MjvOption: new () => { delete: () => void; [key: string]: unknown };
189
+ mj_forward: (m: MujocoModel, d: MujocoData) => void;
190
+ mj_step: (m: MujocoModel, d: MujocoData) => void;
191
+ mj_resetData: (m: MujocoModel, d: MujocoData) => void;
192
+ mj_step1: (m: MujocoModel, d: MujocoData) => void;
193
+ mj_step2: (m: MujocoModel, d: MujocoData) => void;
194
+ mj_applyFT: (
195
+ model: MujocoModel,
196
+ data: MujocoData,
197
+ force: Float64Array,
198
+ torque: Float64Array,
199
+ point: Float64Array,
200
+ bodyId: number,
201
+ qfrc_target: Float64Array
202
+ ) => void;
203
+ mj_ray: (
204
+ model: MujocoModel,
205
+ data: MujocoData,
206
+ pnt: Float64Array,
207
+ vec: Float64Array,
208
+ geomgroup: Uint8Array | null,
209
+ flg_static: number,
210
+ bodyexclude: number,
211
+ geomid: Int32Array
212
+ ) => number;
213
+ mj_name2id: (model: MujocoModel, type: number, name: string) => number;
214
+ mjtObj: Record<string, number>;
215
+ mjtGeom: Record<string, number | {value: number}>;
216
+ mjtJoint: Record<string, number | {value: number}>;
217
+ mjtSensor: Record<string, number | {value: number}>;
218
+ FS: {
219
+ writeFile: (path: string, content: string | Uint8Array) => void;
220
+ readFile: (path: string, opts?: { encoding: string }) => string | Uint8Array;
221
+ mkdir: (path: string) => void;
222
+ unmount: (path: string) => void;
223
+ };
224
+ [key: string]: unknown;
225
+ }
226
+
227
+ // ---- Scene Configuration ----
228
+
229
+ export interface SceneObject {
230
+ name: string;
231
+ type: 'box' | 'sphere' | 'cylinder';
232
+ size: [number, number, number];
233
+ position: [number, number, number];
234
+ rgba: [number, number, number, number];
235
+ mass?: number;
236
+ freejoint?: boolean;
237
+ friction?: string;
238
+ solref?: string;
239
+ solimp?: string;
240
+ condim?: number;
241
+ }
242
+
243
+ export interface XmlPatch {
244
+ target: string;
245
+ inject?: string;
246
+ injectAfter?: string;
247
+ replace?: [string, string];
248
+ }
249
+
250
+ export interface SceneConfig {
251
+ robotId: string;
252
+ sceneFile: string;
253
+ baseUrl?: string;
254
+ sceneObjects?: SceneObject[];
255
+ tcpSiteName?: string;
256
+ gripperActuatorName?: string;
257
+ numArmJoints?: number;
258
+ homeJoints?: number[];
259
+ xmlPatches?: XmlPatch[];
260
+ onReset?: (model: MujocoModel, data: MujocoData) => void;
261
+ }
262
+
263
+ export interface SceneMarker {
264
+ id: number;
265
+ position: THREE.Vector3;
266
+ label: string;
267
+ }
268
+
269
+ // ---- Physics Config (spec 1.1) ----
270
+
271
+ export interface PhysicsConfig {
272
+ gravity?: [number, number, number];
273
+ timestep?: number;
274
+ substeps?: number;
275
+ paused?: boolean;
276
+ speed?: number;
277
+ interpolate?: boolean;
278
+ }
279
+
280
+ // ---- IK ----
281
+
282
+ export type IKSolveFn = (
283
+ pos: THREE.Vector3,
284
+ quat: THREE.Quaternion,
285
+ currentQ: number[]
286
+ ) => number[] | null;
287
+
288
+ // ---- Callbacks ----
289
+
290
+ export type PhysicsStepCallback = (
291
+ model: MujocoModel,
292
+ data: MujocoData
293
+ ) => void;
294
+
295
+ // ---- State Management (spec 4.1) ----
296
+
297
+ export interface StateSnapshot {
298
+ time: number;
299
+ qpos: Float64Array;
300
+ qvel: Float64Array;
301
+ ctrl: Float64Array;
302
+ act: Float64Array;
303
+ qfrc_applied: Float64Array;
304
+ }
305
+
306
+ // ---- Model Introspection (spec 5.1) ----
307
+
308
+ export interface BodyInfo {
309
+ id: number;
310
+ name: string;
311
+ mass: number;
312
+ parentId: number;
313
+ }
314
+
315
+ export interface JointInfo {
316
+ id: number;
317
+ name: string;
318
+ type: number;
319
+ typeName: string;
320
+ range: [number, number];
321
+ limited: boolean;
322
+ bodyId: number;
323
+ qposAdr: number;
324
+ dofAdr: number;
325
+ }
326
+
327
+ export interface GeomInfo {
328
+ id: number;
329
+ name: string;
330
+ type: number;
331
+ typeName: string;
332
+ size: [number, number, number];
333
+ bodyId: number;
334
+ }
335
+
336
+ export interface SiteInfo {
337
+ id: number;
338
+ name: string;
339
+ bodyId: number;
340
+ }
341
+
342
+ export interface ActuatorInfo {
343
+ id: number;
344
+ name: string;
345
+ range: [number, number];
346
+ }
347
+
348
+ export interface SensorInfo {
349
+ id: number;
350
+ name: string;
351
+ type: number;
352
+ typeName: string;
353
+ dim: number;
354
+ adr: number;
355
+ }
356
+
357
+ // ---- Contacts (spec 2.4, 2.5) ----
358
+
359
+ export interface ContactInfo {
360
+ geom1: number;
361
+ geom1Name: string;
362
+ geom2: number;
363
+ geom2Name: string;
364
+ pos: [number, number, number];
365
+ depth: number;
366
+ }
367
+
368
+ // ---- Raycast (spec 7.1) ----
369
+
370
+ export interface RayHit {
371
+ point: THREE.Vector3;
372
+ bodyId: number;
373
+ geomId: number;
374
+ distance: number;
375
+ }
376
+
377
+ // ---- Model Options (spec 5.3) ----
378
+
379
+ export interface ModelOptions {
380
+ timestep: number;
381
+ gravity: [number, number, number];
382
+ integrator: number;
383
+ }
384
+
385
+ // ---- Trajectory (spec 13.1, 13.2) ----
386
+
387
+ export interface TrajectoryFrame {
388
+ time: number;
389
+ qpos: Float64Array;
390
+ qvel?: Float64Array;
391
+ ctrl?: Float64Array;
392
+ sensordata?: Float64Array;
393
+ }
394
+
395
+ export interface TrajectoryData {
396
+ frames: TrajectoryFrame[];
397
+ fps: number;
398
+ }
399
+
400
+ // ---- Keyboard Teleop (spec 12.1) ----
401
+
402
+ export interface KeyBinding {
403
+ actuator: string;
404
+ delta?: number;
405
+ toggle?: [number, number];
406
+ set?: number;
407
+ }
408
+
409
+ export interface KeyboardTeleopConfig {
410
+ bindings: Record<string, KeyBinding>;
411
+ enabled?: boolean;
412
+ }
413
+
414
+ // ---- Policy (spec 10.1) ----
415
+
416
+ export interface PolicyConfig {
417
+ frequency: number;
418
+ onObservation: (model: MujocoModel, data: MujocoData) => Float32Array | Float64Array | number[];
419
+ onAction: (action: Float32Array | Float64Array | number[], model: MujocoModel, data: MujocoData) => void;
420
+ }
421
+
422
+ // ---- Debug Component (spec 6.1) ----
423
+
424
+ export interface DebugProps {
425
+ showGeoms?: boolean;
426
+ showSites?: boolean;
427
+ showJoints?: boolean;
428
+ showContacts?: boolean;
429
+ showCOM?: boolean;
430
+ showInertia?: boolean;
431
+ showTendons?: boolean;
432
+ }
433
+
434
+ // ---- Component Props ----
435
+
436
+ export interface IkGizmoProps {
437
+ siteName?: string;
438
+ scale?: number;
439
+ onDrag?: (position: THREE.Vector3, quaternion: THREE.Quaternion) => void;
440
+ }
441
+
442
+ export interface DragInteractionProps {
443
+ stiffness?: number;
444
+ showArrow?: boolean;
445
+ }
446
+
447
+ export interface SceneLightsProps {
448
+ /** Override intensity for all MJCF lights. Default: 1.0. */
449
+ intensity?: number;
450
+ }
451
+
452
+ export interface TrajectoryPlayerProps {
453
+ trajectory: number[][];
454
+ fps?: number;
455
+ loop?: boolean;
456
+ playing?: boolean;
457
+ onFrame?: (frameIdx: number) => void;
458
+ }
459
+
460
+ export interface SelectionHighlightProps {
461
+ bodyId: number | null;
462
+ color?: string;
463
+ emissiveIntensity?: number;
464
+ }
465
+
466
+ export interface ContactListenerProps {
467
+ body: string;
468
+ onContactEnter?: (info: ContactInfo) => void;
469
+ onContactExit?: (info: ContactInfo) => void;
470
+ }
471
+
472
+ // ---- Public API (spec: full surface) ----
473
+
474
+ export interface MujocoSimAPI {
475
+ // State
476
+ readonly status: 'loading' | 'ready' | 'error';
477
+ readonly config: SceneConfig;
478
+
479
+ // Simulation control (spec 1.1, 1.2, 1.3)
480
+ reset(): void;
481
+ setSpeed(multiplier: number): void;
482
+ togglePause(): boolean;
483
+ setPaused(paused: boolean): void;
484
+ step(n?: number): void;
485
+ getTime(): number;
486
+ getTimestep(): number;
487
+ applyKeyframe(nameOrIndex: string | number): void;
488
+
489
+ // State management (spec 4.1, 4.2, 4.3)
490
+ saveState(): StateSnapshot;
491
+ restoreState(snapshot: StateSnapshot): void;
492
+ setQpos(values: Float64Array | number[]): void;
493
+ setQvel(values: Float64Array | number[]): void;
494
+ getQpos(): Float64Array;
495
+ getQvel(): Float64Array;
496
+
497
+ // Actuator / control (spec 3.1)
498
+ setCtrl(nameOrValues: string | Record<string, number>, value?: number): void;
499
+ getCtrl(): Float64Array;
500
+
501
+ // Force application (spec 8.1)
502
+ applyForce(bodyName: string, force: THREE.Vector3, point?: THREE.Vector3): void;
503
+ applyTorque(bodyName: string, torque: THREE.Vector3): void;
504
+ setExternalForce(bodyName: string, force: THREE.Vector3, torque: THREE.Vector3): void;
505
+ applyGeneralizedForce(values: Float64Array | number[]): void;
506
+
507
+ // Sensors (spec 2.1)
508
+ getSensorData(name: string): Float64Array | null;
509
+
510
+ // Contacts (spec 2.4)
511
+ getContacts(): ContactInfo[];
512
+
513
+ // Model introspection (spec 5.1, 5.2)
514
+ getBodies(): BodyInfo[];
515
+ getJoints(): JointInfo[];
516
+ getGeoms(): GeomInfo[];
517
+ getSites(): SiteInfo[];
518
+ getActuators(): ActuatorInfo[];
519
+ getSensors(): SensorInfo[];
520
+
521
+ // Model parameters (spec 5.3)
522
+ getModelOption(): ModelOptions;
523
+ setGravity(g: [number, number, number]): void;
524
+ setTimestep(dt: number): void;
525
+
526
+ // Raycasting (spec 7.1)
527
+ raycast(origin: THREE.Vector3, direction: THREE.Vector3, maxDist?: number): RayHit | null;
528
+
529
+ // Keyframes (spec 4.2)
530
+ getKeyframeNames(): string[];
531
+ getKeyframeCount(): number;
532
+
533
+ // Model loading (spec 9.1)
534
+ loadScene(newConfig: SceneConfig): Promise<void>;
535
+
536
+ // IK control
537
+ setIkEnabled(enabled: boolean): void;
538
+ moveTarget(pos: THREE.Vector3, duration?: number): void;
539
+ syncTargetToSite(): void;
540
+ solveIK(
541
+ pos: THREE.Vector3,
542
+ quat: THREE.Quaternion,
543
+ currentQ: number[]
544
+ ): number[] | null;
545
+ getGizmoStats(): { pos: THREE.Vector3; rot: THREE.Euler } | null;
546
+
547
+ // Canvas / camera
548
+ getCanvasSnapshot(width?: number, height?: number, mimeType?: string): string;
549
+ project2DTo3D(
550
+ x: number,
551
+ y: number,
552
+ cameraPos: THREE.Vector3,
553
+ lookAt: THREE.Vector3
554
+ ): { point: THREE.Vector3; bodyId: number; geomId: number } | null;
555
+
556
+ // Domain randomization (spec 10.3)
557
+ setBodyMass(name: string, mass: number): void;
558
+ setGeomFriction(name: string, friction: [number, number, number]): void;
559
+ setGeomSize(name: string, size: [number, number, number]): void;
560
+ getCameraState(): { position: THREE.Vector3; target: THREE.Vector3 };
561
+ moveCameraTo(
562
+ position: THREE.Vector3,
563
+ target: THREE.Vector3,
564
+ durationMs: number
565
+ ): Promise<void>;
566
+
567
+ // Internal refs for advanced use
568
+ readonly mjModelRef: React.RefObject<MujocoModel | null>;
569
+ readonly mjDataRef: React.RefObject<MujocoData | null>;
570
+ }
571
+
572
+ // ---- Canvas Props ----
573
+
574
+ export type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
575
+ config: SceneConfig;
576
+ onReady?: (api: MujocoSimAPI) => void;
577
+ onError?: (error: Error) => void;
578
+ onStep?: (time: number) => void;
579
+ onSelection?: (bodyId: number, name: string) => void;
580
+ // Declarative physics config (spec 1.1)
581
+ gravity?: [number, number, number];
582
+ timestep?: number;
583
+ substeps?: number;
584
+ paused?: boolean;
585
+ speed?: number;
586
+ interpolate?: boolean;
587
+ gravityCompensation?: boolean;
588
+ mjcfLights?: boolean;
589
+ };
590
+
591
+ // ---- Hook Return Types ----
592
+
593
+ export interface SitePositionResult {
594
+ position: React.RefObject<THREE.Vector3>;
595
+ quaternion: React.RefObject<THREE.Quaternion>;
596
+ }
597
+
598
+ export interface MujocoContextValue {
599
+ mujoco: MujocoModule | null;
600
+ status: 'loading' | 'ready' | 'error';
601
+ error: string | null;
602
+ }
603
+
604
+ export interface SensorResult {
605
+ value: React.RefObject<Float64Array>;
606
+ size: number;
607
+ }
608
+
609
+ export interface BodyStateResult {
610
+ position: React.RefObject<THREE.Vector3>;
611
+ quaternion: React.RefObject<THREE.Quaternion>;
612
+ linearVelocity: React.RefObject<THREE.Vector3>;
613
+ angularVelocity: React.RefObject<THREE.Vector3>;
614
+ }
615
+
616
+ export interface JointStateResult {
617
+ position: React.RefObject<number | Float64Array>;
618
+ velocity: React.RefObject<number | Float64Array>;
619
+ }