mujoco-react 9.3.0 → 9.5.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 +155 -15
- package/dist/{chunk-T3GVZJ4F.js → chunk-6MOK6ZWB.js} +501 -33
- package/dist/chunk-6MOK6ZWB.js.map +1 -0
- package/dist/index.d.ts +129 -7
- package/dist/index.js +846 -437
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +27 -3
- package/dist/spark.js +156 -3
- package/dist/spark.js.map +1 -1
- package/dist/{types-oxbxOkAx.d.ts → types-BDB9QT6Z.d.ts} +42 -3
- package/dist/vite.js +8 -4
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ContactMarkers.tsx +8 -1
- package/src/components/Debug.tsx +154 -3
- package/src/components/DragInteraction.tsx +2 -0
- package/src/components/IkGizmo.tsx +5 -1
- package/src/components/SplatCollisionProxyPreview.tsx +350 -0
- package/src/components/VisualScenario.tsx +140 -41
- package/src/core/MujocoSimProvider.tsx +6 -6
- package/src/hooks/useMountedCameraSequenceRecorder.ts +54 -6
- package/src/index.ts +32 -0
- package/src/rendering/cameraFrameCapture.ts +259 -28
- package/src/rendering/cameraFrameSource.ts +382 -2
- package/src/spark.tsx +241 -1
- package/src/types.ts +45 -2
- package/src/vite.ts +9 -4
- package/dist/chunk-T3GVZJ4F.js.map +0 -1
|
@@ -56,6 +56,27 @@ export interface ResolvedMountedCameraFrameSource {
|
|
|
56
56
|
source: MountedCameraFrameCaptureSource;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export const MountedCameraFrameSourceSuggestionMatch = {
|
|
60
|
+
Direct: 'direct',
|
|
61
|
+
Alias: 'alias',
|
|
62
|
+
Normalized: 'normalized',
|
|
63
|
+
Prefix: 'prefix',
|
|
64
|
+
Suffix: 'suffix',
|
|
65
|
+
Contains: 'contains',
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
export type MountedCameraFrameSourceSuggestionMatch =
|
|
69
|
+
(typeof MountedCameraFrameSourceSuggestionMatch)[keyof typeof MountedCameraFrameSourceSuggestionMatch];
|
|
70
|
+
|
|
71
|
+
export interface MountedCameraFrameSourceSuggestion {
|
|
72
|
+
key: string;
|
|
73
|
+
selector: CameraFrameMountSelector;
|
|
74
|
+
source: MountedCameraFrameCaptureSource;
|
|
75
|
+
resourceName: string;
|
|
76
|
+
resourceKind: MountedCameraFrameCaptureSource['kind'];
|
|
77
|
+
match: MountedCameraFrameSourceSuggestionMatch;
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
export type MountedCameraFrameSequenceDefaults = Omit<
|
|
60
81
|
CameraFrameSequenceCamera,
|
|
61
82
|
'key' | 'cameraName' | 'siteName' | 'bodyName' | 'source'
|
|
@@ -123,6 +144,57 @@ export interface MountedCameraFrameSequenceRecordResult
|
|
|
123
144
|
readiness: MountedCameraFrameSequenceReadiness;
|
|
124
145
|
}
|
|
125
146
|
|
|
147
|
+
export const MountedCameraFrameSequenceManifestStatus = {
|
|
148
|
+
Complete: 'complete',
|
|
149
|
+
Partial: 'partial',
|
|
150
|
+
Missing: 'missing',
|
|
151
|
+
} as const;
|
|
152
|
+
|
|
153
|
+
export type MountedCameraFrameSequenceManifestStatus =
|
|
154
|
+
(typeof MountedCameraFrameSequenceManifestStatus)[keyof typeof MountedCameraFrameSequenceManifestStatus];
|
|
155
|
+
|
|
156
|
+
export interface MountedCameraFrameSequenceStreamSummary {
|
|
157
|
+
key: string;
|
|
158
|
+
ready: boolean;
|
|
159
|
+
complete: boolean;
|
|
160
|
+
status: MountedCameraFrameSequenceManifestStatus;
|
|
161
|
+
source?: CameraFrameCaptureSource;
|
|
162
|
+
selector?: CameraFrameMountSelector;
|
|
163
|
+
target?: string;
|
|
164
|
+
width?: number;
|
|
165
|
+
height?: number;
|
|
166
|
+
expectedFrameCount: number;
|
|
167
|
+
recordedFrameCount: number;
|
|
168
|
+
missingFrameCount: number;
|
|
169
|
+
firstFrameIndex: number | null;
|
|
170
|
+
lastFrameIndex: number | null;
|
|
171
|
+
firstTimestamp: number | null;
|
|
172
|
+
lastTimestamp: number | null;
|
|
173
|
+
message: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface CreateMountedCameraFrameSequenceManifestOptions {
|
|
177
|
+
expectedFrameCount?: number;
|
|
178
|
+
cameraKeys?: readonly string[];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface MountedCameraFrameSequenceManifest {
|
|
182
|
+
schema: 'mujoco-react/mounted-camera-frame-sequence-manifest@1';
|
|
183
|
+
ready: boolean;
|
|
184
|
+
complete: boolean;
|
|
185
|
+
status: MountedCameraFrameSequenceManifestStatus;
|
|
186
|
+
cameraKeys: string[];
|
|
187
|
+
resolvedKeys: string[];
|
|
188
|
+
missingKeys: string[];
|
|
189
|
+
expectedFrameCount: number;
|
|
190
|
+
recordedFrameCount: number;
|
|
191
|
+
missingFrameCount: number;
|
|
192
|
+
streamSummaries: Record<string, MountedCameraFrameSequenceStreamSummary>;
|
|
193
|
+
streams: MountedCameraFrameSequenceStreamSummary[];
|
|
194
|
+
readiness: MountedCameraFrameSequenceReadiness;
|
|
195
|
+
message: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
126
198
|
export type MountedCameraFrameSequenceRecorderTarget = Pick<
|
|
127
199
|
MujocoSimAPI,
|
|
128
200
|
'getCameras' | 'getSites' | 'getBodies' | 'recordCameraSequence'
|
|
@@ -147,6 +219,16 @@ function createNameSet(
|
|
|
147
219
|
);
|
|
148
220
|
}
|
|
149
221
|
|
|
222
|
+
function createResourceNames(
|
|
223
|
+
resources:
|
|
224
|
+
| readonly (string | NamedCameraFrameResource | null | undefined)[]
|
|
225
|
+
| undefined
|
|
226
|
+
) {
|
|
227
|
+
return (resources ?? [])
|
|
228
|
+
.map((resource) => getResourceName(resource))
|
|
229
|
+
.filter((name): name is string => Boolean(name));
|
|
230
|
+
}
|
|
231
|
+
|
|
150
232
|
function normalizeAliasCandidates(
|
|
151
233
|
value: CameraFrameMountSelector | readonly CameraFrameMountSelector[] | undefined
|
|
152
234
|
) {
|
|
@@ -160,6 +242,35 @@ function countMountedSelectors(selector: CameraFrameMountSelector) {
|
|
|
160
242
|
Number(Boolean(selector.bodyName));
|
|
161
243
|
}
|
|
162
244
|
|
|
245
|
+
function normalizeCameraSourceName(value: string) {
|
|
246
|
+
return value
|
|
247
|
+
.trim()
|
|
248
|
+
.toLowerCase()
|
|
249
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
250
|
+
.replace(/^_+|_+$/g, '');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function createCameraSourceKeyVariants(key: string) {
|
|
254
|
+
const candidates = [
|
|
255
|
+
key,
|
|
256
|
+
key.startsWith('observation.images.')
|
|
257
|
+
? key.slice('observation.images.'.length)
|
|
258
|
+
: '',
|
|
259
|
+
key.includes('.') ? key.split('.').at(-1) ?? '' : '',
|
|
260
|
+
key.includes('/') ? key.split('/').at(-1) ?? '' : '',
|
|
261
|
+
];
|
|
262
|
+
return candidates
|
|
263
|
+
.map((candidate) => candidate.trim())
|
|
264
|
+
.filter((candidate, index, items) => candidate && items.indexOf(candidate) === index);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getSelectorKey(selector: CameraFrameMountSelector) {
|
|
268
|
+
if (selector.cameraName) return `camera:${selector.cameraName}`;
|
|
269
|
+
if (selector.siteName) return `site:${selector.siteName}`;
|
|
270
|
+
if (selector.bodyName) return `body:${selector.bodyName}`;
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
163
274
|
export function getMountedCameraFrameCaptureSource(
|
|
164
275
|
selector: CameraFrameMountSelector
|
|
165
276
|
): MountedCameraFrameCaptureSource | null {
|
|
@@ -197,6 +308,66 @@ export function getCameraFrameCaptureSourceTarget(
|
|
|
197
308
|
return 'fallback camera';
|
|
198
309
|
}
|
|
199
310
|
|
|
311
|
+
function createMountedCameraFrameSourceSuggestion(
|
|
312
|
+
key: string,
|
|
313
|
+
selector: CameraFrameMountSelector,
|
|
314
|
+
resourceName: string,
|
|
315
|
+
match: MountedCameraFrameSourceSuggestionMatch
|
|
316
|
+
): MountedCameraFrameSourceSuggestion | null {
|
|
317
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
318
|
+
if (!source) return null;
|
|
319
|
+
return {
|
|
320
|
+
key,
|
|
321
|
+
selector,
|
|
322
|
+
source,
|
|
323
|
+
resourceName,
|
|
324
|
+
resourceKind: source.kind,
|
|
325
|
+
match,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function addMountedCameraFrameSourceSuggestion(
|
|
330
|
+
suggestions: MountedCameraFrameSourceSuggestion[],
|
|
331
|
+
seen: Set<string>,
|
|
332
|
+
suggestion: MountedCameraFrameSourceSuggestion | null
|
|
333
|
+
) {
|
|
334
|
+
if (!suggestion) return;
|
|
335
|
+
const selectorKey = getSelectorKey(suggestion.selector);
|
|
336
|
+
if (!selectorKey || seen.has(selectorKey)) return;
|
|
337
|
+
seen.add(selectorKey);
|
|
338
|
+
suggestions.push(suggestion);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function getCameraFrameResourceMatch(
|
|
342
|
+
key: string,
|
|
343
|
+
resourceName: string
|
|
344
|
+
): MountedCameraFrameSourceSuggestionMatch | null {
|
|
345
|
+
if (resourceName === key) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
346
|
+
|
|
347
|
+
const normalizedResource = normalizeCameraSourceName(resourceName);
|
|
348
|
+
if (!normalizedResource) return null;
|
|
349
|
+
|
|
350
|
+
for (const variant of createCameraSourceKeyVariants(key)) {
|
|
351
|
+
if (resourceName === variant) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
352
|
+
|
|
353
|
+
const normalizedKey = normalizeCameraSourceName(variant);
|
|
354
|
+
if (!normalizedKey) continue;
|
|
355
|
+
if (normalizedResource === normalizedKey) {
|
|
356
|
+
return MountedCameraFrameSourceSuggestionMatch.Normalized;
|
|
357
|
+
}
|
|
358
|
+
if (normalizedResource.startsWith(`${normalizedKey}_`)) {
|
|
359
|
+
return MountedCameraFrameSourceSuggestionMatch.Prefix;
|
|
360
|
+
}
|
|
361
|
+
if (normalizedResource.endsWith(`_${normalizedKey}`)) {
|
|
362
|
+
return MountedCameraFrameSourceSuggestionMatch.Suffix;
|
|
363
|
+
}
|
|
364
|
+
if (normalizedResource.includes(`_${normalizedKey}_`)) {
|
|
365
|
+
return MountedCameraFrameSourceSuggestionMatch.Contains;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
200
371
|
function isSelectorMounted(
|
|
201
372
|
selector: CameraFrameMountSelector,
|
|
202
373
|
cameraNames: Set<string>,
|
|
@@ -211,6 +382,82 @@ function isSelectorMounted(
|
|
|
211
382
|
);
|
|
212
383
|
}
|
|
213
384
|
|
|
385
|
+
export function createMountedCameraFrameSourceSuggestions(
|
|
386
|
+
key: string,
|
|
387
|
+
options: ResolveMountedCameraFrameSourceOptions
|
|
388
|
+
): MountedCameraFrameSourceSuggestion[] {
|
|
389
|
+
const cameraNames = createNameSet(options.cameras);
|
|
390
|
+
const siteNames = createNameSet(options.sites);
|
|
391
|
+
const bodyNames = createNameSet(options.bodies);
|
|
392
|
+
const suggestions: MountedCameraFrameSourceSuggestion[] = [];
|
|
393
|
+
const seen = new Set<string>();
|
|
394
|
+
|
|
395
|
+
for (const selector of normalizeAliasCandidates(options.aliases?.[key])) {
|
|
396
|
+
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
400
|
+
if (!source) continue;
|
|
401
|
+
addMountedCameraFrameSourceSuggestion(
|
|
402
|
+
suggestions,
|
|
403
|
+
seen,
|
|
404
|
+
createMountedCameraFrameSourceSuggestion(
|
|
405
|
+
key,
|
|
406
|
+
selector,
|
|
407
|
+
getCameraFrameCaptureSourceTarget(source),
|
|
408
|
+
MountedCameraFrameSourceSuggestionMatch.Alias
|
|
409
|
+
)
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
for (const cameraName of createResourceNames(options.cameras)) {
|
|
414
|
+
const match = getCameraFrameResourceMatch(key, cameraName);
|
|
415
|
+
if (!match) continue;
|
|
416
|
+
addMountedCameraFrameSourceSuggestion(
|
|
417
|
+
suggestions,
|
|
418
|
+
seen,
|
|
419
|
+
createMountedCameraFrameSourceSuggestion(
|
|
420
|
+
key,
|
|
421
|
+
{ cameraName },
|
|
422
|
+
cameraName,
|
|
423
|
+
match
|
|
424
|
+
)
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
for (const siteName of createResourceNames(options.sites)) {
|
|
429
|
+
const match = getCameraFrameResourceMatch(key, siteName);
|
|
430
|
+
if (!match) continue;
|
|
431
|
+
addMountedCameraFrameSourceSuggestion(
|
|
432
|
+
suggestions,
|
|
433
|
+
seen,
|
|
434
|
+
createMountedCameraFrameSourceSuggestion(
|
|
435
|
+
key,
|
|
436
|
+
{ siteName },
|
|
437
|
+
siteName,
|
|
438
|
+
match
|
|
439
|
+
)
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const bodyName of createResourceNames(options.bodies)) {
|
|
444
|
+
const match = getCameraFrameResourceMatch(key, bodyName);
|
|
445
|
+
if (!match) continue;
|
|
446
|
+
addMountedCameraFrameSourceSuggestion(
|
|
447
|
+
suggestions,
|
|
448
|
+
seen,
|
|
449
|
+
createMountedCameraFrameSourceSuggestion(
|
|
450
|
+
key,
|
|
451
|
+
{ bodyName },
|
|
452
|
+
bodyName,
|
|
453
|
+
match
|
|
454
|
+
)
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return suggestions;
|
|
459
|
+
}
|
|
460
|
+
|
|
214
461
|
export function resolveMountedCameraFrameSource(
|
|
215
462
|
key: string,
|
|
216
463
|
options: ResolveMountedCameraFrameSourceOptions
|
|
@@ -224,9 +471,8 @@ export function resolveMountedCameraFrameSource(
|
|
|
224
471
|
{ bodyName: key },
|
|
225
472
|
];
|
|
226
473
|
const aliasCandidates = normalizeAliasCandidates(options.aliases?.[key]);
|
|
227
|
-
const candidates = [...directCandidates, ...aliasCandidates];
|
|
228
474
|
|
|
229
|
-
for (const selector of
|
|
475
|
+
for (const selector of aliasCandidates) {
|
|
230
476
|
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
231
477
|
continue;
|
|
232
478
|
}
|
|
@@ -235,6 +481,15 @@ export function resolveMountedCameraFrameSource(
|
|
|
235
481
|
return { key, selector, source };
|
|
236
482
|
}
|
|
237
483
|
|
|
484
|
+
const [suggestion] = createMountedCameraFrameSourceSuggestions(key, options);
|
|
485
|
+
if (suggestion) {
|
|
486
|
+
return {
|
|
487
|
+
key,
|
|
488
|
+
selector: suggestion.selector,
|
|
489
|
+
source: suggestion.source,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
238
493
|
if (options.allowAliasFallback) {
|
|
239
494
|
for (const selector of aliasCandidates) {
|
|
240
495
|
const source = getMountedCameraFrameCaptureSource(selector);
|
|
@@ -243,6 +498,15 @@ export function resolveMountedCameraFrameSource(
|
|
|
243
498
|
}
|
|
244
499
|
}
|
|
245
500
|
|
|
501
|
+
for (const selector of directCandidates) {
|
|
502
|
+
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
506
|
+
if (!source) continue;
|
|
507
|
+
return { key, selector, source };
|
|
508
|
+
}
|
|
509
|
+
|
|
246
510
|
return null;
|
|
247
511
|
}
|
|
248
512
|
|
|
@@ -335,6 +599,122 @@ export function createMountedCameraFrameSequenceReadiness(
|
|
|
335
599
|
};
|
|
336
600
|
}
|
|
337
601
|
|
|
602
|
+
function normalizeFrameCount(frameCount: number | undefined) {
|
|
603
|
+
return Number.isFinite(frameCount) && frameCount !== undefined
|
|
604
|
+
? Math.max(0, Math.floor(frameCount))
|
|
605
|
+
: 0;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export function createMountedCameraFrameSequenceManifest(
|
|
609
|
+
result: MountedCameraFrameSequenceRecordResult,
|
|
610
|
+
options: CreateMountedCameraFrameSequenceManifestOptions = {}
|
|
611
|
+
): MountedCameraFrameSequenceManifest {
|
|
612
|
+
const cameraKeys = [
|
|
613
|
+
...(options.cameraKeys ??
|
|
614
|
+
result.readiness.cameraKeys ??
|
|
615
|
+
result.plan.cameraKeys ??
|
|
616
|
+
result.cameraKeys),
|
|
617
|
+
];
|
|
618
|
+
const expectedFrameCount = normalizeFrameCount(
|
|
619
|
+
options.expectedFrameCount ?? result.frameCount
|
|
620
|
+
);
|
|
621
|
+
const recordedFrameCount = normalizeFrameCount(result.frameCount);
|
|
622
|
+
const streamSummaries: Record<string, MountedCameraFrameSequenceStreamSummary> =
|
|
623
|
+
{};
|
|
624
|
+
const streams: MountedCameraFrameSequenceStreamSummary[] = [];
|
|
625
|
+
let missingFrameCount = 0;
|
|
626
|
+
let completeStreamCount = 0;
|
|
627
|
+
let resolvedOrRecordedStreamCount = 0;
|
|
628
|
+
|
|
629
|
+
for (const key of cameraKeys) {
|
|
630
|
+
const summary = result.cameraSummaries[key];
|
|
631
|
+
const readiness = result.readiness.cameras[key];
|
|
632
|
+
const source = summary?.source ?? readiness?.source;
|
|
633
|
+
const ready = readiness?.ready ?? Boolean(summary);
|
|
634
|
+
const recorded = normalizeFrameCount(summary?.frameCount);
|
|
635
|
+
const missing = Math.max(expectedFrameCount - recorded, 0);
|
|
636
|
+
const complete = ready && missing === 0;
|
|
637
|
+
const status = complete
|
|
638
|
+
? MountedCameraFrameSequenceManifestStatus.Complete
|
|
639
|
+
: ready || recorded > 0
|
|
640
|
+
? MountedCameraFrameSequenceManifestStatus.Partial
|
|
641
|
+
: MountedCameraFrameSequenceManifestStatus.Missing;
|
|
642
|
+
const target = source
|
|
643
|
+
? getCameraFrameCaptureSourceTarget(source)
|
|
644
|
+
: readiness?.message
|
|
645
|
+
? undefined
|
|
646
|
+
: 'missing MuJoCo camera';
|
|
647
|
+
const message = complete
|
|
648
|
+
? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${
|
|
649
|
+
expectedFrameCount === 1 ? '' : 's'
|
|
650
|
+
}.`
|
|
651
|
+
: ready || recorded > 0
|
|
652
|
+
? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${
|
|
653
|
+
expectedFrameCount === 1 ? '' : 's'
|
|
654
|
+
}.`
|
|
655
|
+
: readiness?.message ??
|
|
656
|
+
`Camera stream "${key}" did not record any frames.`;
|
|
657
|
+
const stream = {
|
|
658
|
+
key,
|
|
659
|
+
ready,
|
|
660
|
+
complete,
|
|
661
|
+
status,
|
|
662
|
+
source,
|
|
663
|
+
selector: readiness?.selector,
|
|
664
|
+
target,
|
|
665
|
+
width: summary?.width,
|
|
666
|
+
height: summary?.height,
|
|
667
|
+
expectedFrameCount,
|
|
668
|
+
recordedFrameCount: recorded,
|
|
669
|
+
missingFrameCount: missing,
|
|
670
|
+
firstFrameIndex: summary?.firstFrameIndex ?? null,
|
|
671
|
+
lastFrameIndex: summary?.lastFrameIndex ?? null,
|
|
672
|
+
firstTimestamp: summary?.firstTimestamp ?? null,
|
|
673
|
+
lastTimestamp: summary?.lastTimestamp ?? null,
|
|
674
|
+
message,
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
streamSummaries[key] = stream;
|
|
678
|
+
streams.push(stream);
|
|
679
|
+
missingFrameCount += missing;
|
|
680
|
+
if (complete) completeStreamCount += 1;
|
|
681
|
+
if (ready || recorded > 0) resolvedOrRecordedStreamCount += 1;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const complete =
|
|
685
|
+
result.readiness.ready &&
|
|
686
|
+
streams.length === completeStreamCount &&
|
|
687
|
+
missingFrameCount === 0;
|
|
688
|
+
const status = complete
|
|
689
|
+
? MountedCameraFrameSequenceManifestStatus.Complete
|
|
690
|
+
: resolvedOrRecordedStreamCount > 0
|
|
691
|
+
? MountedCameraFrameSequenceManifestStatus.Partial
|
|
692
|
+
: MountedCameraFrameSequenceManifestStatus.Missing;
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
schema: 'mujoco-react/mounted-camera-frame-sequence-manifest@1',
|
|
696
|
+
ready: result.readiness.ready,
|
|
697
|
+
complete,
|
|
698
|
+
status,
|
|
699
|
+
cameraKeys,
|
|
700
|
+
resolvedKeys: [...result.readiness.resolvedKeys],
|
|
701
|
+
missingKeys: [...result.readiness.missingKeys],
|
|
702
|
+
expectedFrameCount,
|
|
703
|
+
recordedFrameCount,
|
|
704
|
+
missingFrameCount,
|
|
705
|
+
streamSummaries,
|
|
706
|
+
streams,
|
|
707
|
+
readiness: result.readiness,
|
|
708
|
+
message: complete
|
|
709
|
+
? `All ${cameraKeys.length} camera stream${
|
|
710
|
+
cameraKeys.length === 1 ? '' : 's'
|
|
711
|
+
} recorded ${expectedFrameCount} frame${
|
|
712
|
+
expectedFrameCount === 1 ? '' : 's'
|
|
713
|
+
}.`
|
|
714
|
+
: `Mounted camera sequence coverage is ${status}.`,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
338
718
|
export function createMountedCameraFrameSequencePlanFromApi(
|
|
339
719
|
api: MountedCameraFrameSequenceRecorderTarget,
|
|
340
720
|
cameraKeys: readonly string[],
|