mujoco-react 9.0.0 → 9.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 CHANGED
@@ -983,6 +983,20 @@ const video = useVideoRecorder({ fps: 30, mimeType: "video/webm" });
983
983
  // video.start(), video.stop() -> returns Blob
984
984
  ```
985
985
 
986
+ ### Frame Capture
987
+
988
+ Capture dataset/debug stills from the rendered MuJoCo canvas:
989
+
990
+ ```tsx
991
+ const apiRef = useRef<MujocoSimAPI>(null);
992
+
993
+ const frame = await apiRef.current?.captureFrame({ type: "image/png" });
994
+ const blob = await apiRef.current?.captureFrameBlob({ type: "image/png" });
995
+ ```
996
+
997
+ Use `useFrameCapture()` or the standalone `captureFrame()` helpers when you own
998
+ the canvas or want to capture a custom container.
999
+
986
1000
  ### `useCtrlNoise(config)`
987
1001
 
988
1002
  Apply Gaussian noise to controls for robustness testing:
@@ -1104,7 +1118,10 @@ The full API object available via `ref` or `useMujoco()` (when `isReady`):
1104
1118
  |--------|-------------|
1105
1119
  | `raycast(origin, direction, maxDist?)` | Physics raycast via `mj_ray` |
1106
1120
  | `project2DTo3D(x, y, camPos, lookAt)` | Screen-to-world raycast (returns bodyId + geomId) |
1121
+ | `getCanvas()` | Return the underlying R3F canvas element |
1107
1122
  | `getCanvasSnapshot(w?, h?, mime?)` | Base64 screenshot |
1123
+ | `captureFrame(options?)` | Capture the canvas as a data URL |
1124
+ | `captureFrameBlob(options?)` | Capture the canvas as a Blob |
1108
1125
 
1109
1126
  ### Scene Management
1110
1127
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { M as MujocoContextValue, a as MujocoCanvasProps, b as MujocoSimAPI, S as SceneConfig, R as ReadyCallbackInput, c as StepCallbackInput, d as SelectionCallbackInput, e as MujocoModule, P as PhysicsStepCallback, f as MujocoModel, g as MujocoData, C as ControlGroupInfo, A as ActuatedJointInfo, h as ControlGroupSelector, O as ObservationConfig, i as ObservationResult, I as IkConfig, j as IkContextValue, B as BodyProps, k as IkGizmoProps, D as DragInteractionProps, l as SceneLightsProps, m as ScenarioLightingProps, n as SplatEnvironmentProps, V as VisualScenarioEffectsProps, o as VisualScenarioConfig, p as SplatRendererKind, q as PairedSplatEnvironmentConfig, r as SplatFormat, s as SplatCollisionProxyConfig, t as SplatCollisionPrimitive, u as ScenarioLightingPreset, v as SplatEnvironmentMetadataInput, w as SplatEnvironmentMetadata, x as SplatSceneInput, y as DebugProps, G as GeomInfo, z as ContactListenerProps, T as TrajectoryPlayerProps, E as ActuatorInfo, F as Sites, H as SitePositionResult, J as Sensors, K as SensorHandle, L as SensorInfo, N as Joints, Q as JointStateResult, U as Bodies, W as BodyStateResult, X as Actuators, Y as CtrlHandle, Z as ContactInfo, _ as KeyboardTeleopConfig, $ as PolicyConfig, a0 as PolicyVector, a1 as ObservationHandle, a2 as TrajectoryInput, a3 as TrajectoryStateChangeInput, a4 as PlaybackState, a5 as TrajectoryFrame } from './types-izZlUweI.js';
3
- export { a6 as BodyInfo, a7 as ControlJointInfo, a8 as Geoms, a9 as IKSolveFn, aa as IkGizmoDragInput, ab as IkSolveInput, ac as JointInfo, ad as KeyBinding, ae as Keyframes, af as ModelOptions, ag as MujocoContact, ah as MujocoContactArray, ai as ObservationLayoutItem, aj as ObservationOutput, ak as PhysicsConfig, al as PhysicsStepInput, am as PolicyActionInput, an as PolicyInferenceInput, ao as PolicyObservationInput, ap as RayHit, aq as Register, ar as RegisteredRobotMap, as as ResetCallbackInput, at as ResourceSelector, au as RobotActuators, av as RobotBodies, aw as RobotGeoms, ax as RobotJoints, ay as RobotKeyframes, az as RobotResource, aA as RobotResources, aB as RobotSensors, aC as RobotSites, aD as Robots, aE as ScenarioCameraConfig, aF as ScenarioMaterialConfig, aG as SceneMarker, aH as SceneObject, aI as SensorResult, aJ as SiteInfo, aK as SplatAssetConfig, aL as SplatScenarioConfig, aM as StateSnapshot, aN as TrajectoryData, aO as TrajectoryFrameCallbackInput, aP as VisualScenarioMaterialFilterInput, aQ as XmlPatch, aR as getContact, aS as registerRobotResources } from './types-izZlUweI.js';
2
+ import { M as MujocoContextValue, a as MujocoCanvasProps, b as MujocoSimAPI, S as SceneConfig, R as ReadyCallbackInput, c as StepCallbackInput, d as SelectionCallbackInput, e as MujocoModule, P as PhysicsStepCallback, f as MujocoModel, g as MujocoData, C as ControlGroupInfo, A as ActuatedJointInfo, h as ControlGroupSelector, O as ObservationConfig, i as ObservationResult, I as IkConfig, j as IkContextValue, B as BodyProps, k as IkGizmoProps, D as DragInteractionProps, l as SceneLightsProps, m as ScenarioLightingProps, n as SplatEnvironmentProps, V as VisualScenarioEffectsProps, o as VisualScenarioConfig, p as SplatRendererKind, q as PairedSplatEnvironmentConfig, r as SplatFormat, s as SplatCollisionProxyConfig, t as SplatCollisionPrimitive, u as ScenarioLightingPreset, v as SplatEnvironmentMetadataInput, w as SplatEnvironmentMetadata, x as SplatSceneInput, y as DebugProps, G as GeomInfo, z as ContactListenerProps, T as TrajectoryPlayerProps, E as ActuatorInfo, F as Sites, H as SitePositionResult, J as Sensors, K as SensorHandle, L as SensorInfo, N as Joints, Q as JointStateResult, U as Bodies, W as BodyStateResult, X as Actuators, Y as CtrlHandle, Z as ContactInfo, _ as KeyboardTeleopConfig, $ as PolicyConfig, a0 as PolicyVector, a1 as ObservationHandle, a2 as TrajectoryInput, a3 as TrajectoryStateChangeInput, a4 as PlaybackState, a5 as TrajectoryFrame, a6 as FrameCaptureOptions, a7 as FrameCaptureResult, a8 as FrameCaptureBlobResult, a9 as FrameCaptureAPI } from './types-C5gTvR7b.js';
3
+ export { aa as BodyInfo, ab as ControlJointInfo, ac as FrameCaptureStatus, ad as FrameCaptureTarget, ae as FrameCaptureTargetRef, af as Geoms, ag as IKSolveFn, ah as IkGizmoDragInput, ai as IkSolveInput, aj as JointInfo, ak as KeyBinding, al as Keyframes, am as ModelOptions, an as MujocoContact, ao as MujocoContactArray, ap as MujocoFrameCaptureOptions, aq as ObservationLayoutItem, ar as ObservationOutput, as as PhysicsConfig, at as PhysicsStepInput, au as PolicyActionInput, av as PolicyInferenceInput, aw as PolicyObservationInput, ax as RayHit, ay as Register, az as RegisteredRobotMap, aA as ResetCallbackInput, aB as ResourceSelector, aC as RobotActuators, aD as RobotBodies, aE as RobotGeoms, aF as RobotJoints, aG as RobotKeyframes, aH as RobotResource, aI as RobotResources, aJ as RobotSensors, aK as RobotSites, aL as Robots, aM as ScenarioCameraConfig, aN as ScenarioMaterialConfig, aO as SceneMarker, aP as SceneObject, aQ as SensorResult, aR as SiteInfo, aS as SplatAssetConfig, aT as SplatScenarioConfig, aU as StateSnapshot, aV as TrajectoryData, aW as TrajectoryFrameCallbackInput, aX as VisualScenarioMaterialFilterInput, aY as XmlPatch, aZ as getContact, a_ as registerRobotResources } from './types-C5gTvR7b.js';
4
4
  import * as React$1 from 'react';
5
- import React__default from 'react';
6
5
  import { ThreeElements } from '@react-three/fiber';
7
6
  import * as THREE from 'three';
8
7
 
@@ -758,33 +757,6 @@ declare function useVideoRecorder(options?: VideoRecorderOptions): {
758
757
  * useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
759
758
  */
760
759
 
761
- type FrameCaptureStatus = 'idle' | 'capturing' | 'captured' | 'error';
762
- type FrameCaptureTarget = HTMLCanvasElement | HTMLElement | null | undefined;
763
- type FrameCaptureTargetRef = React__default.RefObject<HTMLCanvasElement | HTMLElement | null>;
764
- interface FrameCaptureOptions {
765
- target?: FrameCaptureTarget | FrameCaptureTargetRef;
766
- type?: string;
767
- quality?: number;
768
- waitForAnimationFrame?: boolean;
769
- }
770
- interface FrameCaptureResult {
771
- canvas: HTMLCanvasElement;
772
- dataUrl: string;
773
- type: string;
774
- }
775
- interface FrameCaptureBlobResult {
776
- canvas: HTMLCanvasElement;
777
- blob: Blob;
778
- type: string;
779
- }
780
- interface FrameCaptureAPI {
781
- status: FrameCaptureStatus;
782
- error: Error | null;
783
- isCapturing: boolean;
784
- capture: (options?: FrameCaptureOptions) => Promise<FrameCaptureResult>;
785
- captureBlob: (options?: FrameCaptureOptions) => Promise<FrameCaptureBlobResult>;
786
- reset: () => void;
787
- }
788
760
  /**
789
761
  * Capture the current canvas frame as a data URL.
790
762
  *
@@ -896,4 +868,4 @@ interface CameraAnimationAPI {
896
868
  */
897
869
  declare function useCameraAnimation(): CameraAnimationAPI;
898
870
 
899
- export { ActuatedJointInfo, ActuatorInfo, Actuators, Bodies, Body, BodyProps, BodyStateResult, type CameraAnimationAPI, ContactInfo, ContactListener, ContactListenerProps, ContactMarkers, ControlGroupInfo, ControlGroupSelector, type ControllerComponent, type ControllerOptions, CtrlHandle, Debug, DebugProps, DragInteraction, DragInteractionProps, FlexRenderer, type FrameCaptureAPI, type FrameCaptureBlobResult, type FrameCaptureOptions, type FrameCaptureResult, type FrameCaptureStatus, type FrameCaptureTarget, type FrameCaptureTargetRef, GeomInfo, IkConfig, IkContextValue, IkGizmo, IkGizmoProps, InstancedGeomRenderer, JointStateResult, Joints, KeyboardTeleopConfig, MujocoCanvas, MujocoCanvasProps, MujocoContextValue, MujocoData, type MujocoLoader, type MujocoLoaderOptions, MujocoModel, MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, ObservationConfig, ObservationHandle, ObservationResult, PairedSplatEnvironmentConfig, PhysicsStepCallback, PlaybackState, PolicyConfig, PolicyVector, ReadyCallbackInput, ScenarioLighting, ScenarioLightingPreset, ScenarioLightingProps, SceneConfig, SceneLights, SceneLightsProps, SelectionCallbackInput, SensorHandle, SensorInfo, Sensors, SitePositionResult, Sites, SplatCollisionPrimitive, SplatCollisionProxyConfig, SplatEnvironment, SplatEnvironmentMetadata, SplatEnvironmentMetadataInput, SplatEnvironmentProps, SplatFormat, SplatRendererKind, SplatSceneInput, StepCallbackInput, TendonRenderer, TrajectoryFrame, TrajectoryInput, TrajectoryPlayer, TrajectoryPlayerProps, TrajectoryStateChangeInput, VisualScenarioConfig, VisualScenarioEffects, VisualScenarioEffectsProps, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getControlMap, getName, getScenarioBackground, getScenarioCameraPosition, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatEnvironment, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder, useVisualScenarioEffects, withSplatEnvironment };
871
+ export { ActuatedJointInfo, ActuatorInfo, Actuators, Bodies, Body, BodyProps, BodyStateResult, type CameraAnimationAPI, ContactInfo, ContactListener, ContactListenerProps, ContactMarkers, ControlGroupInfo, ControlGroupSelector, type ControllerComponent, type ControllerOptions, CtrlHandle, Debug, DebugProps, DragInteraction, DragInteractionProps, FlexRenderer, FrameCaptureAPI, FrameCaptureBlobResult, FrameCaptureOptions, FrameCaptureResult, GeomInfo, IkConfig, IkContextValue, IkGizmo, IkGizmoProps, InstancedGeomRenderer, JointStateResult, Joints, KeyboardTeleopConfig, MujocoCanvas, MujocoCanvasProps, MujocoContextValue, MujocoData, type MujocoLoader, type MujocoLoaderOptions, MujocoModel, MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, ObservationConfig, ObservationHandle, ObservationResult, PairedSplatEnvironmentConfig, PhysicsStepCallback, PlaybackState, PolicyConfig, PolicyVector, ReadyCallbackInput, ScenarioLighting, ScenarioLightingPreset, ScenarioLightingProps, SceneConfig, SceneLights, SceneLightsProps, SelectionCallbackInput, SensorHandle, SensorInfo, Sensors, SitePositionResult, Sites, SplatCollisionPrimitive, SplatCollisionProxyConfig, SplatEnvironment, SplatEnvironmentMetadata, SplatEnvironmentMetadataInput, SplatEnvironmentProps, SplatFormat, SplatRendererKind, SplatSceneInput, StepCallbackInput, TendonRenderer, TrajectoryFrame, TrajectoryInput, TrajectoryPlayer, TrajectoryPlayerProps, TrajectoryStateChangeInput, VisualScenarioConfig, VisualScenarioEffects, VisualScenarioEffectsProps, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getControlMap, getName, getScenarioBackground, getScenarioCameraPosition, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatEnvironment, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder, useVisualScenarioEffects, withSplatEnvironment };
package/dist/index.js CHANGED
@@ -1294,6 +1294,114 @@ function SceneRenderer(props) {
1294
1294
  }
1295
1295
  var _previousQuat = new THREE11.Quaternion();
1296
1296
  var _currentQuat = new THREE11.Quaternion();
1297
+ function isTargetRef(target) {
1298
+ return Boolean(target && typeof target === "object" && "current" in target);
1299
+ }
1300
+ function resolveCanvasTarget(target) {
1301
+ const resolvedTarget = isTargetRef(target) ? target.current : target;
1302
+ if (!resolvedTarget) {
1303
+ throw new Error("No frame capture target is available.");
1304
+ }
1305
+ if (resolvedTarget instanceof HTMLCanvasElement) {
1306
+ return resolvedTarget;
1307
+ }
1308
+ const canvas = resolvedTarget.querySelector("canvas");
1309
+ if (!canvas) {
1310
+ throw new Error("Frame capture target does not contain a canvas.");
1311
+ }
1312
+ return canvas;
1313
+ }
1314
+ function waitForNextAnimationFrame() {
1315
+ return new Promise((resolve) => {
1316
+ requestAnimationFrame(() => resolve());
1317
+ });
1318
+ }
1319
+ async function captureFrame(options) {
1320
+ const type = options.type ?? "image/png";
1321
+ const canvas = resolveCanvasTarget(options.target);
1322
+ if (options.waitForAnimationFrame ?? true) {
1323
+ await waitForNextAnimationFrame();
1324
+ }
1325
+ return {
1326
+ canvas,
1327
+ dataUrl: canvas.toDataURL(type, options.quality),
1328
+ type
1329
+ };
1330
+ }
1331
+ async function captureFrameBlob(options) {
1332
+ const type = options.type ?? "image/png";
1333
+ const canvas = resolveCanvasTarget(options.target);
1334
+ if (options.waitForAnimationFrame ?? true) {
1335
+ await waitForNextAnimationFrame();
1336
+ }
1337
+ const blob = await new Promise((resolve, reject) => {
1338
+ canvas.toBlob(
1339
+ (nextBlob) => {
1340
+ if (nextBlob) {
1341
+ resolve(nextBlob);
1342
+ } else {
1343
+ reject(new Error("Canvas frame capture did not produce a Blob."));
1344
+ }
1345
+ },
1346
+ type,
1347
+ options.quality
1348
+ );
1349
+ });
1350
+ return { canvas, blob, type };
1351
+ }
1352
+ function useFrameCapture(defaultOptions = {}) {
1353
+ const [status, setStatus] = useState("idle");
1354
+ const [error, setError] = useState(null);
1355
+ const reset = useCallback(() => {
1356
+ setStatus("idle");
1357
+ setError(null);
1358
+ }, []);
1359
+ const capture = useCallback(
1360
+ async (options = {}) => {
1361
+ setStatus("capturing");
1362
+ setError(null);
1363
+ try {
1364
+ const result = await captureFrame({ ...defaultOptions, ...options });
1365
+ setStatus("captured");
1366
+ return result;
1367
+ } catch (nextError) {
1368
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
1369
+ setError(error2);
1370
+ setStatus("error");
1371
+ throw error2;
1372
+ }
1373
+ },
1374
+ [defaultOptions]
1375
+ );
1376
+ const captureBlob = useCallback(
1377
+ async (options = {}) => {
1378
+ setStatus("capturing");
1379
+ setError(null);
1380
+ try {
1381
+ const result = await captureFrameBlob({
1382
+ ...defaultOptions,
1383
+ ...options
1384
+ });
1385
+ setStatus("captured");
1386
+ return result;
1387
+ } catch (nextError) {
1388
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
1389
+ setError(error2);
1390
+ setStatus("error");
1391
+ throw error2;
1392
+ }
1393
+ },
1394
+ [defaultOptions]
1395
+ );
1396
+ return {
1397
+ status,
1398
+ error,
1399
+ isCapturing: status === "capturing",
1400
+ capture,
1401
+ captureBlob,
1402
+ reset
1403
+ };
1404
+ }
1297
1405
  var JOINT_TYPE_NAMES2 = ["free", "ball", "slide", "hinge"];
1298
1406
  var GEOM_TYPE_NAMES = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
1299
1407
  var SENSOR_TYPE_NAMES = {
@@ -2145,6 +2253,21 @@ function MujocoSimProvider({
2145
2253
  },
2146
2254
  [gl]
2147
2255
  );
2256
+ const getCanvas = useCallback(() => {
2257
+ return gl.domElement ?? null;
2258
+ }, [gl]);
2259
+ const captureFrameApi = useCallback(
2260
+ (options = {}) => {
2261
+ return captureFrame({ ...options, target: gl.domElement });
2262
+ },
2263
+ [gl]
2264
+ );
2265
+ const captureFrameBlobApi = useCallback(
2266
+ (options = {}) => {
2267
+ return captureFrameBlob({ ...options, target: gl.domElement });
2268
+ },
2269
+ [gl]
2270
+ );
2148
2271
  const project2DTo3D = useCallback(
2149
2272
  (x, y, cameraPos, lookAt) => {
2150
2273
  const virtCam = camera.clone();
@@ -2251,7 +2374,10 @@ function MujocoSimProvider({
2251
2374
  addBody: addBodyApi,
2252
2375
  removeBody: removeBodyApi,
2253
2376
  recompile: recompileApi,
2377
+ getCanvas,
2254
2378
  getCanvasSnapshot,
2379
+ captureFrame: captureFrameApi,
2380
+ captureFrameBlob: captureFrameBlobApi,
2255
2381
  project2DTo3D,
2256
2382
  setBodyMass,
2257
2383
  setGeomFriction,
@@ -2303,7 +2429,10 @@ function MujocoSimProvider({
2303
2429
  addBodyApi,
2304
2430
  removeBodyApi,
2305
2431
  recompileApi,
2432
+ getCanvas,
2306
2433
  getCanvasSnapshot,
2434
+ captureFrameApi,
2435
+ captureFrameBlobApi,
2307
2436
  project2DTo3D,
2308
2437
  setBodyMass,
2309
2438
  setGeomFriction,
@@ -4979,114 +5108,6 @@ function useVideoRecorder(options = {}) {
4979
5108
  }
4980
5109
  };
4981
5110
  }
4982
- function isTargetRef(target) {
4983
- return Boolean(target && typeof target === "object" && "current" in target);
4984
- }
4985
- function resolveCanvasTarget(target) {
4986
- const resolvedTarget = isTargetRef(target) ? target.current : target;
4987
- if (!resolvedTarget) {
4988
- throw new Error("No frame capture target is available.");
4989
- }
4990
- if (resolvedTarget instanceof HTMLCanvasElement) {
4991
- return resolvedTarget;
4992
- }
4993
- const canvas = resolvedTarget.querySelector("canvas");
4994
- if (!canvas) {
4995
- throw new Error("Frame capture target does not contain a canvas.");
4996
- }
4997
- return canvas;
4998
- }
4999
- function waitForNextAnimationFrame() {
5000
- return new Promise((resolve) => {
5001
- requestAnimationFrame(() => resolve());
5002
- });
5003
- }
5004
- async function captureFrame(options) {
5005
- const type = options.type ?? "image/png";
5006
- const canvas = resolveCanvasTarget(options.target);
5007
- if (options.waitForAnimationFrame ?? true) {
5008
- await waitForNextAnimationFrame();
5009
- }
5010
- return {
5011
- canvas,
5012
- dataUrl: canvas.toDataURL(type, options.quality),
5013
- type
5014
- };
5015
- }
5016
- async function captureFrameBlob(options) {
5017
- const type = options.type ?? "image/png";
5018
- const canvas = resolveCanvasTarget(options.target);
5019
- if (options.waitForAnimationFrame ?? true) {
5020
- await waitForNextAnimationFrame();
5021
- }
5022
- const blob = await new Promise((resolve, reject) => {
5023
- canvas.toBlob(
5024
- (nextBlob) => {
5025
- if (nextBlob) {
5026
- resolve(nextBlob);
5027
- } else {
5028
- reject(new Error("Canvas frame capture did not produce a Blob."));
5029
- }
5030
- },
5031
- type,
5032
- options.quality
5033
- );
5034
- });
5035
- return { canvas, blob, type };
5036
- }
5037
- function useFrameCapture(defaultOptions = {}) {
5038
- const [status, setStatus] = useState("idle");
5039
- const [error, setError] = useState(null);
5040
- const reset = useCallback(() => {
5041
- setStatus("idle");
5042
- setError(null);
5043
- }, []);
5044
- const capture = useCallback(
5045
- async (options = {}) => {
5046
- setStatus("capturing");
5047
- setError(null);
5048
- try {
5049
- const result = await captureFrame({ ...defaultOptions, ...options });
5050
- setStatus("captured");
5051
- return result;
5052
- } catch (nextError) {
5053
- const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
5054
- setError(error2);
5055
- setStatus("error");
5056
- throw error2;
5057
- }
5058
- },
5059
- [defaultOptions]
5060
- );
5061
- const captureBlob = useCallback(
5062
- async (options = {}) => {
5063
- setStatus("capturing");
5064
- setError(null);
5065
- try {
5066
- const result = await captureFrameBlob({
5067
- ...defaultOptions,
5068
- ...options
5069
- });
5070
- setStatus("captured");
5071
- return result;
5072
- } catch (nextError) {
5073
- const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
5074
- setError(error2);
5075
- setStatus("error");
5076
- throw error2;
5077
- }
5078
- },
5079
- [defaultOptions]
5080
- );
5081
- return {
5082
- status,
5083
- error,
5084
- isCapturing: status === "capturing",
5085
- capture,
5086
- captureBlob,
5087
- reset
5088
- };
5089
- }
5090
5111
  function useCtrlNoise(config = {}) {
5091
5112
  const { mjModelRef } = useMujocoContext();
5092
5113
  const configRef = useRef(config);
@@ -5239,6 +5260,12 @@ function useCameraAnimation() {
5239
5260
  * @license
5240
5261
  * SPDX-License-Identifier: Apache-2.0
5241
5262
  */
5263
+ /**
5264
+ * @license
5265
+ * SPDX-License-Identifier: Apache-2.0
5266
+ *
5267
+ * useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
5268
+ */
5242
5269
  /**
5243
5270
  * @license
5244
5271
  * SPDX-License-Identifier: Apache-2.0
@@ -5372,12 +5399,6 @@ function useCameraAnimation() {
5372
5399
  *
5373
5400
  * useVideoRecorder — canvas video recording hook (spec 13.3)
5374
5401
  */
5375
- /**
5376
- * @license
5377
- * SPDX-License-Identifier: Apache-2.0
5378
- *
5379
- * useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
5380
- */
5381
5402
  /**
5382
5403
  * @license
5383
5404
  * SPDX-License-Identifier: Apache-2.0