mujoco-react 10.5.0 → 10.7.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 +42 -11
- package/dist/{chunk-KHZ5U36J.js → chunk-EN55TTGH.js} +2 -2
- package/dist/chunk-EN55TTGH.js.map +1 -0
- package/dist/index.d.ts +41 -5
- package/dist/index.js +122 -25
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-CViUme8D.d.ts → types-Dvtm4I0o.d.ts} +14 -3
- package/package.json +1 -1
- package/src/core/GenericIK.ts +17 -10
- package/src/hooks/useControlGroup.ts +0 -0
- package/src/hooks/useIkController.ts +33 -5
- package/src/index.ts +3 -1
- package/src/types.ts +22 -3
- package/dist/chunk-KHZ5U36J.js.map +0 -1
package/dist/onnx.d.ts
CHANGED
package/dist/spark.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as _sparkjsdev_spark from '@sparkjsdev/spark';
|
|
3
|
-
import { o as SplatEnvironmentProps, r as PairedSplatEnvironmentConfig, S as SceneConfig, u as SplatEnvironmentReadiness, p as VisualScenarioConfig } from './types-
|
|
3
|
+
import { o as SplatEnvironmentProps, r as PairedSplatEnvironmentConfig, S as SceneConfig, u as SplatEnvironmentReadiness, p as VisualScenarioConfig } from './types-Dvtm4I0o.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '@react-three/fiber';
|
|
6
6
|
import 'three';
|
package/dist/spark.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY } from './chunk-
|
|
1
|
+
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY } from './chunk-EN55TTGH.js';
|
|
2
2
|
import { useThree } from '@react-three/fiber';
|
|
3
3
|
import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
@@ -540,7 +540,7 @@ interface IkContextValue {
|
|
|
540
540
|
ikTargetRef: React__default.RefObject<THREE.Group>;
|
|
541
541
|
siteIdRef: React__default.RefObject<number>;
|
|
542
542
|
setIkEnabled: (enabled: boolean) => void;
|
|
543
|
-
moveTarget: (pos:
|
|
543
|
+
moveTarget: (pos: IkTargetPosition, duration?: number) => void;
|
|
544
544
|
syncTargetToSite: () => void;
|
|
545
545
|
solveIK: (input: IkSolveInput) => number[] | null;
|
|
546
546
|
getGizmoStats: () => {
|
|
@@ -561,9 +561,20 @@ interface PhysicsConfig {
|
|
|
561
561
|
speed?: number;
|
|
562
562
|
}
|
|
563
563
|
type IKSolveFn = (input: IkSolveInput) => number[] | null;
|
|
564
|
+
type IkTargetPosition = THREE.Vector3 | readonly [number, number, number] | {
|
|
565
|
+
readonly x: number;
|
|
566
|
+
readonly y: number;
|
|
567
|
+
readonly z: number;
|
|
568
|
+
};
|
|
569
|
+
type IkTargetQuaternion = THREE.Quaternion | readonly [number, number, number, number] | {
|
|
570
|
+
readonly x: number;
|
|
571
|
+
readonly y: number;
|
|
572
|
+
readonly z: number;
|
|
573
|
+
readonly w: number;
|
|
574
|
+
};
|
|
564
575
|
interface IkSolveInput {
|
|
565
|
-
position:
|
|
566
|
-
quaternion:
|
|
576
|
+
position: IkTargetPosition;
|
|
577
|
+
quaternion: IkTargetQuaternion;
|
|
567
578
|
currentQ: number[];
|
|
568
579
|
context?: IKSolveContext;
|
|
569
580
|
}
|
package/package.json
CHANGED
package/src/core/GenericIK.ts
CHANGED
|
@@ -40,13 +40,6 @@ function resolveOptions(opts?: Partial<GenericIKOptions>): ResolvedGenericIKOpti
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function clampJoint(value: number, limit: readonly [number, number] | null | undefined) {
|
|
44
|
-
if (!limit) return value;
|
|
45
|
-
const [min, max] = limit;
|
|
46
|
-
if (!Number.isFinite(min) || !Number.isFinite(max) || min >= max) return value;
|
|
47
|
-
return Math.max(min, Math.min(max, value));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
43
|
/**
|
|
51
44
|
* Generic Damped Least-Squares IK solver.
|
|
52
45
|
* Uses finite-difference Jacobian via MuJoCo's mj_forward.
|
|
@@ -93,7 +86,7 @@ export class GenericIK {
|
|
|
93
86
|
|
|
94
87
|
// Working joint angles — start from current configuration
|
|
95
88
|
const q = new Float64Array(n);
|
|
96
|
-
for (let i = 0; i < n; i++) q[i] =
|
|
89
|
+
for (let i = 0; i < n; i++) q[i] = currentQ[i];
|
|
97
90
|
|
|
98
91
|
// Pre-allocate work arrays
|
|
99
92
|
const J = new Float64Array(6 * n); // 6×n Jacobian (row-major)
|
|
@@ -108,6 +101,14 @@ export class GenericIK {
|
|
|
108
101
|
|
|
109
102
|
let bestQ: number[] | null = null;
|
|
110
103
|
let bestErr = Infinity;
|
|
104
|
+
// Stop early once the error stops improving. The solver always returns the
|
|
105
|
+
// best configuration it has seen, so when it stalls or diverges (an
|
|
106
|
+
// unreachable target, e.g. a near-singular wrist orientation) further
|
|
107
|
+
// iterations can't beat bestQ — they only burn (1 + n) mj_forward calls
|
|
108
|
+
// each and feed jitter into ctrl. This is what bounds the cost of dragging
|
|
109
|
+
// the target into an unreachable region.
|
|
110
|
+
const patience = 4;
|
|
111
|
+
let noImprove = 0;
|
|
111
112
|
|
|
112
113
|
if (n === 0) return null;
|
|
113
114
|
|
|
@@ -145,14 +146,20 @@ export class GenericIK {
|
|
|
145
146
|
);
|
|
146
147
|
|
|
147
148
|
// Track best solution
|
|
148
|
-
if (errNorm < bestErr) {
|
|
149
|
+
if (errNorm < bestErr - 1e-9) {
|
|
149
150
|
bestErr = errNorm;
|
|
150
151
|
bestQ = Array.from(q);
|
|
152
|
+
noImprove = 0;
|
|
153
|
+
} else {
|
|
154
|
+
noImprove++;
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
// Converged
|
|
154
158
|
if (errNorm < o.tolerance) break;
|
|
155
159
|
|
|
160
|
+
// Stalled or diverging — bestQ can no longer improve, so stop.
|
|
161
|
+
if (noImprove >= patience) break;
|
|
162
|
+
|
|
156
163
|
// Compute Jacobian via finite differences
|
|
157
164
|
for (let j = 0; j < n; j++) {
|
|
158
165
|
const adr = qposAdr[j];
|
|
@@ -208,7 +215,7 @@ export class GenericIK {
|
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
// Update joints
|
|
211
|
-
for (let i = 0; i < n; i++) q[i]
|
|
218
|
+
for (let i = 0; i < n; i++) q[i] += dq[i];
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
// Restore original qpos
|
|
Binary file
|
|
@@ -10,11 +10,31 @@ import { createControllerHook } from '../core/createController';
|
|
|
10
10
|
import { useMujocoContext, useBeforePhysicsStep } from '../core/MujocoSimProvider';
|
|
11
11
|
import { GenericIK } from '../core/GenericIK';
|
|
12
12
|
import { createContiguousControlGroup, findSiteByName, resolveControlGroup } from '../core/SceneLoader';
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
ControlGroupInfo,
|
|
15
|
+
IkConfig,
|
|
16
|
+
IkContextValue,
|
|
17
|
+
IKSolveFn,
|
|
18
|
+
IkSolveInput,
|
|
19
|
+
IkTargetPosition,
|
|
20
|
+
MujocoData,
|
|
21
|
+
} from '../types';
|
|
14
22
|
|
|
15
23
|
// Preallocated temp for syncGizmoToSite
|
|
16
24
|
const _syncMat4 = new THREE.Matrix4();
|
|
17
25
|
|
|
26
|
+
function toIkVector3(value: IkSolveInput['position']): THREE.Vector3 {
|
|
27
|
+
return 'x' in value
|
|
28
|
+
? new THREE.Vector3(value.x, value.y, value.z)
|
|
29
|
+
: new THREE.Vector3(value[0], value[1], value[2]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toIkQuaternion(value: IkSolveInput['quaternion']): THREE.Quaternion {
|
|
33
|
+
return 'x' in value
|
|
34
|
+
? new THREE.Quaternion(value.x, value.y, value.z, value.w)
|
|
35
|
+
: new THREE.Quaternion(value[0], value[1], value[2], value[3]);
|
|
36
|
+
}
|
|
37
|
+
|
|
18
38
|
function syncGizmoToSite(data: MujocoData, siteId: number, target: THREE.Group) {
|
|
19
39
|
if (siteId === -1) return;
|
|
20
40
|
const sitePos = data.site_xpos.subarray(siteId * 3, siteId * 3 + 3);
|
|
@@ -86,14 +106,22 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
|
|
|
86
106
|
const ikSolveFn = useCallback(
|
|
87
107
|
({ position, quaternion, currentQ, context }: IkSolveInput): number[] | null => {
|
|
88
108
|
if (!config) return null;
|
|
89
|
-
|
|
109
|
+
const targetPosition = toIkVector3(position);
|
|
110
|
+
const targetQuaternion = toIkQuaternion(quaternion);
|
|
111
|
+
const normalizedInput: IkSolveInput = {
|
|
112
|
+
position: targetPosition,
|
|
113
|
+
quaternion: targetQuaternion,
|
|
114
|
+
currentQ,
|
|
115
|
+
context,
|
|
116
|
+
};
|
|
117
|
+
if (config.ikSolveFn) return config.ikSolveFn(normalizedInput);
|
|
90
118
|
const model = mjModelRef.current;
|
|
91
119
|
const data = mjDataRef.current;
|
|
92
120
|
const controlGroup = controlGroupRef.current;
|
|
93
121
|
if (!model || !data || !controlGroup || siteIdRef.current === -1) return null;
|
|
94
122
|
return genericIkRef.current.solve(
|
|
95
123
|
model, data, siteIdRef.current, controlGroup.qposAdr,
|
|
96
|
-
|
|
124
|
+
targetPosition, targetQuaternion, currentQ,
|
|
97
125
|
{
|
|
98
126
|
damping: config.damping,
|
|
99
127
|
epsilon: config.epsilon,
|
|
@@ -218,12 +246,12 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
|
|
|
218
246
|
);
|
|
219
247
|
|
|
220
248
|
const moveTarget = useCallback(
|
|
221
|
-
(pos:
|
|
249
|
+
(pos: IkTargetPosition, duration = 0) => {
|
|
222
250
|
if (!ikEnabledRef.current) setIkEnabled(true);
|
|
223
251
|
const target = ikTargetRef.current;
|
|
224
252
|
if (!target) return;
|
|
225
253
|
|
|
226
|
-
const targetPos = pos
|
|
254
|
+
const targetPos = toIkVector3(pos);
|
|
227
255
|
const targetRot = new THREE.Quaternion().setFromEuler(
|
|
228
256
|
new THREE.Euler(Math.PI, 0, 0),
|
|
229
257
|
);
|
package/src/index.ts
CHANGED
|
@@ -97,11 +97,13 @@ export { useBodyState } from './hooks/useBodyState';
|
|
|
97
97
|
export { useBodyPose, useGeomPose, useSitePose } from './hooks/usePose';
|
|
98
98
|
export type { PoseReadout, PoseResourceKind } from './hooks/usePose';
|
|
99
99
|
export { useCtrl } from './hooks/useCtrl';
|
|
100
|
-
export { controlGroup, useControlGroup } from './hooks/useControlGroup';
|
|
100
|
+
export { controlGroup, defineControls, useControlGroup, useControls } from './hooks/useControlGroup';
|
|
101
101
|
export type {
|
|
102
102
|
ControlGroup,
|
|
103
103
|
ControlGroupHandle,
|
|
104
104
|
ControlGroupSetOptions,
|
|
105
|
+
ControlsHandle,
|
|
106
|
+
DefinedControls,
|
|
105
107
|
UseControlGroupOptions,
|
|
106
108
|
} from './hooks/useControlGroup';
|
|
107
109
|
export { useControlWriter } from './hooks/useControlWriter';
|
package/src/types.ts
CHANGED
|
@@ -564,7 +564,7 @@ export interface IkContextValue {
|
|
|
564
564
|
ikTargetRef: React.RefObject<THREE.Group>;
|
|
565
565
|
siteIdRef: React.RefObject<number>;
|
|
566
566
|
setIkEnabled: (enabled: boolean) => void;
|
|
567
|
-
moveTarget: (pos:
|
|
567
|
+
moveTarget: (pos: IkTargetPosition, duration?: number) => void;
|
|
568
568
|
syncTargetToSite: () => void;
|
|
569
569
|
solveIK: (input: IkSolveInput) => number[] | null;
|
|
570
570
|
getGizmoStats: () => { pos: THREE.Vector3; rot: THREE.Euler } | null;
|
|
@@ -592,9 +592,28 @@ export type IKSolveFn = (
|
|
|
592
592
|
input: IkSolveInput
|
|
593
593
|
) => number[] | null;
|
|
594
594
|
|
|
595
|
+
export type IkTargetPosition =
|
|
596
|
+
| THREE.Vector3
|
|
597
|
+
| readonly [number, number, number]
|
|
598
|
+
| {
|
|
599
|
+
readonly x: number;
|
|
600
|
+
readonly y: number;
|
|
601
|
+
readonly z: number;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
export type IkTargetQuaternion =
|
|
605
|
+
| THREE.Quaternion
|
|
606
|
+
| readonly [number, number, number, number]
|
|
607
|
+
| {
|
|
608
|
+
readonly x: number;
|
|
609
|
+
readonly y: number;
|
|
610
|
+
readonly z: number;
|
|
611
|
+
readonly w: number;
|
|
612
|
+
};
|
|
613
|
+
|
|
595
614
|
export interface IkSolveInput {
|
|
596
|
-
position:
|
|
597
|
-
quaternion:
|
|
615
|
+
position: IkTargetPosition;
|
|
616
|
+
quaternion: IkTargetQuaternion;
|
|
598
617
|
currentQ: number[];
|
|
599
618
|
context?: IKSolveContext;
|
|
600
619
|
}
|