supervision 0.2.5 → 0.2.7

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 (197) hide show
  1. package/dist/depth-preview-probe-DG7McQEN.js +314 -0
  2. package/dist/depth-preview-probe-DG7McQEN.js.map +1 -0
  3. package/dist/depth-preview-track-BvgwNEE9.js +612 -0
  4. package/dist/depth-preview-track-BvgwNEE9.js.map +1 -0
  5. package/dist/detections/chunked-detection-frame-source.d.ts.map +1 -1
  6. package/dist/editing.d.ts +1 -1
  7. package/dist/editing.d.ts.map +1 -1
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +9845 -4759
  11. package/dist/index.js.map +1 -1
  12. package/dist/mask-preparation.worker.js +788 -25
  13. package/dist/mask-preparation.worker.js.map +1 -1
  14. package/dist/media/decoder-deadline.d.ts +18 -0
  15. package/dist/media/decoder-deadline.d.ts.map +1 -0
  16. package/dist/media/depth-preview-probe.d.ts +65 -0
  17. package/dist/media/depth-preview-probe.d.ts.map +1 -0
  18. package/dist/media/depth-preview-track.d.ts +96 -0
  19. package/dist/media/depth-preview-track.d.ts.map +1 -0
  20. package/dist/media/media-normalization.d.ts.map +1 -1
  21. package/dist/media/media-source.d.ts +7 -0
  22. package/dist/media/media-source.d.ts.map +1 -1
  23. package/dist/media/mediabunny-frame-clock.d.ts +7 -0
  24. package/dist/media/mediabunny-frame-clock.d.ts.map +1 -0
  25. package/dist/media/mediabunny-media-source.d.ts +3 -0
  26. package/dist/media/mediabunny-media-source.d.ts.map +1 -1
  27. package/dist/media/track-frame-index.d.ts +14 -0
  28. package/dist/media/track-frame-index.d.ts.map +1 -0
  29. package/dist/media/video-engine-media-source.d.ts.map +1 -1
  30. package/dist/media/video-engine-media-source.js +1 -1
  31. package/dist/render-preparation/depth/clip-preview.d.ts +41 -0
  32. package/dist/render-preparation/depth/clip-preview.d.ts.map +1 -0
  33. package/dist/render-preparation/depth/clip-timing.d.ts +18 -0
  34. package/dist/render-preparation/depth/clip-timing.d.ts.map +1 -0
  35. package/dist/render-preparation/depth/clip.d.ts +14 -0
  36. package/dist/render-preparation/depth/clip.d.ts.map +1 -0
  37. package/dist/render-preparation/depth/exact-at-rest.d.ts +44 -0
  38. package/dist/render-preparation/depth/exact-at-rest.d.ts.map +1 -0
  39. package/dist/render-preparation/depth/exact-playback.d.ts +40 -0
  40. package/dist/render-preparation/depth/exact-playback.d.ts.map +1 -0
  41. package/dist/render-preparation/depth/files.d.ts +55 -0
  42. package/dist/render-preparation/depth/files.d.ts.map +1 -0
  43. package/dist/render-preparation/depth/frame-decode.d.ts +20 -0
  44. package/dist/render-preparation/depth/frame-decode.d.ts.map +1 -0
  45. package/dist/render-preparation/depth/frame-preparer.d.ts +51 -0
  46. package/dist/render-preparation/depth/frame-preparer.d.ts.map +1 -0
  47. package/dist/render-preparation/depth/frame-window.d.ts +146 -0
  48. package/dist/render-preparation/depth/frame-window.d.ts.map +1 -0
  49. package/dist/render-preparation/depth/options.d.ts +37 -0
  50. package/dist/render-preparation/depth/options.d.ts.map +1 -0
  51. package/dist/render-preparation/depth/png16.d.ts +40 -0
  52. package/dist/render-preparation/depth/png16.d.ts.map +1 -0
  53. package/dist/render-preparation/depth/preview-luma-copier.d.ts +12 -0
  54. package/dist/render-preparation/depth/preview-luma-copier.d.ts.map +1 -0
  55. package/dist/render-preparation/depth/preview-luma.d.ts +48 -0
  56. package/dist/render-preparation/depth/preview-luma.d.ts.map +1 -0
  57. package/dist/render-preparation/depth/source.d.ts +123 -0
  58. package/dist/render-preparation/depth/source.d.ts.map +1 -0
  59. package/dist/render-preparation/depth/worker-protocol.d.ts +76 -0
  60. package/dist/render-preparation/depth/worker-protocol.d.ts.map +1 -0
  61. package/dist/render-preparation/mask-frame-artifact.d.ts +10 -0
  62. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  63. package/dist/render-preparation/mask-frame-compositor.d.ts +2 -1
  64. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  65. package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
  66. package/dist/render-preparation/mask-outline-paths.d.ts +3 -0
  67. package/dist/render-preparation/mask-outline-paths.d.ts.map +1 -0
  68. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +2 -1
  69. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  70. package/dist/render-preparation/playhead-motion.d.ts +67 -0
  71. package/dist/render-preparation/playhead-motion.d.ts.map +1 -0
  72. package/dist/render-preparation/prepared-render-window.d.ts +12 -0
  73. package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
  74. package/dist/renderers/depth-textures.d.ts +79 -0
  75. package/dist/renderers/depth-textures.d.ts.map +1 -0
  76. package/dist/renderers/detection-render-order.d.ts +4 -0
  77. package/dist/renderers/detection-render-order.d.ts.map +1 -0
  78. package/dist/renderers/id-mask-stroke-coverage.d.ts +3 -0
  79. package/dist/renderers/id-mask-stroke-coverage.d.ts.map +1 -0
  80. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  81. package/dist/renderers/media-renderer-scene.d.ts +16 -2
  82. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  83. package/dist/renderers/pixi-batch-texture-bindings.d.ts +9 -0
  84. package/dist/renderers/pixi-batch-texture-bindings.d.ts.map +1 -0
  85. package/dist/renderers/pixi-box-layer.d.ts +1 -0
  86. package/dist/renderers/pixi-box-layer.d.ts.map +1 -1
  87. package/dist/renderers/pixi-depth-layer.d.ts +76 -0
  88. package/dist/renderers/pixi-depth-layer.d.ts.map +1 -0
  89. package/dist/renderers/pixi-depth-shader.d.ts +76 -0
  90. package/dist/renderers/pixi-depth-shader.d.ts.map +1 -0
  91. package/dist/renderers/pixi-focus-layer.d.ts +1 -0
  92. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  93. package/dist/renderers/pixi-frame-present.d.ts +1 -0
  94. package/dist/renderers/pixi-frame-present.d.ts.map +1 -1
  95. package/dist/renderers/pixi-id-mask-shader.d.ts +1 -0
  96. package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
  97. package/dist/renderers/pixi-interaction-layer.d.ts +1 -1
  98. package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
  99. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +1 -0
  100. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  101. package/dist/renderers/pixi-mask-brush-preview.d.ts.map +1 -1
  102. package/dist/renderers/pixi-mask-halo.d.ts +1 -0
  103. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
  104. package/dist/renderers/pixi-mask-layer.d.ts +8 -2
  105. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  106. package/dist/renderers/pixi-mask-screen-stroke.d.ts +17 -0
  107. package/dist/renderers/pixi-mask-screen-stroke.d.ts.map +1 -0
  108. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  109. package/dist/renderers/pixi-region-coverage-mask.d.ts +1 -0
  110. package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -1
  111. package/dist/renderers/pixi-region-layer.d.ts +1 -0
  112. package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
  113. package/dist/renderers/pixi-scene-layer-slot.d.ts +1 -0
  114. package/dist/renderers/pixi-scene-layer-slot.d.ts.map +1 -1
  115. package/dist/renderers/pixi-vector-layer.d.ts +1 -0
  116. package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
  117. package/dist/renderers/prepared-annotation-window.d.ts +1 -0
  118. package/dist/renderers/prepared-annotation-window.d.ts.map +1 -1
  119. package/dist/renderers/render-preparation-report.d.ts +23 -0
  120. package/dist/renderers/render-preparation-report.d.ts.map +1 -0
  121. package/dist/sessions/media-session-defaults.d.ts.map +1 -1
  122. package/dist/sessions/media-session-state.d.ts.map +1 -1
  123. package/dist/sessions/media-session.d.ts.map +1 -1
  124. package/dist/tracking.worker.js +64 -0
  125. package/dist/tracking.worker.js.map +1 -1
  126. package/dist/types/media-depth.d.ts +55 -0
  127. package/dist/types/media-depth.d.ts.map +1 -0
  128. package/dist/types/media-renderer.d.ts +33 -0
  129. package/dist/types/media-renderer.d.ts.map +1 -1
  130. package/dist/types/media-session.d.ts +21 -0
  131. package/dist/types/media-session.d.ts.map +1 -1
  132. package/dist/types/render-preparation.d.ts +105 -3
  133. package/dist/types/render-preparation.d.ts.map +1 -1
  134. package/dist/{video-engine-media-source-DR-x1UfK.js → video-engine-media-source-DOFsuGS4.js} +35 -29
  135. package/dist/video-engine-media-source-DOFsuGS4.js.map +1 -0
  136. package/dist/web-video-engine/analysis.js +6 -43
  137. package/dist/web-video-engine/cache-budget.d.ts +4 -7
  138. package/dist/web-video-engine/constants.d.ts +3 -4
  139. package/dist/web-video-engine/create-scrub-cursor.d.ts +1 -0
  140. package/dist/web-video-engine/decode-resolution-fynpRIAG.js +109 -0
  141. package/dist/web-video-engine/decode-scheduler.d.ts +1 -0
  142. package/dist/web-video-engine/engine-core.d.ts +4 -2
  143. package/dist/web-video-engine/engine.js +8 -6
  144. package/dist/web-video-engine/engine.worker.js +164 -66
  145. package/dist/web-video-engine/frame-cache.d.ts +9 -0
  146. package/dist/web-video-engine/frame-index.d.ts +1 -0
  147. package/dist/web-video-engine/frame-index.js +97 -0
  148. package/dist/web-video-engine/{frame-timeline-DreEwsRY.js → frame-timeline-CYgzzmpA.js} +1 -109
  149. package/dist/web-video-engine/frame-timeline-reader.d.ts +21 -0
  150. package/dist/web-video-engine/video-engine.d.ts +9 -4
  151. package/node_modules/supervision-js-core/THIRD_PARTY_NOTICES.md +132 -0
  152. package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
  153. package/node_modules/supervision-js-core/dist/index.d.ts +8 -3
  154. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  155. package/node_modules/supervision-js-core/dist/index.js +1172 -70
  156. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  157. package/node_modules/supervision-js-core/dist/interactions/annotation-editing-engine.d.ts +2 -2
  158. package/node_modules/supervision-js-core/dist/interactions/annotation-editing-engine.d.ts.map +1 -1
  159. package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts +2 -2
  160. package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts.map +1 -1
  161. package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts.map +1 -1
  162. package/node_modules/supervision-js-core/dist/interactions/viewport-controller.d.ts.map +1 -1
  163. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-presentation.d.ts.map +1 -1
  164. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts.map +1 -1
  165. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +50 -3
  166. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
  167. package/node_modules/supervision-js-core/dist/types/depth-map.d.ts +203 -0
  168. package/node_modules/supervision-js-core/dist/types/depth-map.d.ts.map +1 -0
  169. package/node_modules/supervision-js-core/dist/types/detections.d.ts +2 -1
  170. package/node_modules/supervision-js-core/dist/types/detections.d.ts.map +1 -1
  171. package/node_modules/supervision-js-core/dist/types/editing.d.ts +9 -0
  172. package/node_modules/supervision-js-core/dist/types/editing.d.ts.map +1 -1
  173. package/node_modules/supervision-js-core/dist/types/interaction.d.ts +4 -0
  174. package/node_modules/supervision-js-core/dist/types/interaction.d.ts.map +1 -1
  175. package/node_modules/supervision-js-core/dist/types/mask-style.d.ts +3 -0
  176. package/node_modules/supervision-js-core/dist/types/mask-style.d.ts.map +1 -1
  177. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +3 -0
  178. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  179. package/node_modules/supervision-js-core/dist/types/viewport.d.ts +3 -0
  180. package/node_modules/supervision-js-core/dist/types/viewport.d.ts.map +1 -1
  181. package/node_modules/supervision-js-core/dist/utils/depth-color-mapping.d.ts +91 -0
  182. package/node_modules/supervision-js-core/dist/utils/depth-color-mapping.d.ts.map +1 -0
  183. package/node_modules/supervision-js-core/dist/utils/depth-colormap-tables.d.ts +30 -0
  184. package/node_modules/supervision-js-core/dist/utils/depth-colormap-tables.d.ts.map +1 -0
  185. package/node_modules/supervision-js-core/dist/utils/depth-colormaps.d.ts +16 -0
  186. package/node_modules/supervision-js-core/dist/utils/depth-colormaps.d.ts.map +1 -0
  187. package/node_modules/supervision-js-core/dist/utils/depth-manifest.d.ts +23 -0
  188. package/node_modules/supervision-js-core/dist/utils/depth-manifest.d.ts.map +1 -0
  189. package/node_modules/supervision-js-core/dist/utils/depth-readout.d.ts +14 -0
  190. package/node_modules/supervision-js-core/dist/utils/depth-readout.d.ts.map +1 -0
  191. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts +21 -0
  192. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
  193. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +3 -7
  194. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
  195. package/node_modules/supervision-js-core/package.json +2 -1
  196. package/package.json +6 -1
  197. package/dist/video-engine-media-source-DR-x1UfK.js.map +0 -1
@@ -89,6 +89,19 @@ var DetectionFrameRetentionMode;
89
89
  * function and not in a smaller number here.
90
90
  */
91
91
  const PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS = 0.0005;
92
+ /**
93
+ * Validates and orders a window without copying it. The frames stay the
94
+ * caller's own objects, which is what a source wants: `DetectionFrame` and its
95
+ * `detections` are both `readonly`, nothing in the library writes to either,
96
+ * and the buffer that receives this window already keeps the objects it
97
+ * recognises and copies only the ones it does not
98
+ * (`reuseBufferedFrameSnapshots`). A source that copies first pays for every
99
+ * frame the buffer is about to discard.
100
+ */
101
+ function sortedDetectionFrames(detectionFrames) {
102
+ validateDetectionFrames(detectionFrames ?? []);
103
+ return [...(detectionFrames ?? [])].sort((left, right) => left.mediaTime - right.mediaTime);
104
+ }
92
105
  function copySortedDetectionFrames(detectionFrames) {
93
106
  validateDetectionFrames(detectionFrames ?? []);
94
107
  return (detectionFrames ?? [])
@@ -668,6 +681,9 @@ function createBufferedDetectionTimeline(options) {
668
681
  let state = createIdleDetectionBufferState();
669
682
  let destroyed = false;
670
683
  let loadId = 0;
684
+ // Preserve snapshot reuse without retaining evicted geometry.
685
+ const snapshotByIdentity = new Map();
686
+ let snapshotSourceVersion = null;
671
687
  let bufferedSourceVersion = null;
672
688
  let bufferedVersionRange = null;
673
689
  let timelineContext = {
@@ -693,6 +709,37 @@ function createBufferedDetectionTimeline(options) {
693
709
  }
694
710
  return ranges.reduce((version, range) => Math.max(version, options.source.getVersion?.(range) ?? 0), 0);
695
711
  };
712
+ const synchronizeSnapshotMemo = () => {
713
+ const sourceVersion = getSourceVersion();
714
+ if (snapshotSourceVersion === sourceVersion) {
715
+ return;
716
+ }
717
+ const frames = [...snapshotByIdentity.values()].flatMap((reference) => {
718
+ const frame = reference.deref();
719
+ return frame ? [frame] : [];
720
+ });
721
+ const changes = snapshotSourceVersion === null || frames.length === 0
722
+ ? undefined
723
+ : options.source.getChangesSince?.(snapshotSourceVersion, [
724
+ {
725
+ startTime: Math.min(...frames.map((frame) => frame.mediaTime)),
726
+ endTime: Math.max(...frames.map((frame) => frame.endTime ??
727
+ frame.mediaTime +
728
+ Number.EPSILON * Math.max(1, Math.abs(frame.mediaTime)))),
729
+ },
730
+ ]);
731
+ if (!changes || changes.requiresReload) {
732
+ snapshotByIdentity.clear();
733
+ }
734
+ else {
735
+ for (const frame of frames) {
736
+ if (changes.ranges.some((range) => detectionFrameOverlapsRange(frame, range.startTime, range.endTime))) {
737
+ snapshotByIdentity.delete(getDetectionFrameIdentity(frame));
738
+ }
739
+ }
740
+ }
741
+ snapshotSourceVersion = sourceVersion;
742
+ };
696
743
  const isLoadingEnabled = () => typeof options.enabled === "function"
697
744
  ? options.enabled()
698
745
  : options.enabled !== false;
@@ -740,14 +787,19 @@ function createBufferedDetectionTimeline(options) {
740
787
  return;
741
788
  }
742
789
  const committedSourceVersion = getSourceVersion(sourceRanges);
790
+ const sourceUnchanged = sourceVersion === committedSourceVersion;
743
791
  const loadedFrames = frameRanges.flat();
744
- buffer =
745
- bufferedSourceVersion !== null &&
746
- bufferedSourceVersion === committedSourceVersion
747
- ? reuseBufferedFrameSnapshots(buffer, loadedFrames)
748
- : copySortedDetectionFrames(loadedFrames);
792
+ synchronizeSnapshotMemo();
793
+ buffer = sourceUnchanged
794
+ ? reuseBufferedFrameSnapshots(bufferedSourceVersion === sourceVersion ? buffer : [], loadedFrames, snapshotByIdentity)
795
+ : copySortedDetectionFrames(loadedFrames);
796
+ if (sourceUnchanged) {
797
+ for (const frame of buffer) {
798
+ rememberSnapshot(snapshotByIdentity, frame);
799
+ }
800
+ }
749
801
  bufferedVersionRange = versionRange;
750
- bufferedSourceVersion = committedSourceVersion;
802
+ bufferedSourceVersion = sourceVersion;
751
803
  state = {
752
804
  bufferEndTime: endTime,
753
805
  bufferStartTime: startTime,
@@ -1003,6 +1055,8 @@ function createBufferedDetectionTimeline(options) {
1003
1055
  listeners.clear();
1004
1056
  pendingPrefetch = undefined;
1005
1057
  buffer = [];
1058
+ snapshotByIdentity.clear();
1059
+ snapshotSourceVersion = null;
1006
1060
  bufferedSourceVersion = null;
1007
1061
  bufferedVersionRange = null;
1008
1062
  state = {
@@ -1086,7 +1140,14 @@ function createBufferedDetectionTimeline(options) {
1086
1140
  bufferedVersionRange !== incrementalVersionRange) {
1087
1141
  return;
1088
1142
  }
1089
- buffer = mergeIncrementalFrames(buffer, changedFrameRanges.flat(), changedRanges);
1143
+ const changedFrames = copySortedDetectionFrames(changedFrameRanges.flat());
1144
+ synchronizeSnapshotMemo();
1145
+ buffer = mergeIncrementalFrames(buffer, changedFrames, changedRanges);
1146
+ if (getSourceVersion(sourceRanges) === changes.version) {
1147
+ for (const frame of buffer) {
1148
+ rememberSnapshot(snapshotByIdentity, frame);
1149
+ }
1150
+ }
1090
1151
  bufferedSourceVersion = changes.version;
1091
1152
  state = {
1092
1153
  ...state,
@@ -1391,18 +1452,36 @@ function mergeIncrementalFrames(currentFrames, changedFrames, changedRanges) {
1391
1452
  }
1392
1453
  return Array.from(framesByIdentity.values()).sort(compareDetectionFrames$1);
1393
1454
  }
1394
- /**
1395
- * Keeps the frame already held wherever the source returned one this buffer
1396
- * knows, and copies only what is new. Copying everything first and then
1397
- * discarding it is the same result for a great deal more work: a window rebuilt
1398
- * while a gesture moves inside it re-derives hundreds of frames it already has.
1399
- */
1400
- function reuseBufferedFrameSnapshots(currentFrames, loadedFrames) {
1401
- validateDetectionFrames(loadedFrames);
1455
+ const MAX_REMEMBERED_SNAPSHOTS = 8192;
1456
+ function rememberSnapshot(snapshots, frame) {
1457
+ const identity = getDetectionFrameIdentity(frame);
1458
+ if (snapshots.get(identity)?.deref() === frame) {
1459
+ return;
1460
+ }
1461
+ snapshots.delete(identity);
1462
+ snapshots.set(identity, new WeakRef(frame));
1463
+ while (snapshots.size > MAX_REMEMBERED_SNAPSHOTS) {
1464
+ const oldest = snapshots.keys().next().value;
1465
+ if (oldest === undefined) {
1466
+ break;
1467
+ }
1468
+ snapshots.delete(oldest);
1469
+ }
1470
+ }
1471
+ /** Reuse unchanged snapshots so moving the window copies only new frames. */
1472
+ function reuseBufferedFrameSnapshots(currentFrames, loadedFrames, snapshots) {
1402
1473
  const currentFramesByIdentity = new Map(currentFrames.map((frame) => [getDetectionFrameIdentity(frame), frame]));
1403
1474
  return loadedFrames
1404
- .map((frame) => currentFramesByIdentity.get(getDetectionFrameIdentity(frame)) ??
1405
- copyDetectionFrame(frame))
1475
+ .map((frame) => {
1476
+ const identity = getDetectionFrameIdentity(frame);
1477
+ const existing = currentFramesByIdentity.get(identity) ??
1478
+ snapshots.get(identity)?.deref();
1479
+ if (existing) {
1480
+ return existing;
1481
+ }
1482
+ validateDetectionFrames([frame]);
1483
+ return copyDetectionFrame(frame);
1484
+ })
1406
1485
  .sort(compareDetectionFrames$1);
1407
1486
  }
1408
1487
  function getDetectionFrameIdentity(frame) {
@@ -4860,7 +4939,14 @@ function pickDetectionAtPoint(frame, point, options = {}) {
4860
4939
  zIndex: detection.zIndex ?? detectionIndex,
4861
4940
  });
4862
4941
  };
4863
- if (detection.rect && containsPoint(detection.rect, point, padding)) {
4942
+ const rectIsBounds = options.preferTopmost &&
4943
+ Boolean(detection.mask ||
4944
+ detection.polygon ||
4945
+ detection.polyline ||
4946
+ detection.orientedBox);
4947
+ if (detection.rect &&
4948
+ !rectIsBounds &&
4949
+ containsPoint(detection.rect, point, padding)) {
4864
4950
  pushCandidate(DetectionPickTarget.Box, rectArea(detection.rect));
4865
4951
  }
4866
4952
  if (detection.polygon && pointInPolygon(point, detection.polygon.points)) {
@@ -4936,6 +5022,16 @@ function pickDetectionAtPoint(frame, point, options = {}) {
4936
5022
  }
4937
5023
  }
4938
5024
  candidates.sort((left, right) => {
5025
+ if (options.preferTopmost) {
5026
+ const layer = Number(left.result.target === DetectionPickTarget.Mask) -
5027
+ Number(right.result.target === DetectionPickTarget.Mask);
5028
+ if (layer !== 0)
5029
+ return layer;
5030
+ const order = right.zIndex - left.zIndex ||
5031
+ right.result.detectionIndex - left.result.detectionIndex;
5032
+ if (order !== 0)
5033
+ return order;
5034
+ }
4939
5035
  const priorityDifference = left.priority - right.priority;
4940
5036
  if (priorityDifference !== 0) {
4941
5037
  return priorityDifference;
@@ -5169,11 +5265,13 @@ function createViewportController(initial = {}) {
5169
5265
  screenToMedia: (point) => screenToMedia(point, transform),
5170
5266
  mediaToScreen: (point) => mediaToScreen(point, transform),
5171
5267
  setTransform(next) {
5172
- if (transform.locked)
5173
- return;
5268
+ if (next.scale !== undefined &&
5269
+ (!Number.isFinite(next.scale) || next.scale <= 0)) {
5270
+ throw new RangeError("Viewport scale must be a finite number greater than zero.");
5271
+ }
5174
5272
  update({
5175
5273
  ...transform,
5176
- ...(next.scale === undefined ? {} : { scale: clampScale(next.scale) }),
5274
+ ...(next.scale === undefined ? {} : { scale: next.scale }),
5177
5275
  ...(next.x === undefined ? {} : { x: next.x }),
5178
5276
  ...(next.y === undefined ? {} : { y: next.y }),
5179
5277
  });
@@ -5190,7 +5288,7 @@ function createViewportController(initial = {}) {
5190
5288
  if (transform.locked || !Number.isFinite(factor) || factor <= 0)
5191
5289
  return;
5192
5290
  const mediaPoint = screenToMedia(point, transform);
5193
- const scale = clampScale(transform.scale * factor);
5291
+ const scale = clampScale(transform.scale * factor, transform.scale);
5194
5292
  update({
5195
5293
  ...transform,
5196
5294
  scale,
@@ -5221,8 +5319,8 @@ function mediaToScreen(point, transform) {
5221
5319
  y: point.y * transform.scale + transform.y,
5222
5320
  };
5223
5321
  }
5224
- function clampScale(scale) {
5225
- return Math.max(MIN_SCALE, Math.min(MAX_SCALE, Number.isFinite(scale) ? scale : 1));
5322
+ function clampScale(scale, current = 1) {
5323
+ return Math.max(Math.min(MIN_SCALE, current), Math.min(Math.max(MAX_SCALE, current), Number.isFinite(scale) ? scale : 1));
5226
5324
  }
5227
5325
  function freeze(transform) {
5228
5326
  return Object.freeze(transform);
@@ -5311,9 +5409,9 @@ function pickAnnotationHandle(handles, point) {
5311
5409
  }
5312
5410
  return picked;
5313
5411
  }
5314
- function applyAnnotationHandleDrag(detection, handle, point) {
5412
+ function applyAnnotationHandleDrag(detection, handle, point, mediaDimensions) {
5315
5413
  if (detection.rect && handle.kind === AnnotationHandleKind.Resize) {
5316
- const rect = resizeRect(detection.rect, handle.id, point);
5414
+ const rect = resizeRect(detection.rect, handle.id, point, mediaDimensions);
5317
5415
  const boxRelative = detection.keypoints?.boxRelative;
5318
5416
  if (!detection.keypoints || !boxRelative?.some(Boolean)) {
5319
5417
  return { ...detection, rect };
@@ -5477,26 +5575,50 @@ function getPathHandles(points, closed, radius, hitSize) {
5477
5575
  }
5478
5576
  return handles;
5479
5577
  }
5480
- function resizeRect(rect, handle, point) {
5578
+ function resizeRect(rect, handle, point, mediaDimensions) {
5481
5579
  let left = rect.x - rect.width / 2;
5482
5580
  let right = rect.x + rect.width / 2;
5483
5581
  let top = rect.y - rect.height / 2;
5484
5582
  let bottom = rect.y + rect.height / 2;
5583
+ if (mediaDimensions) {
5584
+ left = Math.max(0, Math.min(left, mediaDimensions.width));
5585
+ right = Math.max(0, Math.min(right, mediaDimensions.width));
5586
+ top = Math.max(0, Math.min(top, mediaDimensions.height));
5587
+ bottom = Math.max(0, Math.min(bottom, mediaDimensions.height));
5588
+ }
5485
5589
  if (handle.includes("w"))
5486
- left = Math.min(point.x, right - 5);
5590
+ left = resizeEdge(point.x, right, -1, mediaDimensions?.width);
5487
5591
  if (handle.includes("e"))
5488
- right = Math.max(point.x, left + 5);
5592
+ right = resizeEdge(point.x, left, 1, mediaDimensions?.width);
5489
5593
  if (handle.includes("n"))
5490
- top = Math.min(point.y, bottom - 5);
5594
+ top = resizeEdge(point.y, bottom, -1, mediaDimensions?.height);
5491
5595
  if (handle.includes("s"))
5492
- bottom = Math.max(point.y, top + 5);
5596
+ bottom = resizeEdge(point.y, top, 1, mediaDimensions?.height);
5493
5597
  return {
5494
5598
  x: (left + right) / 2,
5495
5599
  y: (top + bottom) / 2,
5496
- width: right - left,
5497
- height: bottom - top,
5600
+ width: Math.abs(right - left),
5601
+ height: Math.abs(bottom - top),
5498
5602
  };
5499
5603
  }
5604
+ function resizeEdge(point, anchor, initialDirection, extent) {
5605
+ let direction = point === anchor ? initialDirection : point < anchor ? -1 : 1;
5606
+ let minimum = 5;
5607
+ if (extent !== undefined) {
5608
+ const available = direction === 1 ? extent - anchor : anchor;
5609
+ const opposite = direction === 1 ? anchor : extent - anchor;
5610
+ // Crossing cannot push the minimum-size box past the media edge. Use the
5611
+ // other side of the fixed anchor when it has more room.
5612
+ if (available < Math.min(minimum, extent) && opposite > available) {
5613
+ direction *= -1;
5614
+ }
5615
+ minimum = Math.min(minimum, direction === 1 ? extent - anchor : anchor);
5616
+ }
5617
+ const edge = direction === 1
5618
+ ? Math.max(point, anchor + minimum)
5619
+ : Math.min(point, anchor - minimum);
5620
+ return extent === undefined ? edge : Math.max(0, Math.min(edge, extent));
5621
+ }
5500
5622
  function replacePathPoint(detection, key, index, point) {
5501
5623
  const geometry = detection[key];
5502
5624
  const points = [...geometry.points];
@@ -5553,12 +5675,10 @@ function createAnnotationEditingEngine(options = {}) {
5553
5675
  setPreview(resolveBoxPreview(gesture));
5554
5676
  break;
5555
5677
  case "move": {
5556
- const dx = input.point.x - gesture.start.point.x;
5557
- const dy = input.point.y - gesture.start.point.y;
5558
5678
  const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
5559
5679
  gesture = { ...gesture, current: input, moved };
5560
5680
  if (moved) {
5561
- const preview = offsetDetection(gesture.detection, dx, dy);
5681
+ const { preview, dx, dy } = resolveMovePreview(gesture, input);
5562
5682
  setPreview(preview);
5563
5683
  if (gesture.detection.id !== undefined) {
5564
5684
  options.onFastTranslate?.(gesture.detection.id, dx, dy);
@@ -5576,7 +5696,7 @@ function createAnnotationEditingEngine(options = {}) {
5576
5696
  const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
5577
5697
  gesture = { ...gesture, current: input, moved };
5578
5698
  if (moved) {
5579
- setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point));
5699
+ setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point, input.mediaDimensions ?? gesture.start.mediaDimensions));
5580
5700
  }
5581
5701
  break;
5582
5702
  }
@@ -5637,7 +5757,7 @@ function createAnnotationEditingEngine(options = {}) {
5637
5757
  setState(idleState());
5638
5758
  return;
5639
5759
  }
5640
- const preview = offsetDetection(active.detection, input.point.x - active.start.point.x, input.point.y - active.start.point.y);
5760
+ const { preview } = resolveMovePreview(active, input);
5641
5761
  commit(preview, active.detection);
5642
5762
  return;
5643
5763
  }
@@ -5645,7 +5765,7 @@ function createAnnotationEditingEngine(options = {}) {
5645
5765
  setState(idleState());
5646
5766
  return;
5647
5767
  }
5648
- const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point);
5768
+ const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point, input.mediaDimensions ?? active.start.mediaDimensions);
5649
5769
  commit(preview, active.detection);
5650
5770
  },
5651
5771
  keyDown(key) {
@@ -5656,6 +5776,18 @@ function createAnnotationEditingEngine(options = {}) {
5656
5776
  commitPath();
5657
5777
  }
5658
5778
  },
5779
+ undoCreation() {
5780
+ if (gesture?.kind !== "path")
5781
+ return false;
5782
+ gesture.points.pop();
5783
+ gesture.lastTimestamp = null;
5784
+ const last = gesture.points.at(-1);
5785
+ if (!last)
5786
+ cancel();
5787
+ else
5788
+ setPreview(resolvePathPreview([...gesture.points, last]));
5789
+ return true;
5790
+ },
5659
5791
  beginHandleDrag(detection, handle, input) {
5660
5792
  if (detection.locked || detection.mask)
5661
5793
  return;
@@ -5745,9 +5877,10 @@ function createAnnotationEditingEngine(options = {}) {
5745
5877
  const minimum = tool.minVertices ??
5746
5878
  (tool.geometry === AnnotationGeometryKind.Polygon ? 3 : 2);
5747
5879
  const closeDistance = Math.hypot(input.point.x - gesture.points[0].x, input.point.y - gesture.points[0].y) * scale();
5748
- const doubleClick = input.detail !== undefined
5749
- ? input.detail >= 2
5750
- : input.timestamp - gesture.lastTimestamp < CLICK_CANCEL_MS;
5880
+ const doubleClick = gesture.lastTimestamp !== null &&
5881
+ (input.detail !== undefined
5882
+ ? input.detail >= 2
5883
+ : input.timestamp - gesture.lastTimestamp < CLICK_CANCEL_MS);
5751
5884
  if (gesture.points.length >= minimum &&
5752
5885
  ((tool.geometry === AnnotationGeometryKind.Polygon &&
5753
5886
  closeDistance <= CLOSE_ZONE_SCREEN_PX) ||
@@ -5777,10 +5910,13 @@ function createAnnotationEditingEngine(options = {}) {
5777
5910
  });
5778
5911
  }
5779
5912
  function resolveBoxPreview(active) {
5780
- const left = Math.min(active.start.point.x, active.current.point.x);
5781
- const right = Math.max(active.start.point.x, active.current.point.x);
5782
- const top = Math.min(active.start.point.y, active.current.point.y);
5783
- const bottom = Math.max(active.start.point.y, active.current.point.y);
5913
+ const dimensions = active.current.mediaDimensions ?? active.start.mediaDimensions;
5914
+ const start = boundPoint(active.start.point, dimensions);
5915
+ const current = boundPoint(active.current.point, dimensions);
5916
+ const left = Math.min(start.x, current.x);
5917
+ const right = Math.max(start.x, current.x);
5918
+ const top = Math.min(start.y, current.y);
5919
+ const bottom = Math.max(start.y, current.y);
5784
5920
  const rect = {
5785
5921
  x: (left + right) / 2,
5786
5922
  y: (top + bottom) / 2,
@@ -5789,6 +5925,35 @@ function createAnnotationEditingEngine(options = {}) {
5789
5925
  };
5790
5926
  return tool.createDetection(rect);
5791
5927
  }
5928
+ function resolveMovePreview(active, input) {
5929
+ let dx = input.point.x - active.start.point.x;
5930
+ let dy = input.point.y - active.start.point.y;
5931
+ let rect = active.detection.rect;
5932
+ const dimensions = input.mediaDimensions ?? active.start.mediaDimensions;
5933
+ if (rect && dimensions) {
5934
+ const width = Math.min(rect.width, dimensions.width);
5935
+ const height = Math.min(rect.height, dimensions.height);
5936
+ const x = Math.max(width / 2, Math.min(rect.x + dx, dimensions.width - width / 2));
5937
+ const y = Math.max(height / 2, Math.min(rect.y + dy, dimensions.height - height / 2));
5938
+ dx = x - rect.x;
5939
+ dy = y - rect.y;
5940
+ rect = { ...rect, x, y, width, height };
5941
+ }
5942
+ const preview = offsetDetection(active.detection, dx, dy);
5943
+ return {
5944
+ preview: rect && dimensions ? { ...preview, rect } : preview,
5945
+ dx,
5946
+ dy,
5947
+ };
5948
+ }
5949
+ function boundPoint(point, dimensions) {
5950
+ return dimensions
5951
+ ? {
5952
+ x: Math.max(0, Math.min(point.x, dimensions.width)),
5953
+ y: Math.max(0, Math.min(point.y, dimensions.height)),
5954
+ }
5955
+ : point;
5956
+ }
5792
5957
  function resolvePathPreview(points) {
5793
5958
  return tool.createDetection(points);
5794
5959
  }
@@ -6456,7 +6621,7 @@ function serializeStroke(stroke) {
6456
6621
  if (!stroke) {
6457
6622
  return "none";
6458
6623
  }
6459
- return `${stroke.color}:${stroke.alpha}:${stroke.width}`;
6624
+ return `${stroke.color}:${stroke.alpha}:${stroke.width}${stroke.widthUnit === "screen" ? ":screen" : ""}`;
6460
6625
  }
6461
6626
  function normalizeStroke(stroke, fallbackColor) {
6462
6627
  if (!stroke) {
@@ -6466,6 +6631,7 @@ function normalizeStroke(stroke, fallbackColor) {
6466
6631
  alpha: stroke.alpha ?? DEFAULT_MASK_STROKE_ALPHA,
6467
6632
  color: stroke.color ?? fallbackColor,
6468
6633
  width: stroke.width ?? DEFAULT_MASK_STROKE_WIDTH,
6634
+ ...(stroke.widthUnit === undefined ? {} : { widthUnit: stroke.widthUnit }),
6469
6635
  };
6470
6636
  }
6471
6637
  function resolveStroke(stroke, fallbackColor, mode) {
@@ -7275,6 +7441,7 @@ function resolveDefaultClassColor(detection, getClassColor) {
7275
7441
  const annotationRendererKinds = [
7276
7442
  "box",
7277
7443
  "box-corners",
7444
+ "depth",
7278
7445
  "ellipse",
7279
7446
  "heatmap",
7280
7447
  "keypoints",
@@ -7328,6 +7495,11 @@ const RegionRendererComposeMode = {
7328
7495
  const annotationRenderers = {
7329
7496
  box: (options) => createAnnotationRenderer("box", options),
7330
7497
  boxCorners: (options) => createAnnotationRenderer("box-corners", options),
7498
+ depth: (options) => ({
7499
+ ...options,
7500
+ id: options?.id ?? "depth",
7501
+ kind: "depth",
7502
+ }),
7331
7503
  ellipse: (options) => createAnnotationRenderer("ellipse", options),
7332
7504
  heatmap: (options) => ({
7333
7505
  ...options,
@@ -7363,6 +7535,7 @@ const annotationRendererRegistry = {
7363
7535
  createCanonicalStyle: createDefaultBoxCornerStyle,
7364
7536
  styleField: "boxCornerStyle",
7365
7537
  },
7538
+ depth: { cardinality: "multiple" },
7366
7539
  ellipse: {
7367
7540
  cardinality: "singleton",
7368
7541
  createCanonicalStyle: createDefaultEllipseStyle,
@@ -7427,6 +7600,241 @@ function resolveAnnotationRendererStyleFields(kinds) {
7427
7600
  });
7428
7601
  }
7429
7602
 
7603
+ /**
7604
+ * What the stored values of a depth map measure.
7605
+ *
7606
+ * Every kind stores 0 where there is no depth.
7607
+ */
7608
+ const DepthMapKind = {
7609
+ /** Stereo disparity in pixels of the map. Larger is nearer. */
7610
+ DisparityPx: "disparity_px",
7611
+ /** Metric depth along the optical axis in metres. Smaller is nearer. */
7612
+ DepthM: "depth_m",
7613
+ /**
7614
+ * Unitless inverse depth up to an unknown scale and shift, as monocular
7615
+ * models produce it. Larger is nearer; it has no metric depth.
7616
+ */
7617
+ RelativeInverse: "relative_inverse",
7618
+ };
7619
+ /**
7620
+ * The quantity a depth renderer colours.
7621
+ *
7622
+ * Disparity is inverse depth, so it spends colour on near detail the way
7623
+ * stereo measures it. Either way the nearest end of the range is the warm or
7624
+ * bright end of the colour table.
7625
+ */
7626
+ const DepthQuantity = {
7627
+ Disparity: "disparity",
7628
+ Depth: "depth",
7629
+ };
7630
+ const DepthColormap = {
7631
+ Turbo: "turbo",
7632
+ Viridis: "viridis",
7633
+ Cividis: "cividis",
7634
+ Inferno: "inferno",
7635
+ Magma: "magma",
7636
+ Grayscale: "grayscale",
7637
+ };
7638
+ /** How a depth map is sampled where one map pixel does not cover one media pixel. */
7639
+ const DepthSampling = {
7640
+ /** Nearest when the map is at least media size, edge-aware otherwise. */
7641
+ Auto: "auto",
7642
+ Nearest: "nearest",
7643
+ /**
7644
+ * Bilinear over valid neighbours only, falling back to the nearest one
7645
+ * across a depth edge, so objects never bleed into the background.
7646
+ */
7647
+ EdgeAware: "edge-aware",
7648
+ };
7649
+ /**
7650
+ * The luma range a depth preview video's codes are written in.
7651
+ *
7652
+ * TV range is what browsers' hardware decoders return as written; a decoder
7653
+ * may squeeze full range into TV range or move codes converting it to RGB.
7654
+ */
7655
+ const DepthPreviewLevels = {
7656
+ /** Every code from 0 to 255; the top code is 255. */
7657
+ Full: "full",
7658
+ /** Limited range: black is 16 and the top code is 235. */
7659
+ Tv: "tv",
7660
+ };
7661
+
7662
+ // Generated by tools/depth-colormaps/generate-depth-colormap-tables.py
7663
+ // from matplotlib 3.11.2. Do not edit by hand.
7664
+ //
7665
+ // Each table is 256 sRGB entries as 6-digit hex, darkest or coldest
7666
+ // first, in the order the colour coordinate t runs from 0 to 1.
7667
+ /*!
7668
+ * Turbo, Anton Mikhailov's published table.
7669
+ * Copyright 2019 Google LLC.
7670
+ * SPDX-License-Identifier: Apache-2.0
7671
+ * https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f
7672
+ */
7673
+ const turboTable = [
7674
+ "30123b32154333184a341b51351e5836215f37246638276d392a733a2d793b2f803c32863d358b3e38913f3b973f3e9c",
7675
+ "4040a24143a74146ac4249b1424bb5434eba4451bf4454c34456c74559cb455ccf455ed34661d64664da4666dd4669e0",
7676
+ "466be3476ee64771e94773eb4776ee4778f0477bf2467df44680f64682f84685fa4687fb458afc458cfd448ffe4391fe",
7677
+ "4294ff4196ff4099ff3e9bfe3d9efe3ba0fd3aa3fc38a5fb37a8fa35abf833adf731aff52fb2f42eb4f22cb7f02ab9ee",
7678
+ "28bceb27bee925c0e723c3e422c5e220c7df1fc9dd1ecbda1ccdd81bd0d51ad2d21ad4d019d5cd18d7ca18d9c818dbc5",
7679
+ "18ddc218dec018e0bd19e2bb19e3b91ae4b61ce6b41de7b21fe9af20eaac22ebaa25eca727eea42aefa12cf09e2ff19b",
7680
+ "32f29835f39438f4913cf58e3ff68a43f78746f8844af8804ef97d52fa7a55fa7659fb735dfc6f61fc6c65fd6969fd66",
7681
+ "6dfe6271fe5f75fe5c79fe597dff5680ff5384ff5188ff4e8bff4b8fff4992ff4796fe4499fe429cfe409ffd3fa1fd3d",
7682
+ "a4fc3ca7fc3aa9fb39acfb38affa37b1f936b4f836b7f735b9f635bcf534bef434c1f334c3f134c6f034c8ef34cbed34",
7683
+ "cdec34d0ea34d2e935d4e735d7e535d9e436dbe236dde037dfdf37e1dd37e3db38e5d938e7d739e9d539ebd339ecd13a",
7684
+ "eecf3aefcd3af1cb3af2c93af4c73af5c53af6c33af7c13af8be39f9bc39faba39fbb838fbb637fcb336fcb136fdae35",
7685
+ "fdac34fea933fea732fea431fea130fe9e2ffe9b2dfe992cfe962bfe932afe9029fd8d27fd8a26fc8725fc8423fb8122",
7686
+ "fb7e21fa7b1ff9781ef9751df8721cf76f1af66c19f56918f46617f36315f26014f15d13f05b12ef5811ed5510ec530f",
7687
+ "eb500eea4e0de84b0ce7490ce5470be4450ae2430ae14109df3f08dd3d08dc3b07da3907d83706d63506d43305d23105",
7688
+ "d02f05ce2d04cc2b04ca2a04c82803c52603c32503c12302be2102bc2002b91e02b71d02b41b01b21a01af1801ac1701",
7689
+ "a91601a71401a41301a112019e10019b0f01980e01950d01920b018e0a018b09028808028507028106027e05027a0403",
7690
+ ].join("");
7691
+ /*!
7692
+ * Viridis, by Stéfan van der Walt, Nathaniel Smith and Eric Firing.
7693
+ * Released under CC0: https://github.com/BIDS/colormap
7694
+ */
7695
+ const viridisTable = [
7696
+ "44015444025645045745055946075a46085c460a5d460b5e470d60470e61471063471164471365481467481668481769",
7697
+ "48186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a",
7698
+ "472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f85",
7699
+ "4240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b",
7700
+ "3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d",
7701
+ "33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e",
7702
+ "2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e",
7703
+ "26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d",
7704
+ "21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f88",
7705
+ "1fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad81",
7706
+ "28ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc74",
7707
+ "3fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc863",
7708
+ "5ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d",
7709
+ "84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32",
7710
+ "addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21a",
7711
+ "d8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725",
7712
+ ].join("");
7713
+ /*!
7714
+ * Cividis, Nuñez, Anderton and Renslow, PLOS ONE 13(7) e0199239 (2018),
7715
+ * from https://github.com/pnnl/cmaputil as shipped by matplotlib.
7716
+ * Copyright (c) 2017, Battelle Memorial Institute. Redistributed under
7717
+ * its BSD-style licence, which asks for this notice to be kept.
7718
+ */
7719
+ const cividisTable = [
7720
+ "00224e00234f00245100255300255400265600275800285900285b00295d002a5f002a61002b62002c64002c66002d68",
7721
+ "002e6a002e6c002f6d00306f0030700031700031710132710533710833700c34700f357012357014367016377018376f",
7722
+ "1a386f1c396f1e3a6f203a6f213b6e233c6e243c6e263d6e273e6e293f6e2a3f6d2b406d2d416d2e416d2f426d31436d",
7723
+ "32436d33446d34456c35456c36466c38476c39486c3a486c3b496c3c4a6c3d4a6c3e4b6c3f4c6c404c6c414d6c424e6c",
7724
+ "434e6c444f6c45506c46516c47516c48526c49536c4a536c4b546c4c556c4d556c4e566c4f576c50576c51586d52596d",
7725
+ "535a6d545a6d555b6d555c6d565c6d575d6d585e6d595e6e5a5f6e5b606e5c616e5d616e5e626e5e636f5f636f60646f",
7726
+ "61656f62656f636670646770656870656870666970676a71686a71696b716a6c716b6d726c6d726c6e726d6f726e6f73",
7727
+ "6f70737071737172747272747273747374757474757575757676767777767777777878777979777a7a787b7a787c7b78",
7728
+ "7d7c787e7c787e7d787f7e78807f78817f788280798381798482798582798683798784788885788985788a86788b8778",
7729
+ "8c88788d88788e89788f8a78908b78918b78928c78928d78938e78948e77958f779690779791779892779992779a9376",
7730
+ "9b94769c95769d95769e96769f9775a09875a19975a29975a39a74a49b74a59c74a69c74a79d73a89e73a99f73aaa073",
7731
+ "aba072aca172ada272aea371afa471b0a571b1a570b3a670b4a76fb5a86fb6a96fb7a96eb8aa6eb9ab6dbaac6dbbad6d",
7732
+ "bcae6cbdae6cbeaf6bbfb06bc0b16ac1b26ac2b369c3b369c4b468c5b568c6b667c7b767c8b866c9b965cbb965ccba64",
7733
+ "cdbb63cebc63cfbd62d0be62d1bf61d2c060d3c05fd4c15fd5c25ed6c35dd7c45cd9c55cdac65bdbc75adcc859ddc858",
7734
+ "dec958dfca57e0cb56e1cc55e2cd54e4ce53e5cf52e6d051e7d150e8d24fe9d34eead34cebd44bedd54aeed649efd748",
7735
+ "f0d846f1d945f2da44f3db42f5dc41f6dd3ff7de3ef8df3cf9e03afbe138fce236fde334fee434fee535fee636fee838",
7736
+ ].join("");
7737
+ /*!
7738
+ * Inferno, by Stéfan van der Walt and Nathaniel Smith.
7739
+ * Released under CC0: https://github.com/BIDS/colormap
7740
+ */
7741
+ const infernoTable = [
7742
+ "00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a0722",
7743
+ "0b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48",
7744
+ "210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b0964",
7745
+ "3d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d",
7746
+ "57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e",
7747
+ "71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a",
7748
+ "8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62",
7749
+ "a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655",
7750
+ "bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545",
7751
+ "d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933",
7752
+ "e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711f",
7753
+ "f1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0a",
7754
+ "f98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0f",
7755
+ "fcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932",
7756
+ "f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865",
7757
+ "f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4",
7758
+ ].join("");
7759
+ /*!
7760
+ * Magma, by Stéfan van der Walt and Nathaniel Smith.
7761
+ * Released under CC0: https://github.com/BIDS/colormap
7762
+ */
7763
+ const magmaTable = [
7764
+ "00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e090720",
7765
+ "0a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c1044",
7766
+ "1d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f1163311165331067341069",
7767
+ "36106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b",
7768
+ "51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c81",
7769
+ "6a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e2482802582812581",
7770
+ "8326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f",
7771
+ "9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367a",
7772
+ "b73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070",
7773
+ "d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064",
7774
+ "e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695c",
7775
+ "f56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761",
7776
+ "fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571",
7777
+ "fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287",
7778
+ "fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1",
7779
+ "fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf",
7780
+ ].join("");
7781
+
7782
+ const DEPTH_COLORMAP_ENTRIES = 256;
7783
+ const colormapTables = {
7784
+ cividis: cividisTable,
7785
+ inferno: infernoTable,
7786
+ magma: magmaTable,
7787
+ turbo: turboTable,
7788
+ viridis: viridisTable,
7789
+ };
7790
+ const depthColormaps = new Set(Object.values(DepthColormap));
7791
+ function isDepthColormap(value) {
7792
+ return typeof value === "string" && depthColormaps.has(value);
7793
+ }
7794
+ /**
7795
+ * The colour table as 256 RGBA8 entries with opaque alpha, far or low values
7796
+ * first. Entry `i` is the colour of `t = i / 255`.
7797
+ */
7798
+ function createDepthColormapLut(name) {
7799
+ if (!isDepthColormap(name)) {
7800
+ throw new RangeError(`Unknown depth colormap "${String(name)}".`);
7801
+ }
7802
+ const lut = new Uint8Array(DEPTH_COLORMAP_ENTRIES * 4);
7803
+ for (let index = 0; index < DEPTH_COLORMAP_ENTRIES; index += 1) {
7804
+ const offset = index * 4;
7805
+ if (name === DepthColormap.Grayscale) {
7806
+ lut[offset] = index;
7807
+ lut[offset + 1] = index;
7808
+ lut[offset + 2] = index;
7809
+ }
7810
+ else {
7811
+ const table = colormapTables[name];
7812
+ const hex = index * 6;
7813
+ lut[offset] = Number.parseInt(table.slice(hex, hex + 2), 16);
7814
+ lut[offset + 1] = Number.parseInt(table.slice(hex + 2, hex + 4), 16);
7815
+ lut[offset + 2] = Number.parseInt(table.slice(hex + 4, hex + 6), 16);
7816
+ }
7817
+ lut[offset + 3] = 255;
7818
+ }
7819
+ return lut;
7820
+ }
7821
+ /**
7822
+ * CSS colours of a depth colormap at `stops` evenly spaced points, far end
7823
+ * first and near end last, read from the same table the depth renderer draws
7824
+ * with. A legend that matches the picture is
7825
+ * `linear-gradient(to right, ${colors.join(", ")})`.
7826
+ */
7827
+ function depthColormapColors(colormap, stops = 16) {
7828
+ if (!Number.isInteger(stops) || stops < 2 || stops > DEPTH_COLORMAP_ENTRIES) {
7829
+ throw new RangeError(`Depth colormap stops must be an integer from 2 to ${DEPTH_COLORMAP_ENTRIES}, got ${stops}.`);
7830
+ }
7831
+ const lut = createDepthColormapLut(colormap);
7832
+ return Array.from({ length: stops }, (_, stop) => {
7833
+ const offset = Math.round((stop * (DEPTH_COLORMAP_ENTRIES - 1)) / (stops - 1)) * 4;
7834
+ return `#${Array.from(lut.subarray(offset, offset + 3), (channel) => channel.toString(16).padStart(2, "0")).join("")}`;
7835
+ });
7836
+ }
7837
+
7430
7838
  /**
7431
7839
  * Resolves built-in renderer descriptors into the existing specialized style
7432
7840
  * fields. The browser backend deliberately keeps ownership of its box, mask,
@@ -7458,6 +7866,9 @@ function resolveAnnotationRendererPresentation(presentation) {
7458
7866
  if (renderer.kind === "heatmap") {
7459
7867
  validateHeatmapRenderer(renderer);
7460
7868
  }
7869
+ if (renderer.kind === "depth") {
7870
+ validateDepthRenderer(renderer);
7871
+ }
7461
7872
  if (rendererIds.has(renderer.id)) {
7462
7873
  throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer id "${renderer.id}".`);
7463
7874
  }
@@ -7500,6 +7911,32 @@ function validateHeatmapRenderer(renderer) {
7500
7911
  throw new RangeError(`Invalid heatmap renderer settings for "${renderer.id}".`);
7501
7912
  }
7502
7913
  }
7914
+ const depthQuantities = new Set(Object.values(DepthQuantity));
7915
+ const depthSamplings = new Set(Object.values(DepthSampling));
7916
+ function validateDepthRenderer(renderer) {
7917
+ const { colormap, noDepthColor, opacity, quantity, range, sampling, wipe } = renderer;
7918
+ if ((colormap !== undefined && !isDepthColormap(colormap)) ||
7919
+ (quantity !== undefined && !depthQuantities.has(quantity)) ||
7920
+ (sampling !== undefined && !depthSamplings.has(sampling)) ||
7921
+ (range !== undefined &&
7922
+ range !== "clip" &&
7923
+ range !== "auto" &&
7924
+ (typeof range !== "object" ||
7925
+ range === null ||
7926
+ !Number.isFinite(range.min) ||
7927
+ !Number.isFinite(range.max) ||
7928
+ range.min >= range.max)) ||
7929
+ (opacity !== undefined && !Number.isFinite(opacity)) ||
7930
+ (wipe !== undefined &&
7931
+ !(Number.isFinite(wipe) && wipe >= 0 && wipe <= 1)) ||
7932
+ (noDepthColor !== undefined &&
7933
+ noDepthColor !== null &&
7934
+ !(Number.isInteger(noDepthColor) &&
7935
+ noDepthColor >= 0 &&
7936
+ noDepthColor <= 0xffffff))) {
7937
+ throw new RangeError(`Invalid depth renderer settings for "${renderer.id}".`);
7938
+ }
7939
+ }
7503
7940
  function isStyleBackedAnnotationRenderer(renderer) {
7504
7941
  return isStyleBackedAnnotationRendererKind(renderer.kind);
7505
7942
  }
@@ -7955,8 +8392,10 @@ function createIdMaskFrame(instructions, options = {}) {
7955
8392
  if (instructions.length === 0) {
7956
8393
  return undefined;
7957
8394
  }
7958
- const maskWidth = Math.max(...instructions.map(({ mask }) => mask.width));
7959
- const maskHeight = Math.max(...instructions.map(({ mask }) => mask.height));
8395
+ /* Pixel-grid dimensions use integer parts throughout allocation and RLE
8396
+ traversal. */
8397
+ const maskWidth = Math.floor(Math.max(...instructions.map(({ mask }) => mask.width)));
8398
+ const maskHeight = Math.floor(Math.max(...instructions.map(({ mask }) => mask.height)));
7960
8399
  const width = resolveRasterWidth(maskWidth, options.maxWidth);
7961
8400
  const height = width === maskWidth
7962
8401
  ? maskHeight
@@ -8008,8 +8447,8 @@ function createIdMaskFrame(instructions, options = {}) {
8008
8447
  */
8009
8448
  function writeMaskRuns(data, frameWidth, mask, detectionMaskId) {
8010
8449
  const counts = decodeCompressedRleCounts(mask.counts);
8011
- const maskWidth = mask.width;
8012
- const maskHeight = mask.height;
8450
+ const maskWidth = Math.floor(mask.width);
8451
+ const maskHeight = Math.floor(mask.height);
8013
8452
  let maskOffset = 0;
8014
8453
  for (let index = 0; index < counts.length; index += 1) {
8015
8454
  const runLength = counts[index] ?? 0;
@@ -8043,9 +8482,10 @@ function writeMaskRuns(data, frameWidth, mask, detectionMaskId) {
8043
8482
  */
8044
8483
  function writeScaledMaskRuns(data, axes, mask, detectionMaskId) {
8045
8484
  const counts = decodeCompressedRleCounts(mask.counts);
8046
- const maskWidth = mask.width;
8047
- const maskHeight = mask.height;
8485
+ const maskWidth = Math.floor(mask.width);
8486
+ const maskHeight = Math.floor(mask.height);
8048
8487
  let maskOffset = 0;
8488
+ const stride = axes.stride;
8049
8489
  for (let index = 0; index < counts.length; index += 1) {
8050
8490
  const runLength = counts[index] ?? 0;
8051
8491
  if (index % 2 === 0 || runLength <= 0) {
@@ -8054,17 +8494,30 @@ function writeScaledMaskRuns(data, axes, mask, detectionMaskId) {
8054
8494
  }
8055
8495
  let x = Math.floor(maskOffset / maskHeight);
8056
8496
  let y = maskOffset - x * maskHeight;
8057
- let column = axes.columns[x];
8058
- for (let step = 0; step < runLength; step += 1) {
8059
- if (x >= maskWidth) {
8060
- break;
8061
- }
8062
- data[axes.rows[y] + column] = detectionMaskId;
8063
- y += 1;
8497
+ let remaining = runLength;
8498
+ /* A run walks down one column and wraps into the next, so it splits into
8499
+ column-sized segments. Within a segment the destination offset only ever
8500
+ steps by the row pitch or stays put: createMaskAxisMap builds
8501
+ `rows[i] = min(height - 1, floor(i * scale)) * width` with scale <= 1, so
8502
+ consecutive entries differ by exactly 0 or exactly `width`. Walking the
8503
+ destination directly therefore touches the same bytes the per-pixel loop
8504
+ touched, and drops the bounds test, the wrap test and one typed-array
8505
+ load from every source pixel. Where the scale collapses several source
8506
+ rows onto one destination row, this writes that byte once instead of
8507
+ once per source row. */
8508
+ while (remaining > 0 && x < maskWidth) {
8509
+ const span = Math.min(remaining, maskHeight - y);
8510
+ const column = axes.columns[x];
8511
+ const first = axes.rows[y];
8512
+ const last = axes.rows[y + span - 1];
8513
+ for (let offset = first; offset <= last; offset += stride) {
8514
+ data[offset + column] = detectionMaskId;
8515
+ }
8516
+ remaining -= span;
8517
+ y += span;
8064
8518
  if (y === maskHeight) {
8065
8519
  y = 0;
8066
8520
  x += 1;
8067
- column = axes.columns[x];
8068
8521
  }
8069
8522
  }
8070
8523
  maskOffset += runLength;
@@ -8076,22 +8529,19 @@ function resolveRasterWidth(maskWidth, maxWidth) {
8076
8529
  : maskWidth;
8077
8530
  }
8078
8531
  /**
8079
- * A stroke is measured in texels of the raster it is drawn on, so a coarser
8080
- * raster measures it in coarser texels. A stroke of a texel or more keeps at
8081
- * least one, the thinnest line the shader can draw; a narrower one keeps its
8082
- * own width, which the shader draws as an inner boundary at any scale. The
8083
- * ceiling is the widest neighbourhood the shaders scan, so a wider stroke would
8084
- * be drawn at the ceiling anyway and every layer drawing the same annotation
8085
- * has to arrive at the same width.
8532
+ * Converts a source-grid stroke width into raster texels. Fractional widths
8533
+ * let coverage shaders draw a border within a texel. The ceiling bounds the
8534
+ * neighbourhood the shaders scan.
8086
8535
  */
8087
8536
  function resolveIdMaskStrokeTexels(strokeWidth, maskWidth, rasterWidth) {
8088
8537
  const scale = maskWidth > 0 ? rasterWidth / maskWidth : 1;
8089
- return Math.min(Math.max(strokeWidth * scale, Math.min(strokeWidth, 1)), MAX_ID_MASK_STROKE_WIDTH);
8538
+ return Math.min(strokeWidth * scale, MAX_ID_MASK_STROKE_WIDTH);
8090
8539
  }
8091
8540
  function createScaledMaskAxes(frame) {
8092
8541
  return {
8093
8542
  columns: createMaskAxisMap(frame.maskWidth, frame.width, 1),
8094
8543
  rows: createMaskAxisMap(frame.maskHeight, frame.height, frame.width),
8544
+ stride: frame.width,
8095
8545
  };
8096
8546
  }
8097
8547
  /** Rows hold their destination offset, so writing a run is two lookups. */
@@ -8320,6 +8770,658 @@ function includes(values, value) {
8320
8770
  : values.has(value);
8321
8771
  }
8322
8772
 
8773
+ const DEPTH_MANIFEST_SCHEMA = "supervision.depth-manifest";
8774
+ const PREVIEW_TOP_CODES = {
8775
+ full: 255,
8776
+ tv: 235,
8777
+ };
8778
+ /**
8779
+ * TV black, 16, is written for no depth, and a decoder that converts to RGB
8780
+ * returns every code below it as black too, so the reserved band must cover it.
8781
+ */
8782
+ const PREVIEW_MIN_RESERVED_CODES = { full: 0, tv: 16 };
8783
+ const depthPreviewLevels = new Set(Object.values(DepthPreviewLevels));
8784
+ const depthMapKinds = new Set(Object.values(DepthMapKind));
8785
+ const FRAME_INDEX_TOKEN = /\{index(?::0(\d{1,2}))?\}/g;
8786
+ /**
8787
+ * Reads the snake_case `depth.json` a producer writes into a
8788
+ * {@link DepthManifest}, and rejects anything a renderer could not honour.
8789
+ *
8790
+ * Messages name the offending wire field, such as
8791
+ * `depth.json: storage.scale must be a positive number`. Unknown fields are
8792
+ * ignored so newer producers stay readable.
8793
+ */
8794
+ function parseDepthManifest(json) {
8795
+ const root = readObject(json, "the manifest");
8796
+ if (root.schema !== DEPTH_MANIFEST_SCHEMA) {
8797
+ fail(`schema must be "${DEPTH_MANIFEST_SCHEMA}"`);
8798
+ }
8799
+ if (root.version !== 1) {
8800
+ fail(`version ${String(root.version)} is not supported; expected 1`);
8801
+ }
8802
+ const kind = root.kind;
8803
+ if (typeof kind !== "string" || !depthMapKinds.has(kind)) {
8804
+ fail(`kind must be one of ${[...depthMapKinds].join(", ")}`);
8805
+ }
8806
+ const depthKind = kind;
8807
+ const width = readPositiveInteger(root.width, "width");
8808
+ const height = readPositiveInteger(root.height, "height");
8809
+ const view = readOptionalString(root.view, "view");
8810
+ const storage = readObject(root.storage, "storage");
8811
+ if (storage.format !== "png16") {
8812
+ fail('storage.format must be "png16"');
8813
+ }
8814
+ const scale = readPositiveNumber(storage.scale, "storage.scale");
8815
+ if (storage.no_depth !== 0) {
8816
+ fail("storage.no_depth must be 0");
8817
+ }
8818
+ const camera = root.camera === undefined || root.camera === null
8819
+ ? undefined
8820
+ : readCamera(root.camera);
8821
+ const displayRange = readDisplayRange(root, depthKind);
8822
+ const hasImage = root.image !== undefined && root.image !== null;
8823
+ const hasFrames = root.frames !== undefined && root.frames !== null;
8824
+ if (hasImage === hasFrames) {
8825
+ fail("exactly one of image or frames must be present");
8826
+ }
8827
+ const image = hasImage ? readImage(root.image) : undefined;
8828
+ const frames = hasFrames ? readFrames(root.frames) : undefined;
8829
+ let preview;
8830
+ if (root.preview !== undefined && root.preview !== null) {
8831
+ if (!frames) {
8832
+ fail("preview is only valid next to frames");
8833
+ }
8834
+ if (depthKind !== DepthMapKind.DisparityPx) {
8835
+ fail(`preview is only supported for kind ${DepthMapKind.DisparityPx}`);
8836
+ }
8837
+ preview = readPreview(root.preview);
8838
+ }
8839
+ return {
8840
+ schema: DEPTH_MANIFEST_SCHEMA,
8841
+ version: 1,
8842
+ kind: depthKind,
8843
+ ...(view === undefined ? {} : { view }),
8844
+ width,
8845
+ height,
8846
+ storage: { format: "png16", noDepth: 0, scale },
8847
+ ...(camera === undefined ? {} : { camera }),
8848
+ ...(displayRange === undefined ? {} : { displayRange }),
8849
+ ...(image === undefined ? {} : { image }),
8850
+ ...(frames === undefined ? {} : { frames }),
8851
+ ...(preview === undefined ? {} : { preview }),
8852
+ };
8853
+ }
8854
+ /**
8855
+ * Expands a clip frame pattern for one frame index: `{index}` becomes the
8856
+ * plain number and `{index:06}` the number zero-padded to six digits.
8857
+ */
8858
+ function resolveDepthFrameFile(pattern, index) {
8859
+ if (!Number.isInteger(index) || index < 0) {
8860
+ throw new RangeError(`Depth frame index must be a non-negative integer, got ${index}.`);
8861
+ }
8862
+ return pattern.replace(FRAME_INDEX_TOKEN, (_token, width) => width === undefined
8863
+ ? String(index)
8864
+ : String(index).padStart(Number(width), "0"));
8865
+ }
8866
+ /**
8867
+ * Checks a host-supplied depth map's shape, without reading its sample values.
8868
+ */
8869
+ function validateDepthMap(map) {
8870
+ const reject = (message) => {
8871
+ throw new RangeError(`DepthMap ${message}.`);
8872
+ };
8873
+ if (!depthMapKinds.has(map.kind)) {
8874
+ reject(`kind must be one of ${[...depthMapKinds].join(", ")}`);
8875
+ }
8876
+ if (!isPositiveInteger(map.width) || !isPositiveInteger(map.height)) {
8877
+ reject("width and height must be positive integers");
8878
+ }
8879
+ const pixelCount = map.width * map.height;
8880
+ const samples = map.samples;
8881
+ if (samples?.encoding === "scaled16") {
8882
+ if (!(samples.values instanceof Uint16Array)) {
8883
+ reject("samples.values must be a Uint16Array for scaled16 samples");
8884
+ }
8885
+ if (!isPositiveNumber(samples.scale)) {
8886
+ reject("samples.scale must be a positive number");
8887
+ }
8888
+ }
8889
+ else if (samples?.encoding === "preview8") {
8890
+ if (!(samples.values instanceof Uint8Array)) {
8891
+ reject("samples.values must be a Uint8Array for preview8 samples");
8892
+ }
8893
+ const levels = samples.levels ?? DepthPreviewLevels.Full;
8894
+ if (!depthPreviewLevels.has(levels)) {
8895
+ reject(`samples.levels must be one of ${[...depthPreviewLevels].join(", ")}`);
8896
+ }
8897
+ if (!isReservedPreviewCode(samples.reservedMax, levels)) {
8898
+ reject(`samples.reservedMax must be ${describeReservedCodes(levels)}`);
8899
+ }
8900
+ if (!isRange(samples.range)) {
8901
+ reject("samples.range must have finite bounds with min < max");
8902
+ }
8903
+ }
8904
+ else {
8905
+ reject('samples.encoding must be "scaled16" or "preview8"');
8906
+ }
8907
+ if (samples.values.length !== pixelCount) {
8908
+ reject(`samples.values has ${samples.values.length} values for ${map.width}x${map.height} pixels`);
8909
+ }
8910
+ if (map.camera !== undefined && !isCamera(map.camera)) {
8911
+ reject("camera needs finite fxPx and baselineM above 0, and finite optional offsets");
8912
+ }
8913
+ if (map.displayRange !== undefined && !isRange(map.displayRange)) {
8914
+ reject("displayRange must have finite bounds with min < max");
8915
+ }
8916
+ if (map.confidence !== undefined) {
8917
+ if (!(map.confidence instanceof Uint8Array)) {
8918
+ reject("confidence must be a Uint8Array");
8919
+ }
8920
+ if (map.confidence.length !== pixelCount) {
8921
+ reject(`confidence has ${map.confidence.length} values for ${map.width}x${map.height} pixels`);
8922
+ }
8923
+ }
8924
+ }
8925
+ function readCamera(value) {
8926
+ const camera = readObject(value, "camera");
8927
+ const fxPx = readPositiveNumber(camera.fx_px, "camera.fx_px");
8928
+ const baselineM = readPositiveNumber(camera.baseline_m, "camera.baseline_m");
8929
+ const doffsPx = readOptionalFinite(camera.doffs_px, "camera.doffs_px");
8930
+ const cxPx = readOptionalFinite(camera.cx_px, "camera.cx_px");
8931
+ const cyPx = readOptionalFinite(camera.cy_px, "camera.cy_px");
8932
+ return {
8933
+ fxPx,
8934
+ baselineM,
8935
+ ...(doffsPx === undefined ? {} : { doffsPx }),
8936
+ ...(cxPx === undefined ? {} : { cxPx }),
8937
+ ...(cyPx === undefined ? {} : { cyPx }),
8938
+ };
8939
+ }
8940
+ /**
8941
+ * `display_range` is in the kind's unit; `display_range_px` is its name for
8942
+ * disparity, kept because disparity producers already write it.
8943
+ */
8944
+ function readDisplayRange(root, kind) {
8945
+ const inKindUnit = readOptionalRange(root.display_range, "display_range");
8946
+ const inPixels = readOptionalRange(root.display_range_px, "display_range_px");
8947
+ if (inPixels !== undefined && kind !== DepthMapKind.DisparityPx) {
8948
+ fail(`display_range_px is only valid for kind ${DepthMapKind.DisparityPx}; use display_range`);
8949
+ }
8950
+ if (inKindUnit !== undefined &&
8951
+ inPixels !== undefined &&
8952
+ (inKindUnit.min !== inPixels.min || inKindUnit.max !== inPixels.max)) {
8953
+ fail("display_range and display_range_px disagree");
8954
+ }
8955
+ return inKindUnit ?? inPixels;
8956
+ }
8957
+ function readImage(value) {
8958
+ const image = readObject(value, "image");
8959
+ const file = readFileName(image.file, "image.file");
8960
+ const confidenceFile = readOptionalFileName(image.confidence_file, "image.confidence_file");
8961
+ return {
8962
+ file,
8963
+ ...(confidenceFile === undefined ? {} : { confidenceFile }),
8964
+ };
8965
+ }
8966
+ function readFrames(value) {
8967
+ const frames = readObject(value, "frames");
8968
+ const count = readPositiveInteger(frames.count, "frames.count");
8969
+ const exact = readFramePattern(frames.exact, "frames.exact");
8970
+ const confidence = frames.confidence === undefined || frames.confidence === null
8971
+ ? undefined
8972
+ : readFramePattern(frames.confidence, "frames.confidence");
8973
+ const timesS = frames.times_s === undefined || frames.times_s === null
8974
+ ? undefined
8975
+ : readFrameTimes(frames.times_s, count);
8976
+ return {
8977
+ count,
8978
+ exact,
8979
+ ...(confidence === undefined ? {} : { confidence }),
8980
+ ...(timesS === undefined ? {} : { timesS }),
8981
+ };
8982
+ }
8983
+ function readFrameTimes(value, count) {
8984
+ if (!Array.isArray(value) || value.length !== count) {
8985
+ fail(`frames.times_s must be an array of frames.count (${count}) times`);
8986
+ }
8987
+ const times = value;
8988
+ times.forEach((time, index) => {
8989
+ if (typeof time !== "number" || !Number.isFinite(time) || time < 0) {
8990
+ fail(`frames.times_s[${index}] must be a finite number of seconds >= 0`);
8991
+ }
8992
+ if (index > 0 && time <= times[index - 1]) {
8993
+ fail(`frames.times_s must strictly increase at index ${index}`);
8994
+ }
8995
+ });
8996
+ return [...times];
8997
+ }
8998
+ /** A preview without `levels` predates TV range and is read as full range. */
8999
+ function readPreview(value) {
9000
+ const preview = readObject(value, "preview");
9001
+ const file = readFileName(preview.file, "preview.file");
9002
+ const codec = readOptionalString(preview.codec, "preview.codec");
9003
+ const levels = preview.levels ?? DepthPreviewLevels.Full;
9004
+ const reservedMax = preview.reserved_max;
9005
+ if (typeof levels !== "string" || !depthPreviewLevels.has(levels)) {
9006
+ fail(`preview.levels must be one of ${[...depthPreviewLevels].join(", ")}`);
9007
+ }
9008
+ const previewLevels = levels;
9009
+ if (!isReservedPreviewCode(reservedMax, previewLevels)) {
9010
+ fail(`preview.reserved_max must be ${describeReservedCodes(previewLevels)}`);
9011
+ }
9012
+ const range = readOptionalRange(preview.range_px, "preview.range_px");
9013
+ if (range === undefined) {
9014
+ fail("preview.range_px is required");
9015
+ }
9016
+ return {
9017
+ file,
9018
+ ...(codec === undefined ? {} : { codec }),
9019
+ levels: previewLevels,
9020
+ reservedMax,
9021
+ range,
9022
+ };
9023
+ }
9024
+ /** The code that stands for the top of a preview's range: 255, or 235 in TV range. */
9025
+ function depthPreviewTopCode(levels = "full") {
9026
+ return PREVIEW_TOP_CODES[levels];
9027
+ }
9028
+ function readFramePattern(value, path) {
9029
+ const pattern = readFileName(value, path);
9030
+ if (!new RegExp(FRAME_INDEX_TOKEN.source).test(pattern)) {
9031
+ fail(`${path} must contain {index} or a padded {index:06}`);
9032
+ }
9033
+ return pattern;
9034
+ }
9035
+ function readOptionalRange(value, path) {
9036
+ if (value === undefined || value === null) {
9037
+ return undefined;
9038
+ }
9039
+ if (!Array.isArray(value) ||
9040
+ value.length !== 2 ||
9041
+ !value.every((bound) => typeof bound === "number" && Number.isFinite(bound))) {
9042
+ fail(`${path} must be two finite numbers [low, high]`);
9043
+ }
9044
+ const [min, max] = value;
9045
+ if (!(min < max)) {
9046
+ fail(`${path} must have low < high`);
9047
+ }
9048
+ return { min, max };
9049
+ }
9050
+ function readObject(value, path) {
9051
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9052
+ fail(`${path} must be an object`);
9053
+ }
9054
+ return value;
9055
+ }
9056
+ function readPositiveInteger(value, path) {
9057
+ if (!isPositiveInteger(value)) {
9058
+ fail(`${path} must be a positive integer`);
9059
+ }
9060
+ return value;
9061
+ }
9062
+ function readPositiveNumber(value, path) {
9063
+ if (!isPositiveNumber(value)) {
9064
+ fail(`${path} must be a positive number`);
9065
+ }
9066
+ return value;
9067
+ }
9068
+ function readOptionalFinite(value, path) {
9069
+ if (value === undefined || value === null) {
9070
+ return undefined;
9071
+ }
9072
+ if (typeof value !== "number" || !Number.isFinite(value)) {
9073
+ fail(`${path} must be a finite number`);
9074
+ }
9075
+ return value;
9076
+ }
9077
+ function readOptionalString(value, path) {
9078
+ if (value === undefined || value === null) {
9079
+ return undefined;
9080
+ }
9081
+ if (typeof value !== "string") {
9082
+ fail(`${path} must be a string`);
9083
+ }
9084
+ return value;
9085
+ }
9086
+ function readFileName(value, path) {
9087
+ if (typeof value !== "string" || value.length === 0) {
9088
+ fail(`${path} must be a non-empty file name`);
9089
+ }
9090
+ return value;
9091
+ }
9092
+ function readOptionalFileName(value, path) {
9093
+ return value === undefined || value === null
9094
+ ? undefined
9095
+ : readFileName(value, path);
9096
+ }
9097
+ function isPositiveInteger(value) {
9098
+ return typeof value === "number" && Number.isInteger(value) && value > 0;
9099
+ }
9100
+ function isPositiveNumber(value) {
9101
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
9102
+ }
9103
+ /** At least two valid codes must remain, or the preview span is zero. */
9104
+ function isReservedPreviewCode(value, levels) {
9105
+ return (typeof value === "number" &&
9106
+ Number.isInteger(value) &&
9107
+ value >= PREVIEW_MIN_RESERVED_CODES[levels] &&
9108
+ value <= PREVIEW_TOP_CODES[levels] - 2);
9109
+ }
9110
+ function describeReservedCodes(levels) {
9111
+ return `an integer from ${PREVIEW_MIN_RESERVED_CODES[levels]} to ${PREVIEW_TOP_CODES[levels] - 2} at ${levels} levels`;
9112
+ }
9113
+ function isRange(range) {
9114
+ return (range !== undefined &&
9115
+ Number.isFinite(range.min) &&
9116
+ Number.isFinite(range.max) &&
9117
+ range.min < range.max);
9118
+ }
9119
+ function isCamera(camera) {
9120
+ return (isPositiveNumber(camera.fxPx) &&
9121
+ isPositiveNumber(camera.baselineM) &&
9122
+ [camera.doffsPx, camera.cxPx, camera.cyPx].every((value) => value === undefined || Number.isFinite(value)));
9123
+ }
9124
+ function fail(message) {
9125
+ throw new RangeError(`depth.json: ${message}`);
9126
+ }
9127
+
9128
+ /** Percentiles `"auto"` colours between: outliers and flying pixels fall outside. */
9129
+ const DEFAULT_DEPTH_PERCENTILE_LOW = 0.02;
9130
+ const DEFAULT_DEPTH_PERCENTILE_HIGH = 0.98;
9131
+ /** Below this share of valid samples a percentile says nothing about the scene. */
9132
+ const MIN_VALID_SAMPLE_SHARE = 0.01;
9133
+ const PERCENTILE_SAMPLE_STRIDE = 2;
9134
+ const FALLBACK_RANGE = { min: 0, max: 1 };
9135
+ /**
9136
+ * Metric depth needs a camera to come out of disparity, and relative inverse
9137
+ * depth has no metric depth at all, so both fall back to disparity.
9138
+ */
9139
+ function resolveDepthQuantity(kind, quantity = DepthQuantity.Disparity, camera) {
9140
+ if (quantity === DepthQuantity.Disparity) {
9141
+ return { fellBack: false, quantity };
9142
+ }
9143
+ if (kind === DepthMapKind.DepthM ||
9144
+ (kind === DepthMapKind.DisparityPx && camera !== undefined)) {
9145
+ return { fellBack: false, quantity: DepthQuantity.Depth };
9146
+ }
9147
+ return { fellBack: true, quantity: DepthQuantity.Disparity };
9148
+ }
9149
+ function resolveDepthValueConversion(map, quantity) {
9150
+ const resolved = resolveDepthQuantity(map.kind, quantity, map.camera);
9151
+ const nearIsLow = resolved.quantity === DepthQuantity.Depth;
9152
+ const identity = {
9153
+ ...resolved,
9154
+ innerOffset: 0,
9155
+ nearIsLow,
9156
+ numerator: 1,
9157
+ outerOffset: 0,
9158
+ reciprocal: false,
9159
+ };
9160
+ const camera = map.camera;
9161
+ const focalBaseline = camera ? camera.fxPx * camera.baselineM : 1;
9162
+ const doffs = camera?.doffsPx ?? 0;
9163
+ if (map.kind === DepthMapKind.DisparityPx &&
9164
+ resolved.quantity === DepthQuantity.Depth) {
9165
+ // Z = fx * B / (d + doffs)
9166
+ return {
9167
+ ...identity,
9168
+ innerOffset: doffs,
9169
+ numerator: focalBaseline,
9170
+ reciprocal: true,
9171
+ };
9172
+ }
9173
+ if (map.kind === DepthMapKind.DepthM &&
9174
+ resolved.quantity === DepthQuantity.Disparity) {
9175
+ // d = fx * B / Z - doffs, or plain inverse depth without a camera.
9176
+ return {
9177
+ ...identity,
9178
+ numerator: focalBaseline,
9179
+ outerOffset: doffs === 0 ? 0 : -doffs,
9180
+ reciprocal: true,
9181
+ };
9182
+ }
9183
+ return identity;
9184
+ }
9185
+ function convertDepthValue(value, conversion) {
9186
+ return conversion.reciprocal
9187
+ ? conversion.numerator / (value + conversion.innerOffset) +
9188
+ conversion.outerOffset
9189
+ : value;
9190
+ }
9191
+ /**
9192
+ * Converts a range in the map kind's unit, swapping its bounds under a
9193
+ * reciprocal. Null when a bound has no image, such as disparity at or below
9194
+ * `-doffsPx` for depth, which would be infinite or negative.
9195
+ */
9196
+ function convertDepthRange(range, conversion) {
9197
+ if (!hasConvertedValue(Math.min(range.min, range.max), conversion)) {
9198
+ return null;
9199
+ }
9200
+ const first = convertDepthValue(range.min, conversion);
9201
+ const second = convertDepthValue(range.max, conversion);
9202
+ const min = Math.min(first, second);
9203
+ const max = Math.max(first, second);
9204
+ return Number.isFinite(min) && Number.isFinite(max) && min < max
9205
+ ? { max, min }
9206
+ : null;
9207
+ }
9208
+ /** The value in the map kind's unit, or null where there is no depth. */
9209
+ function decodeDepthSample(stored, samples) {
9210
+ if (samples.encoding === "scaled16") {
9211
+ return stored === 0 ? null : stored / samples.scale;
9212
+ }
9213
+ if (stored <= samples.reservedMax) {
9214
+ return null;
9215
+ }
9216
+ const span = depthPreviewSpan(samples);
9217
+ return (samples.range.min +
9218
+ (Math.min(stored - samples.reservedMax - 1, span) / span) *
9219
+ (samples.range.max - samples.range.min));
9220
+ }
9221
+ /**
9222
+ * Steps between the lowest valid preview code and the top one, so one step
9223
+ * is `(range.max - range.min) / span`.
9224
+ */
9225
+ function depthPreviewSpan(samples) {
9226
+ return depthPreviewTopCode(samples.levels) - samples.reservedMax - 1;
9227
+ }
9228
+ /**
9229
+ * This map's own percentile range in the coloured quantity's unit, or null
9230
+ * when fewer than 1 % of the samples it reads hold depth.
9231
+ *
9232
+ * It depends on this map alone, so a frame always gets the same range. Pass
9233
+ * the result back as an explicit range to keep colours steady across frames.
9234
+ */
9235
+ function computeDepthPercentileRange(map, options = {}) {
9236
+ const low = options.low ?? DEFAULT_DEPTH_PERCENTILE_LOW;
9237
+ const high = options.high ?? DEFAULT_DEPTH_PERCENTILE_HIGH;
9238
+ if (!Number.isFinite(low) ||
9239
+ !Number.isFinite(high) ||
9240
+ low < 0 ||
9241
+ high > 1 ||
9242
+ low >= high) {
9243
+ throw new RangeError("Depth percentiles need 0 <= low < high <= 1, as fractions.");
9244
+ }
9245
+ const conversion = resolveDepthValueConversion(map, options.quantity);
9246
+ const histogram = histogramDepthCodes(map, conversion);
9247
+ if (histogram.valid < histogram.sampled * MIN_VALID_SAMPLE_SHARE) {
9248
+ return null;
9249
+ }
9250
+ return rangeFromHistogram(map, histogram, low, high, conversion);
9251
+ }
9252
+ /**
9253
+ * The range a renderer colours between, in the coloured quantity's unit.
9254
+ *
9255
+ * `autoRange` lets a caller that already computed this map's percentile range
9256
+ * skip the histogram. A range that cannot be honoured falls back step by step:
9257
+ * the display range to the percentile range, and that to the full range of
9258
+ * the valid samples, so a sparse frame still colours what it has.
9259
+ */
9260
+ function resolveDepthDisplayRange(map, options, conversion, autoRange) {
9261
+ const range = options.range ?? "clip";
9262
+ if (typeof range === "object") {
9263
+ return range;
9264
+ }
9265
+ if (range === "clip" && map.displayRange) {
9266
+ const clip = convertDepthRange(map.displayRange, conversion);
9267
+ if (clip) {
9268
+ return clip;
9269
+ }
9270
+ }
9271
+ const percentile = autoRange !== undefined
9272
+ ? autoRange
9273
+ : computeDepthPercentileRange(map, { quantity: conversion.quantity });
9274
+ if (percentile) {
9275
+ return percentile;
9276
+ }
9277
+ const histogram = histogramDepthCodes(map, conversion);
9278
+ return histogram.valid > 0
9279
+ ? rangeFromHistogram(map, histogram, 0, 1, conversion)
9280
+ : FALLBACK_RANGE;
9281
+ }
9282
+ /** Resolves a renderer's quantity and range for one map into shader terms. */
9283
+ function resolveDepthColorMapping(map, options = {}, autoRange) {
9284
+ const conversion = resolveDepthValueConversion(map, options.quantity);
9285
+ const range = resolveDepthDisplayRange(map, options, conversion, autoRange);
9286
+ return { ...conversion, hi: range.max, lo: range.min };
9287
+ }
9288
+ function histogramDepthCodes(map, conversion) {
9289
+ const { samples } = map;
9290
+ const values = samples.values;
9291
+ const counts = new Uint32Array(samples.encoding === "scaled16" ? 65_536 : 256);
9292
+ let firstValid = samples.encoding === "scaled16" ? 1 : samples.reservedMax + 1;
9293
+ // Decoded values never fall as codes rise, so the codes the conversion
9294
+ // gives a value for start at one code.
9295
+ while (firstValid < counts.length &&
9296
+ !hasConvertedValue(decodeDepthSample(firstValid, samples), conversion)) {
9297
+ firstValid += 1;
9298
+ }
9299
+ let sampled = 0;
9300
+ let valid = 0;
9301
+ for (let y = 0; y < map.height; y += PERCENTILE_SAMPLE_STRIDE) {
9302
+ const row = y * map.width;
9303
+ for (let x = 0; x < map.width; x += PERCENTILE_SAMPLE_STRIDE) {
9304
+ const code = values[row + x];
9305
+ sampled += 1;
9306
+ if (code >= firstValid) {
9307
+ counts[code] += 1;
9308
+ valid += 1;
9309
+ }
9310
+ }
9311
+ }
9312
+ return { counts, sampled, valid };
9313
+ }
9314
+ /**
9315
+ * Exact percentiles over the stored codes, taken by nearest rank and then
9316
+ * mapped through the monotonic conversion to the coloured quantity. A frame
9317
+ * whose chosen codes coincide, preview codes above the top one counting as
9318
+ * the top one, is widened by one code, so the range stays usable as an
9319
+ * explicit range.
9320
+ */
9321
+ function rangeFromHistogram(map, histogram, low, high, conversion) {
9322
+ const topCode = map.samples.encoding === "scaled16"
9323
+ ? histogram.counts.length - 1
9324
+ : depthPreviewTopCode(map.samples.levels);
9325
+ const lastRank = histogram.valid - 1;
9326
+ let lowCode = Math.min(topCode, codeAtRank(histogram.counts, Math.round(low * lastRank)));
9327
+ let highCode = Math.min(topCode, codeAtRank(histogram.counts, Math.round(high * lastRank)));
9328
+ if (lowCode === highCode) {
9329
+ if (highCode < topCode)
9330
+ highCode += 1;
9331
+ else
9332
+ lowCode -= 1;
9333
+ }
9334
+ const lowValue = decodeDepthSample(lowCode, map.samples) ?? 0;
9335
+ const highValue = decodeDepthSample(highCode, map.samples) ?? 0;
9336
+ return (convertDepthRange({ max: highValue, min: lowValue }, conversion) ??
9337
+ FALLBACK_RANGE);
9338
+ }
9339
+ /**
9340
+ * Whether `value` converts to the coloured quantity: depth from disparity at
9341
+ * or below `-doffsPx` would be infinite or negative, and `readDepthAt` gives
9342
+ * it none.
9343
+ */
9344
+ function hasConvertedValue(value, conversion) {
9345
+ return !conversion.reciprocal || value + conversion.innerOffset > 0;
9346
+ }
9347
+ function codeAtRank(counts, rank) {
9348
+ let seen = 0;
9349
+ for (let code = 0; code < counts.length; code += 1) {
9350
+ seen += counts[code];
9351
+ if (seen > rank) {
9352
+ return code;
9353
+ }
9354
+ }
9355
+ return counts.length - 1;
9356
+ }
9357
+
9358
+ /**
9359
+ * Reads the depth map pixel under a point.
9360
+ *
9361
+ * `coordinateSpace` is the size `point` is measured in, normally the media's;
9362
+ * the map is stretched over it the way the renderer draws it. Without it the
9363
+ * point is in map pixels. Returns null outside the map.
9364
+ */
9365
+ function readDepthAt(map, point, coordinateSpace) {
9366
+ const scaleX = coordinateSpace ? map.width / coordinateSpace.width : 1;
9367
+ const scaleY = coordinateSpace ? map.height / coordinateSpace.height : 1;
9368
+ const x = Math.floor(point.x * scaleX);
9369
+ const y = Math.floor(point.y * scaleY);
9370
+ if (!Number.isFinite(x) ||
9371
+ !Number.isFinite(y) ||
9372
+ x < 0 ||
9373
+ y < 0 ||
9374
+ x >= map.width ||
9375
+ y >= map.height) {
9376
+ return null;
9377
+ }
9378
+ const index = y * map.width + x;
9379
+ const { samples } = map;
9380
+ const stored = samples.values[index];
9381
+ const value = decodeDepthSample(stored, samples);
9382
+ const step = samples.encoding === "scaled16"
9383
+ ? 1 / samples.scale
9384
+ : (samples.range.max - samples.range.min) / depthPreviewSpan(samples);
9385
+ const confidence = map.confidence?.[index];
9386
+ const readout = {
9387
+ precision: samples.encoding === "scaled16" ? "exact" : "preview",
9388
+ step,
9389
+ stored,
9390
+ valid: value !== null,
9391
+ x,
9392
+ y,
9393
+ ...(confidence === undefined ? {} : { confidence: confidence / 255 }),
9394
+ };
9395
+ if (value === null) {
9396
+ return readout;
9397
+ }
9398
+ const camera = map.camera;
9399
+ const focalBaseline = camera ? camera.fxPx * camera.baselineM : undefined;
9400
+ const doffs = camera?.doffsPx ?? 0;
9401
+ switch (map.kind) {
9402
+ case DepthMapKind.DisparityPx: {
9403
+ const shifted = value + doffs;
9404
+ return {
9405
+ ...readout,
9406
+ disparityPx: value,
9407
+ ...(focalBaseline !== undefined && shifted > 0
9408
+ ? { depthM: focalBaseline / shifted }
9409
+ : {}),
9410
+ };
9411
+ }
9412
+ case DepthMapKind.DepthM:
9413
+ return {
9414
+ ...readout,
9415
+ depthM: value,
9416
+ ...(focalBaseline !== undefined
9417
+ ? { disparityPx: focalBaseline / value - doffs }
9418
+ : {}),
9419
+ };
9420
+ default:
9421
+ return { ...readout, relativeInverse: value };
9422
+ }
9423
+ }
9424
+
8323
9425
  var MediaRendererFit;
8324
9426
  (function (MediaRendererFit) {
8325
9427
  /**
@@ -8481,5 +9583,5 @@ var MediaSessionActivityStatus;
8481
9583
  MediaSessionActivityStatus["Waiting"] = "waiting";
8482
9584
  })(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));
8483
9585
 
8484
- export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BaseOrientedBoxStyle, BasePercentageBarStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, PercentageBarPlacement, PlaybackGateReach, RegionRendererComposeMode, RegionRendererCoverageKind, RegionRendererMediaEffectKind, RegionRendererRegionKind, RegionRendererSizeSpace, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractDetectionMaskRectRuns, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationRendererStyleFields, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveIdMaskPaletteId, resolveIdMaskStrokeTexels, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames, writeIdMaskPaletteEntry };
9586
+ export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BaseOrientedBoxStyle, BasePercentageBarStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DepthColormap, DepthMapKind, DepthPreviewLevels, DepthQuantity, DepthSampling, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS, PercentageBarPlacement, PlaybackGateReach, RegionRendererComposeMode, RegionRendererCoverageKind, RegionRendererMediaEffectKind, RegionRendererRegionKind, RegionRendererSizeSpace, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDepthPercentileRange, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDepthColormapLut, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, depthColormapColors, depthPreviewTopCode, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractDetectionMaskRectRuns, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, isDepthColormap, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, parseDepthManifest, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, readDepthAt, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationRendererStyleFields, resolveAnnotationStyleState, resolveContrastTextColor, resolveDepthColorMapping, resolveDepthFrameFile, resolveDepthQuantity, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveIdMaskPaletteId, resolveIdMaskStrokeTexels, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, sortedDetectionFrames, topLeftRectToCenterRect, validateDepthMap, validateDetectionFrames, writeIdMaskPaletteEntry };
8485
9587
  //# sourceMappingURL=index.js.map