supervision 0.1.2 → 0.1.4

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 (90) hide show
  1. package/dist/index.d.ts +11 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1232 -107
  4. package/dist/index.js.map +1 -1
  5. package/dist/mask-preparation.worker.js +43 -9
  6. package/dist/mask-preparation.worker.js.map +1 -1
  7. package/dist/render-preparation/mask-frame-artifact.d.ts +6 -0
  8. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  9. package/dist/render-preparation/mask-frame-compositor.d.ts +1 -0
  10. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  11. package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
  12. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +1 -0
  13. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  14. package/dist/renderers/annotation-shape-styles.d.ts +17 -0
  15. package/dist/renderers/annotation-shape-styles.d.ts.map +1 -0
  16. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  17. package/dist/renderers/media-renderer-scene.d.ts +17 -2
  18. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  19. package/dist/renderers/pixi-annotation-overlay-layer.d.ts.map +1 -1
  20. package/dist/renderers/pixi-focus-layer.d.ts +1 -0
  21. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  22. package/dist/renderers/pixi-interaction-layer.d.ts +2 -0
  23. package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
  24. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +2 -1
  25. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  26. package/dist/renderers/pixi-mask-halo.d.ts +94 -0
  27. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -0
  28. package/dist/renderers/pixi-mask-layer.d.ts +10 -1
  29. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  30. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  31. package/dist/renderers/pixi-path.d.ts +13 -7
  32. package/dist/renderers/pixi-path.d.ts.map +1 -1
  33. package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
  34. package/dist/renderers/pixi-region-layer.d.ts +42 -0
  35. package/dist/renderers/pixi-region-layer.d.ts.map +1 -0
  36. package/dist/renderers/pixi-scene-layer-slot.d.ts +1 -0
  37. package/dist/renderers/pixi-scene-layer-slot.d.ts.map +1 -1
  38. package/dist/renderers/pixi-vector-layer.d.ts +3 -1
  39. package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
  40. package/dist/sessions/media-session.d.ts.map +1 -1
  41. package/dist/types/media-renderer.d.ts +1 -1
  42. package/dist/types/media-renderer.d.ts.map +1 -1
  43. package/node_modules/supervision-js-core/dist/index.d.ts +19 -3
  44. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  45. package/node_modules/supervision-js-core/dist/index.js +944 -157
  46. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  47. package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts +18 -0
  48. package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts.map +1 -1
  49. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-presentation.d.ts +9 -0
  50. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-presentation.d.ts.map +1 -0
  51. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts +47 -0
  52. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts.map +1 -0
  53. package/node_modules/supervision-js-core/dist/styles/box-corner-style.d.ts +14 -0
  54. package/node_modules/supervision-js-core/dist/styles/box-corner-style.d.ts.map +1 -0
  55. package/node_modules/supervision-js-core/dist/styles/box-style.d.ts.map +1 -1
  56. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts +38 -0
  57. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts.map +1 -1
  58. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts.map +1 -1
  59. package/node_modules/supervision-js-core/dist/styles/keypoint-style.d.ts.map +1 -1
  60. package/node_modules/supervision-js-core/dist/styles/marker-style.d.ts +14 -0
  61. package/node_modules/supervision-js-core/dist/styles/marker-style.d.ts.map +1 -0
  62. package/node_modules/supervision-js-core/dist/styles/polygon-style.d.ts.map +1 -1
  63. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts.map +1 -1
  64. package/node_modules/supervision-js-core/dist/styles/source-presentation.d.ts +18 -2
  65. package/node_modules/supervision-js-core/dist/styles/source-presentation.d.ts.map +1 -1
  66. package/node_modules/supervision-js-core/dist/styles/stroke-style.d.ts +12 -0
  67. package/node_modules/supervision-js-core/dist/styles/stroke-style.d.ts.map +1 -0
  68. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +197 -0
  69. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -0
  70. package/node_modules/supervision-js-core/dist/types/box-corner-style.d.ts +31 -0
  71. package/node_modules/supervision-js-core/dist/types/box-corner-style.d.ts.map +1 -0
  72. package/node_modules/supervision-js-core/dist/types/box-style.d.ts +4 -17
  73. package/node_modules/supervision-js-core/dist/types/box-style.d.ts.map +1 -1
  74. package/node_modules/supervision-js-core/dist/types/ellipse-style.d.ts +30 -0
  75. package/node_modules/supervision-js-core/dist/types/ellipse-style.d.ts.map +1 -0
  76. package/node_modules/supervision-js-core/dist/types/interaction.d.ts +12 -0
  77. package/node_modules/supervision-js-core/dist/types/interaction.d.ts.map +1 -1
  78. package/node_modules/supervision-js-core/dist/types/marker-style.d.ts +46 -0
  79. package/node_modules/supervision-js-core/dist/types/marker-style.d.ts.map +1 -0
  80. package/node_modules/supervision-js-core/dist/types/mask-halo-style.d.ts +29 -0
  81. package/node_modules/supervision-js-core/dist/types/mask-halo-style.d.ts.map +1 -0
  82. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +26 -1
  83. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  84. package/node_modules/supervision-js-core/dist/types/paint-style.d.ts +36 -0
  85. package/node_modules/supervision-js-core/dist/types/paint-style.d.ts.map +1 -0
  86. package/node_modules/supervision-js-core/dist/types/shape-style.d.ts +112 -0
  87. package/node_modules/supervision-js-core/dist/types/shape-style.d.ts.map +1 -0
  88. package/node_modules/supervision-js-core/dist/utils/shape-geometry.d.ts +50 -0
  89. package/node_modules/supervision-js-core/dist/utils/shape-geometry.d.ts.map +1 -0
  90. package/package.json +1 -1
@@ -1928,6 +1928,29 @@ function createDetectionPickKey(pick) {
1928
1928
  pick.geometryIndex ?? "geometry",
1929
1929
  ].join(":");
1930
1930
  }
1931
+ /**
1932
+ * Compares the frame-independent membership of two ordered selections.
1933
+ *
1934
+ * Detection ids retain their runtime type, so numeric `1` and string `"1"`
1935
+ * remain distinct identities. Anonymous detections keep their frame-local
1936
+ * index identity.
1937
+ */
1938
+ function haveSameDetectionPickIdentities(current, next) {
1939
+ if (current.length !== next.length) {
1940
+ return false;
1941
+ }
1942
+ return current.every((pick, index) => {
1943
+ const nextPick = next[index];
1944
+ if (!nextPick) {
1945
+ return false;
1946
+ }
1947
+ const id = pick.detection.id;
1948
+ const nextId = nextPick.detection.id;
1949
+ return id === undefined || nextId === undefined
1950
+ ? id === nextId && pick.detectionIndex === nextPick.detectionIndex
1951
+ : typeof id === typeof nextId && id === nextId;
1952
+ });
1953
+ }
1931
1954
  function rebaseDetectionPickToFrame(pick, frame) {
1932
1955
  if (!pick || !frame) {
1933
1956
  return null;
@@ -1953,13 +1976,55 @@ function rebaseDetectionPickToFrame(pick, frame) {
1953
1976
  ? rebasedPick
1954
1977
  : null;
1955
1978
  }
1979
+ /**
1980
+ * Carries a pick forward onto a different frame of the same timeline.
1981
+ *
1982
+ * Same-frame rebases behave exactly like `rebaseDetectionPickToFrame`. Across
1983
+ * frames, the pick follows its detection only when the detection id is defined
1984
+ * and unique in both frames, so anonymous and duplicate-id detections keep the
1985
+ * per-frame lifetime they have today. Returns `null` when the detection has no
1986
+ * followable identity or has left the frame.
1987
+ */
1988
+ function followDetectionPickAcrossFrames(pick, frame) {
1989
+ const rebasedPick = rebaseDetectionPickToFrame(pick, frame);
1990
+ if (rebasedPick || !pick || !frame) {
1991
+ return rebasedPick;
1992
+ }
1993
+ const id = pick.detection.id;
1994
+ if (id === undefined ||
1995
+ !hasUniqueDetectionId(pick.frame, id) ||
1996
+ !hasUniqueDetectionId(frame, id)) {
1997
+ return null;
1998
+ }
1999
+ const detectionIndex = frame.detections.findIndex((detection) => detection.id === id);
2000
+ const detection = frame.detections[detectionIndex];
2001
+ if (!detection) {
2002
+ return null;
2003
+ }
2004
+ return {
2005
+ detection,
2006
+ detectionIndex,
2007
+ frame,
2008
+ geometryIndex: pick.geometryIndex,
2009
+ mediaTime: frame.mediaTime,
2010
+ point: pick.point,
2011
+ target: pick.target,
2012
+ };
2013
+ }
1956
2014
  function detectionPickKey(pick) {
1957
2015
  if (pick.detection.id === undefined) {
1958
2016
  return ["anonymous", pick.detectionIndex];
1959
2017
  }
2018
+ const idType = typeof pick.detection.id;
1960
2019
  return hasUniqueDetectionId(pick.frame, pick.detection.id)
1961
- ? ["id", String(pick.detection.id)]
1962
- : ["duplicate-id", String(pick.detection.id), "index", pick.detectionIndex];
2020
+ ? ["id", idType, String(pick.detection.id)]
2021
+ : [
2022
+ "duplicate-id",
2023
+ idType,
2024
+ String(pick.detection.id),
2025
+ "index",
2026
+ pick.detectionIndex,
2027
+ ];
1963
2028
  }
1964
2029
  function hasUniqueDetectionId(frame, id) {
1965
2030
  let count = 0;
@@ -2652,17 +2717,46 @@ function resolveStyleValue(value, detection, context) {
2652
2717
  : value;
2653
2718
  }
2654
2719
 
2720
+ /**
2721
+ * Resolves the renderer-neutral stroke contract without dropping optional
2722
+ * geometry controls as styles move from public configuration to draw
2723
+ * instructions.
2724
+ *
2725
+ * Keep this as the single normalization point for every built-in style. That
2726
+ * makes additions to `StrokeStyle` deliberate instead of relying on each style
2727
+ * implementation to copy a growing property list correctly.
2728
+ */
2729
+ function resolveStrokeStyle(stroke, defaults) {
2730
+ const alignment = stroke?.alignment ?? defaults.alignment;
2731
+ const cap = stroke?.cap ?? defaults.cap;
2732
+ const dash = stroke?.dash ?? defaults.dash;
2733
+ const join = stroke?.join ?? defaults.join;
2734
+ const miterLimit = stroke?.miterLimit ?? defaults.miterLimit;
2735
+ return {
2736
+ alpha: stroke?.alpha ?? defaults.alpha,
2737
+ color: stroke?.color ?? defaults.color,
2738
+ width: stroke?.width ?? defaults.width,
2739
+ ...(alignment === undefined ? {} : { alignment }),
2740
+ ...(cap === undefined ? {} : { cap }),
2741
+ ...(dash === undefined ? {} : { dash }),
2742
+ ...(join === undefined ? {} : { join }),
2743
+ ...(miterLimit === undefined ? {} : { miterLimit }),
2744
+ };
2745
+ }
2746
+
2747
+ /** Alignment for strokes applied to closed rendered geometry. */
2748
+ var StrokeAlignment;
2749
+ (function (StrokeAlignment) {
2750
+ StrokeAlignment["Inside"] = "inside";
2751
+ StrokeAlignment["Center"] = "center";
2752
+ StrokeAlignment["Outside"] = "outside";
2753
+ })(StrokeAlignment || (StrokeAlignment = {}));
2754
+
2655
2755
  var BoxShape;
2656
2756
  (function (BoxShape) {
2657
2757
  BoxShape["Rect"] = "rect";
2658
2758
  BoxShape["RoundedRect"] = "roundedRect";
2659
2759
  })(BoxShape || (BoxShape = {}));
2660
- var BoxStrokeAlignment;
2661
- (function (BoxStrokeAlignment) {
2662
- BoxStrokeAlignment["Inside"] = "inside";
2663
- BoxStrokeAlignment["Center"] = "center";
2664
- BoxStrokeAlignment["Outside"] = "outside";
2665
- })(BoxStrokeAlignment || (BoxStrokeAlignment = {}));
2666
2760
 
2667
2761
  const DEFAULT_BOX_STROKE_ALPHA = 1;
2668
2762
  const DEFAULT_BOX_STROKE_COLOR = 0x00ff66;
@@ -2716,22 +2810,12 @@ class BaseBoxStyle {
2716
2810
  if (stroke === null) {
2717
2811
  return undefined;
2718
2812
  }
2719
- const resolvedStroke = {
2720
- alpha: stroke?.alpha ?? DEFAULT_BOX_STROKE_ALPHA,
2721
- color: stroke?.color ?? DEFAULT_BOX_STROKE_COLOR,
2722
- width: stroke?.width ?? DEFAULT_BOX_STROKE_WIDTH,
2723
- };
2724
- return {
2725
- ...resolvedStroke,
2726
- ...(stroke?.alignment === undefined
2727
- ? {}
2728
- : { alignment: stroke.alignment }),
2729
- ...(stroke?.dash === undefined
2730
- ? context.ephemeral
2731
- ? { dash: [6, 4] }
2732
- : {}
2733
- : { dash: stroke.dash }),
2734
- };
2813
+ return resolveStrokeStyle(stroke, {
2814
+ alpha: DEFAULT_BOX_STROKE_ALPHA,
2815
+ color: DEFAULT_BOX_STROKE_COLOR,
2816
+ ...(context.ephemeral ? { dash: [6, 4] } : {}),
2817
+ width: DEFAULT_BOX_STROKE_WIDTH,
2818
+ });
2735
2819
  }
2736
2820
  resolveFill(detection, context) {
2737
2821
  const fill = resolveStyleValue(this.options.fill, detection, context);
@@ -2745,6 +2829,83 @@ class BaseBoxStyle {
2745
2829
  }
2746
2830
  }
2747
2831
 
2832
+ const DEFAULT_CORNER_LENGTH = 20;
2833
+ const DEFAULT_CORNER_STROKE = {
2834
+ alpha: 1,
2835
+ cap: "round",
2836
+ color: 0x00ff66,
2837
+ join: "round",
2838
+ width: 2,
2839
+ };
2840
+ /**
2841
+ * Configurable four-corner decoration derived from a detection rectangle.
2842
+ *
2843
+ * This is the BoxCornerAnnotator-style facade: it keeps the rectangle semantic
2844
+ * and draws only its four open corner segments.
2845
+ */
2846
+ class BaseBoxCornerStyle {
2847
+ options;
2848
+ constructor(options = {}) {
2849
+ this.options = options;
2850
+ }
2851
+ resolve(detection, context) {
2852
+ if (!detection.rect ||
2853
+ context.hidden ||
2854
+ this.options.shouldRender?.(detection, context) === false) {
2855
+ return undefined;
2856
+ }
2857
+ const resolvedLength = resolveStyleValue(this.options.length, detection, context) ??
2858
+ DEFAULT_CORNER_LENGTH;
2859
+ const viewportScale = context.viewportScale && context.viewportScale > 0
2860
+ ? context.viewportScale
2861
+ : 1;
2862
+ const length = Math.min(Math.max(0, resolvedLength / viewportScale), detection.rect.width / 2, detection.rect.height / 2);
2863
+ const stroke = resolveStyleValue(this.options.stroke, detection, context);
2864
+ if (length <= 0 || stroke === null)
2865
+ return undefined;
2866
+ const { height, width, x, y } = detection.rect;
2867
+ const left = x - width / 2;
2868
+ const right = x + width / 2;
2869
+ const top = y - height / 2;
2870
+ const bottom = y + height / 2;
2871
+ const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_CORNER_STROKE);
2872
+ const openStroke = {
2873
+ alpha: resolvedStroke.alpha,
2874
+ cap: resolvedStroke.cap,
2875
+ color: resolvedStroke.color,
2876
+ dash: resolvedStroke.dash,
2877
+ join: resolvedStroke.join,
2878
+ miterLimit: resolvedStroke.miterLimit,
2879
+ width: resolvedStroke.width,
2880
+ };
2881
+ return {
2882
+ segments: [
2883
+ [
2884
+ { x: left + length, y: top },
2885
+ { x: left, y: top },
2886
+ { x: left, y: top + length },
2887
+ ],
2888
+ [
2889
+ { x: right - length, y: top },
2890
+ { x: right, y: top },
2891
+ { x: right, y: top + length },
2892
+ ],
2893
+ [
2894
+ { x: left + length, y: bottom },
2895
+ { x: left, y: bottom },
2896
+ { x: left, y: bottom - length },
2897
+ ],
2898
+ [
2899
+ { x: right - length, y: bottom },
2900
+ { x: right, y: bottom },
2901
+ { x: right, y: bottom - length },
2902
+ ],
2903
+ ],
2904
+ stroke: openStroke,
2905
+ };
2906
+ }
2907
+ }
2908
+
2748
2909
  var FocusTargetMode;
2749
2910
  (function (FocusTargetMode) {
2750
2911
  FocusTargetMode["Hovered"] = "hovered";
@@ -2943,18 +3104,11 @@ class BaseInteractionStyle {
2943
3104
  return undefined;
2944
3105
  }
2945
3106
  const defaults = getStateDefaults(context.state);
2946
- const resolvedStroke = {
2947
- alpha: stroke?.alpha ?? defaults.strokeAlpha,
2948
- color: stroke?.color ?? defaults.strokeColor,
2949
- width: stroke?.width ?? defaults.strokeWidth,
2950
- };
2951
- if (stroke?.alignment !== undefined) {
2952
- return {
2953
- ...resolvedStroke,
2954
- alignment: stroke.alignment,
2955
- };
2956
- }
2957
- return resolvedStroke;
3107
+ return resolveStrokeStyle(stroke, {
3108
+ alpha: defaults.strokeAlpha,
3109
+ color: defaults.strokeColor,
3110
+ width: defaults.strokeWidth,
3111
+ });
2958
3112
  }
2959
3113
  resolveFill(detection, context) {
2960
3114
  const fill = resolveStyleValue(this.options.fill, detection, context);
@@ -3206,6 +3360,115 @@ function createArtifactKey(options) {
3206
3360
  return `base:${color}:${fillAlpha}:${mode}:${serializeStroke(resolveStroke(options.stroke, color, mode))}`;
3207
3361
  }
3208
3362
 
3363
+ /**
3364
+ * Discriminator for renderer-neutral shape draw instructions.
3365
+ */
3366
+ var ShapeInstructionKind;
3367
+ (function (ShapeInstructionKind) {
3368
+ ShapeInstructionKind["Ellipse"] = "ellipse";
3369
+ ShapeInstructionKind["Marker"] = "marker";
3370
+ ShapeInstructionKind["Path"] = "path";
3371
+ })(ShapeInstructionKind || (ShapeInstructionKind = {}));
3372
+ var MarkerShape;
3373
+ (function (MarkerShape) {
3374
+ MarkerShape["Circle"] = "circle";
3375
+ MarkerShape["Cross"] = "cross";
3376
+ MarkerShape["Square"] = "square";
3377
+ MarkerShape["Triangle"] = "triangle";
3378
+ })(MarkerShape || (MarkerShape = {}));
3379
+ /**
3380
+ * Coordinate space for marker sizing. Media sizes scale with the viewport;
3381
+ * screen sizes stay constant on screen, matching stroke-width semantics.
3382
+ */
3383
+ var MarkerSizeSpace;
3384
+ (function (MarkerSizeSpace) {
3385
+ MarkerSizeSpace["Media"] = "media";
3386
+ MarkerSizeSpace["Screen"] = "screen";
3387
+ })(MarkerSizeSpace || (MarkerSizeSpace = {}));
3388
+
3389
+ const DEFAULT_FILL = { alpha: 1, color: 0x00ff66 };
3390
+ const DEFAULT_STROKE = { alpha: 1, color: 0xffffff, width: 1 };
3391
+ const DEFAULT_SIZE = 12;
3392
+ /**
3393
+ * Configurable marker renderer style anchored to semantic detection geometry.
3394
+ *
3395
+ * Use `center` for any domain-specific anchor (keypoint, centroid, or custom
3396
+ * metadata). Without it, the marker lands at the detection rectangle center.
3397
+ */
3398
+ class BaseMarkerStyle {
3399
+ options;
3400
+ constructor(options = {}) {
3401
+ this.options = options;
3402
+ }
3403
+ resolve(detection, context) {
3404
+ if (context.hidden ||
3405
+ this.options.shouldRender?.(detection, context) === false) {
3406
+ return undefined;
3407
+ }
3408
+ const center = resolveStyleValue(this.options.center, detection, context) ??
3409
+ (detection.rect
3410
+ ? { x: detection.rect.x, y: detection.rect.y }
3411
+ : undefined);
3412
+ const shape = resolveStyleValue(this.options.shape, detection, context) ??
3413
+ MarkerShape.Circle;
3414
+ const size = resolveStyleValue(this.options.size, detection, context) ?? DEFAULT_SIZE;
3415
+ const sizeSpace = resolveStyleValue(this.options.sizeSpace, detection, context) ??
3416
+ MarkerSizeSpace.Screen;
3417
+ const rotation = resolveStyleValue(this.options.rotation, detection, context);
3418
+ const stroke = resolveStyleValue(this.options.stroke, detection, context);
3419
+ if (!center || !Number.isFinite(size) || size <= 0) {
3420
+ return undefined;
3421
+ }
3422
+ if (shape === MarkerShape.Cross) {
3423
+ if (stroke === null) {
3424
+ return undefined;
3425
+ }
3426
+ const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_STROKE);
3427
+ const openStroke = {
3428
+ alpha: resolvedStroke.alpha,
3429
+ cap: resolvedStroke.cap,
3430
+ color: resolvedStroke.color,
3431
+ dash: resolvedStroke.dash,
3432
+ join: resolvedStroke.join,
3433
+ miterLimit: resolvedStroke.miterLimit,
3434
+ width: resolvedStroke.width,
3435
+ };
3436
+ return {
3437
+ center,
3438
+ shape,
3439
+ size,
3440
+ sizeSpace,
3441
+ ...(rotation === undefined ? {} : { rotation }),
3442
+ stroke: openStroke,
3443
+ };
3444
+ }
3445
+ const fill = resolveStyleValue(this.options.fill, detection, context);
3446
+ if (fill === null && stroke === null) {
3447
+ return undefined;
3448
+ }
3449
+ return {
3450
+ center,
3451
+ shape,
3452
+ size,
3453
+ sizeSpace,
3454
+ ...(rotation === undefined ? {} : { rotation }),
3455
+ ...(fill === null
3456
+ ? {}
3457
+ : {
3458
+ fill: {
3459
+ alpha: fill?.alpha ?? DEFAULT_FILL.alpha,
3460
+ color: fill?.color ?? DEFAULT_FILL.color,
3461
+ },
3462
+ }),
3463
+ ...(stroke === null
3464
+ ? {}
3465
+ : stroke === undefined
3466
+ ? { stroke: DEFAULT_STROKE }
3467
+ : { stroke: resolveStrokeStyle(stroke, DEFAULT_STROKE) }),
3468
+ };
3469
+ }
3470
+ }
3471
+
3209
3472
  class BasePolygonStyle {
3210
3473
  options;
3211
3474
  constructor(options = {}) {
@@ -3233,15 +3496,11 @@ class BasePolygonStyle {
3233
3496
  ...(stroke === null
3234
3497
  ? {}
3235
3498
  : {
3236
- stroke: {
3237
- alpha: stroke?.alpha ?? 1,
3238
- color: stroke?.color ?? 0x00ff66,
3239
- width: stroke?.width ?? 2,
3240
- ...(stroke?.alignment === undefined
3241
- ? {}
3242
- : { alignment: stroke.alignment }),
3243
- ...(stroke?.dash === undefined ? {} : { dash: stroke.dash }),
3244
- },
3499
+ stroke: resolveStrokeStyle(stroke, {
3500
+ alpha: 1,
3501
+ color: 0x00ff66,
3502
+ width: 2,
3503
+ }),
3245
3504
  }),
3246
3505
  };
3247
3506
  }
@@ -3265,15 +3524,11 @@ class BasePolylineStyle {
3265
3524
  }
3266
3525
  return {
3267
3526
  points: detection.polyline.points,
3268
- stroke: {
3269
- alpha: stroke?.alpha ?? 1,
3270
- color: stroke?.color ?? 0x00ff66,
3271
- width: stroke?.width ?? 2,
3272
- ...(stroke?.alignment === undefined
3273
- ? {}
3274
- : { alignment: stroke.alignment }),
3275
- ...(stroke?.dash === undefined ? {} : { dash: stroke.dash }),
3276
- },
3527
+ stroke: resolveStrokeStyle(stroke, {
3528
+ alpha: 1,
3529
+ color: 0x00ff66,
3530
+ width: 2,
3531
+ }),
3277
3532
  };
3278
3533
  }
3279
3534
  }
@@ -3307,19 +3562,21 @@ class BaseKeypointStyle {
3307
3562
  const edges = geometry.edges.map(([fromIndex, toIndex], edgeIndex) => ({
3308
3563
  from: geometry.points[fromIndex],
3309
3564
  to: geometry.points[toIndex],
3310
- stroke: {
3311
- alpha: edgeStroke?.alpha ?? 1,
3312
- color: definition?.edges[edgeIndex]?.color ?? edgeStroke?.color ?? 0x00ff66,
3313
- width: edgeStroke?.width ?? 2,
3314
- },
3565
+ stroke: resolveStrokeStyle(definition?.edges[edgeIndex]?.color === undefined
3566
+ ? edgeStroke
3567
+ : { ...edgeStroke, color: definition.edges[edgeIndex].color }, {
3568
+ alpha: 1,
3569
+ color: 0x00ff66,
3570
+ width: 2,
3571
+ }),
3315
3572
  ...(shadowStroke === null
3316
3573
  ? {}
3317
3574
  : {
3318
- shadowStroke: {
3319
- alpha: shadowStroke?.alpha ?? 0.65,
3320
- color: shadowStroke?.color ?? 0x000000,
3321
- width: shadowStroke?.width ?? 4,
3322
- },
3575
+ shadowStroke: resolveStrokeStyle(shadowStroke, {
3576
+ alpha: 0.65,
3577
+ color: 0x000000,
3578
+ width: 4,
3579
+ }),
3323
3580
  }),
3324
3581
  }));
3325
3582
  const markers = geometry.points.flatMap((point, index) => {
@@ -3341,11 +3598,11 @@ class BaseKeypointStyle {
3341
3598
  shape: visibility === KeypointVisibility.Occluded
3342
3599
  ? KeypointMarkerShape.Cross
3343
3600
  : KeypointMarkerShape.Circle,
3344
- stroke: {
3345
- alpha: markerStroke?.alpha ?? 1,
3346
- color: markerStroke?.color ?? 0xffffff,
3347
- width: markerStroke?.width ?? 2,
3348
- },
3601
+ stroke: resolveStrokeStyle(markerStroke, {
3602
+ alpha: 1,
3603
+ color: 0xffffff,
3604
+ width: 2,
3605
+ }),
3349
3606
  },
3350
3607
  ];
3351
3608
  });
@@ -3426,6 +3683,9 @@ function createClassColorStyle(fill, labelBackground, labelText, stroke) {
3426
3683
  }
3427
3684
 
3428
3685
  const DEFAULT_BOX_CORNER_RADIUS = 1;
3686
+ const DEFAULT_ELLIPSE_AXIS_RATIO = 0.35;
3687
+ const DEFAULT_ELLIPSE_START_ANGLE = (-45 * Math.PI) / 180;
3688
+ const DEFAULT_ELLIPSE_END_ANGLE = (235 * Math.PI) / 180;
3429
3689
  const DEFAULT_OUTLINE_WIDTH = 2;
3430
3690
  const DEFAULT_FILL_ALPHA = 0.08;
3431
3691
  const DEFAULT_MASK_FILL_ALPHA = 0.45;
@@ -3444,95 +3704,193 @@ const DEFAULT_LABEL_FONT_FAMILY = "ui-monospace, SFMono-Regular, Menlo, Monaco,
3444
3704
  * editing, focus, and theme behaviour so consumers can compose those layers.
3445
3705
  */
3446
3706
  function createDefaultAnnotationPresentation(options = {}) {
3447
- const getClassColor = (detection) => resolveDefaultClassColor(detection, options.getClassColor);
3448
- const shouldRenderBox = (detection) => !(detection.mask ||
3449
- detection.polygon ||
3450
- detection.polyline ||
3451
- detection.keypoints);
3452
3707
  return {
3453
- boxStyle: new BaseBoxStyle({
3454
- cornerRadius: DEFAULT_BOX_CORNER_RADIUS,
3455
- fill: (detection) => ({
3456
- alpha: DEFAULT_FILL_ALPHA,
3457
- color: getClassColor(detection),
3458
- }),
3459
- shape: BoxShape.RoundedRect,
3460
- shouldRender: shouldRenderBox,
3461
- stroke: (detection) => ({
3462
- alpha: 1,
3463
- color: getClassColor(detection),
3464
- width: DEFAULT_OUTLINE_WIDTH,
3465
- }),
3708
+ boxStyle: createDefaultBoxStyle(options),
3709
+ keypointStyle: createDefaultKeypointStyle(options),
3710
+ labelStyle: createDefaultLabelStyle(options),
3711
+ maskStyle: createDefaultMaskStyle(options),
3712
+ polygonStyle: createDefaultPolygonStyle(options),
3713
+ polylineStyle: createDefaultPolylineStyle(options),
3714
+ };
3715
+ }
3716
+ /**
3717
+ * The canonical default style of one built-in layer.
3718
+ *
3719
+ * These exist so a renderer kind can build only the style it needs instead of
3720
+ * the whole default presentation. Keep them equivalent to the matching field of
3721
+ * {@link createDefaultAnnotationPresentation}.
3722
+ */
3723
+ function createDefaultBoxStyle(options = {}) {
3724
+ const getClassColor = createClassColorResolver(options);
3725
+ return new BaseBoxStyle({
3726
+ cornerRadius: DEFAULT_BOX_CORNER_RADIUS,
3727
+ fill: (detection) => ({
3728
+ alpha: DEFAULT_FILL_ALPHA,
3729
+ color: getClassColor(detection),
3466
3730
  }),
3467
- keypointStyle: new BaseKeypointStyle({
3468
- ...(options.skeletonDefinitions === undefined
3469
- ? {}
3470
- : { definitions: options.skeletonDefinitions }),
3471
- edgeShadowStroke: {
3472
- alpha: DEFAULT_KEYPOINT_SHADOW_ALPHA,
3473
- color: 0x000000,
3474
- width: DEFAULT_KEYPOINT_SHADOW_WIDTH,
3475
- },
3476
- edgeStroke: (detection) => ({
3477
- alpha: 1,
3478
- color: getClassColor(detection),
3479
- width: DEFAULT_KEYPOINT_EDGE_WIDTH,
3480
- }),
3481
- markerFill: (detection) => ({
3482
- alpha: 1,
3483
- color: getClassColor(detection),
3484
- }),
3485
- markerStroke: { alpha: 1, color: 0xffffff, width: 1 },
3486
- radius: DEFAULT_KEYPOINT_RADIUS,
3731
+ shape: BoxShape.RoundedRect,
3732
+ shouldRender: (detection) => !(detection.mask ||
3733
+ detection.polygon ||
3734
+ detection.polyline ||
3735
+ detection.keypoints),
3736
+ stroke: (detection) => ({
3737
+ alpha: 1,
3738
+ color: getClassColor(detection),
3739
+ width: DEFAULT_OUTLINE_WIDTH,
3487
3740
  }),
3488
- labelStyle: new BaseLabelStyle({
3489
- background: (detection) => ({
3490
- alpha: 1,
3491
- color: getClassColor(detection),
3492
- cornerRadius: DEFAULT_LABEL_CORNER_RADIUS,
3493
- paddingX: DEFAULT_LABEL_PADDING_X,
3494
- paddingY: DEFAULT_LABEL_PADDING_Y,
3495
- topCornersOnly: true,
3496
- }),
3497
- includeConfidence: options.includeConfidence,
3498
- placement: LabelPlacement.Top,
3499
- textStyle: {
3500
- fontFamily: DEFAULT_LABEL_FONT_FAMILY,
3501
- fontSize: DEFAULT_LABEL_FONT_SIZE,
3502
- fontWeight: "600",
3503
- },
3741
+ });
3742
+ }
3743
+ /** Canonical opt-in BoxCornerAnnotator-style presentation. */
3744
+ function createDefaultBoxCornerStyle(options = {}) {
3745
+ const getClassColor = createClassColorResolver(options);
3746
+ return new BaseBoxCornerStyle({
3747
+ stroke: (detection) => ({ color: getClassColor(detection) }),
3748
+ });
3749
+ }
3750
+ /**
3751
+ * Matches the Python Supervision EllipseAnnotator: an elliptical footprint
3752
+ * arc under the detection box, swept from -45deg to 235deg, in the class
3753
+ * color.
3754
+ */
3755
+ function createDefaultEllipseStyle(options = {}) {
3756
+ const getClassColor = createClassColorResolver(options);
3757
+ return {
3758
+ resolve(detection, context) {
3759
+ if (!detection.rect || context.hidden) {
3760
+ return undefined;
3761
+ }
3762
+ const radiusX = detection.rect.width / 2;
3763
+ const radiusY = radiusX * DEFAULT_ELLIPSE_AXIS_RATIO;
3764
+ return {
3765
+ center: {
3766
+ x: detection.rect.x,
3767
+ // Bottom-tangent so the arc hugs the detection instead of dipping
3768
+ // below its feet.
3769
+ y: detection.rect.y + detection.rect.height / 2 - radiusY,
3770
+ },
3771
+ endAngle: DEFAULT_ELLIPSE_END_ANGLE,
3772
+ radiusX,
3773
+ radiusY,
3774
+ startAngle: DEFAULT_ELLIPSE_START_ANGLE,
3775
+ stroke: {
3776
+ alpha: 1,
3777
+ color: getClassColor(detection),
3778
+ width: DEFAULT_OUTLINE_WIDTH,
3779
+ },
3780
+ };
3781
+ },
3782
+ };
3783
+ }
3784
+ function createDefaultKeypointStyle(options = {}) {
3785
+ const getClassColor = createClassColorResolver(options);
3786
+ return new BaseKeypointStyle({
3787
+ ...(options.skeletonDefinitions === undefined
3788
+ ? {}
3789
+ : { definitions: options.skeletonDefinitions }),
3790
+ edgeShadowStroke: {
3791
+ alpha: DEFAULT_KEYPOINT_SHADOW_ALPHA,
3792
+ color: 0x000000,
3793
+ width: DEFAULT_KEYPOINT_SHADOW_WIDTH,
3794
+ },
3795
+ edgeStroke: (detection) => ({
3796
+ alpha: 1,
3797
+ color: getClassColor(detection),
3798
+ width: DEFAULT_KEYPOINT_EDGE_WIDTH,
3504
3799
  }),
3505
- maskStyle: new BaseMaskStyle({
3506
- color: (detection) => getClassColor(detection),
3507
- fillAlpha: DEFAULT_MASK_FILL_ALPHA,
3508
- mode: MaskRenderMode.FillAndStroke,
3509
- opacity: 1,
3510
- stroke: (detection) => ({
3511
- alpha: 1,
3512
- color: getClassColor(detection),
3513
- width: DEFAULT_OUTLINE_WIDTH,
3514
- }),
3800
+ markerFill: (detection) => ({
3801
+ alpha: 1,
3802
+ color: getClassColor(detection),
3515
3803
  }),
3516
- polygonStyle: new BasePolygonStyle({
3517
- fill: (detection) => ({
3518
- alpha: DEFAULT_FILL_ALPHA,
3519
- color: getClassColor(detection),
3520
- }),
3521
- stroke: (detection) => ({
3522
- alpha: 1,
3523
- color: getClassColor(detection),
3524
- width: DEFAULT_OUTLINE_WIDTH,
3525
- }),
3804
+ markerStroke: { alpha: 1, color: 0xffffff, width: 1 },
3805
+ radius: DEFAULT_KEYPOINT_RADIUS,
3806
+ });
3807
+ }
3808
+ function createDefaultLabelStyle(options = {}) {
3809
+ const getClassColor = createClassColorResolver(options);
3810
+ return new BaseLabelStyle({
3811
+ background: (detection) => ({
3812
+ alpha: 1,
3813
+ color: getClassColor(detection),
3814
+ cornerRadius: DEFAULT_LABEL_CORNER_RADIUS,
3815
+ paddingX: DEFAULT_LABEL_PADDING_X,
3816
+ paddingY: DEFAULT_LABEL_PADDING_Y,
3817
+ topCornersOnly: true,
3526
3818
  }),
3527
- polylineStyle: new BasePolylineStyle({
3528
- stroke: (detection) => ({
3529
- alpha: 1,
3530
- color: getClassColor(detection),
3531
- width: DEFAULT_OUTLINE_WIDTH,
3532
- }),
3819
+ includeConfidence: options.includeConfidence,
3820
+ placement: LabelPlacement.Top,
3821
+ textStyle: {
3822
+ fontFamily: DEFAULT_LABEL_FONT_FAMILY,
3823
+ fontSize: DEFAULT_LABEL_FONT_SIZE,
3824
+ fontWeight: "600",
3825
+ },
3826
+ });
3827
+ }
3828
+ function createDefaultMaskStyle(options = {}) {
3829
+ const getClassColor = createClassColorResolver(options);
3830
+ return new BaseMaskStyle({
3831
+ color: (detection) => getClassColor(detection),
3832
+ fillAlpha: DEFAULT_MASK_FILL_ALPHA,
3833
+ mode: MaskRenderMode.FillAndStroke,
3834
+ opacity: 1,
3835
+ stroke: (detection) => ({
3836
+ alpha: 1,
3837
+ color: getClassColor(detection),
3838
+ width: DEFAULT_OUTLINE_WIDTH,
3533
3839
  }),
3840
+ });
3841
+ }
3842
+ /** Canonical opt-in marker presentation. */
3843
+ function createDefaultMarkerStyle() {
3844
+ return new BaseMarkerStyle();
3845
+ }
3846
+ const DEFAULT_MASK_HALO_ALPHA = 0.6;
3847
+ const DEFAULT_MASK_HALO_SPREAD = 12;
3848
+ /**
3849
+ * Canonical mask halo: a class-colored glow that follows the mask silhouette.
3850
+ * Used when the `maskHalo` renderer is listed without an explicit style.
3851
+ */
3852
+ function createDefaultMaskHaloStyle(options = {}) {
3853
+ const getClassColor = createClassColorResolver(options);
3854
+ return {
3855
+ resolve(detection, context) {
3856
+ if (!detection.mask || context.hidden) {
3857
+ return undefined;
3858
+ }
3859
+ return {
3860
+ alpha: DEFAULT_MASK_HALO_ALPHA,
3861
+ color: getClassColor(detection),
3862
+ spread: DEFAULT_MASK_HALO_SPREAD,
3863
+ };
3864
+ },
3534
3865
  };
3535
3866
  }
3867
+ function createDefaultPolygonStyle(options = {}) {
3868
+ const getClassColor = createClassColorResolver(options);
3869
+ return new BasePolygonStyle({
3870
+ fill: (detection) => ({
3871
+ alpha: DEFAULT_FILL_ALPHA,
3872
+ color: getClassColor(detection),
3873
+ }),
3874
+ stroke: (detection) => ({
3875
+ alpha: 1,
3876
+ color: getClassColor(detection),
3877
+ width: DEFAULT_OUTLINE_WIDTH,
3878
+ }),
3879
+ });
3880
+ }
3881
+ function createDefaultPolylineStyle(options = {}) {
3882
+ const getClassColor = createClassColorResolver(options);
3883
+ return new BasePolylineStyle({
3884
+ stroke: (detection) => ({
3885
+ alpha: 1,
3886
+ color: getClassColor(detection),
3887
+ width: DEFAULT_OUTLINE_WIDTH,
3888
+ }),
3889
+ });
3890
+ }
3891
+ function createClassColorResolver(options) {
3892
+ return (detection) => resolveDefaultClassColor(detection, options.getClassColor);
3893
+ }
3536
3894
  function resolveDefaultClassColor(detection, getClassColor) {
3537
3895
  const color = getClassColor?.(detection.className);
3538
3896
  return typeof color === "number" && Number.isFinite(color)
@@ -3540,33 +3898,246 @@ function resolveDefaultClassColor(detection, getClassColor) {
3540
3898
  : resolveDetectionClassColorStyle(detection.className).fill;
3541
3899
  }
3542
3900
 
3543
- function createSourceAwarePresentation(globalPresentation = {}, sources) {
3901
+ /**
3902
+ * The built-in annotation renderer vocabulary.
3903
+ *
3904
+ * These stay plain string literals so `{ kind: "box" }` remains assignable to
3905
+ * the public descriptor union from JavaScript and TypeScript alike.
3906
+ */
3907
+ const annotationRendererKinds = [
3908
+ "box",
3909
+ "box-corners",
3910
+ "ellipse",
3911
+ "keypoints",
3912
+ "label",
3913
+ "mask",
3914
+ "maskHalo",
3915
+ "marker",
3916
+ "polygon",
3917
+ "polyline",
3918
+ "region",
3919
+ ];
3920
+ /** Sources currently supported by a region renderer. */
3921
+ const RegionRendererSourceKind = {
3922
+ Asset: "asset",
3923
+ };
3924
+ /** Media-space regions currently supported by a region renderer. */
3925
+ const RegionRendererRegionKind = {
3926
+ Bounds: "bounds",
3927
+ KeypointAnchor: "keypoint-anchor",
3928
+ };
3929
+ /** Composition modes currently supported by a region renderer. */
3930
+ const RegionRendererComposeMode = {
3931
+ Over: "over",
3932
+ };
3933
+ /**
3934
+ * Creates built-in annotation renderer descriptors for
3935
+ * `MediaRendererPresentation.renderers`.
3936
+ *
3937
+ * The currently supported renderers retain their established scene ordering:
3938
+ * masks, polygons, vectors, labels. A later renderer kind may add a new
3939
+ * composition capability without changing that ordering for existing scenes.
3940
+ */
3941
+ const annotationRenderers = {
3942
+ box: (options) => createAnnotationRenderer("box", options),
3943
+ boxCorners: (options) => createAnnotationRenderer("box-corners", options),
3944
+ ellipse: (options) => createAnnotationRenderer("ellipse", options),
3945
+ keypoints: (options) => createAnnotationRenderer("keypoints", options),
3946
+ label: (options) => createAnnotationRenderer("label", options),
3947
+ mask: (options) => createAnnotationRenderer("mask", options),
3948
+ maskHalo: (options) => createAnnotationRenderer("maskHalo", options),
3949
+ marker: (options) => createAnnotationRenderer("marker", options),
3950
+ polygon: (options) => createAnnotationRenderer("polygon", options),
3951
+ polyline: (options) => createAnnotationRenderer("polyline", options),
3952
+ region: (options) => ({ kind: "region", ...options }),
3953
+ };
3954
+ function createAnnotationRenderer(kind, options) {
3955
+ // Every built-in renderer keeps its kind as its stable id. TypeScript cannot
3956
+ // narrow the descriptor union through the generic kind parameter, so the
3957
+ // descriptor shape is asserted here and covered by a focused test.
3958
+ return { id: kind, kind, ...options };
3959
+ }
3960
+
3961
+ const annotationRendererRegistry = {
3962
+ box: {
3963
+ cardinality: "singleton",
3964
+ createCanonicalStyle: createDefaultBoxStyle,
3965
+ styleField: "boxStyle",
3966
+ },
3967
+ "box-corners": {
3968
+ cardinality: "singleton",
3969
+ createCanonicalStyle: createDefaultBoxCornerStyle,
3970
+ styleField: "boxCornerStyle",
3971
+ },
3972
+ ellipse: {
3973
+ cardinality: "singleton",
3974
+ createCanonicalStyle: createDefaultEllipseStyle,
3975
+ styleField: "ellipseStyle",
3976
+ },
3977
+ keypoints: {
3978
+ cardinality: "singleton",
3979
+ createCanonicalStyle: createDefaultKeypointStyle,
3980
+ styleField: "keypointStyle",
3981
+ },
3982
+ label: {
3983
+ cardinality: "singleton",
3984
+ createCanonicalStyle: createDefaultLabelStyle,
3985
+ styleField: "labelStyle",
3986
+ },
3987
+ mask: {
3988
+ cardinality: "singleton",
3989
+ createCanonicalStyle: createDefaultMaskStyle,
3990
+ styleField: "maskStyle",
3991
+ },
3992
+ maskHalo: {
3993
+ cardinality: "singleton",
3994
+ createCanonicalStyle: () => createDefaultMaskHaloStyle(),
3995
+ styleField: "maskHaloStyle",
3996
+ },
3997
+ marker: {
3998
+ cardinality: "singleton",
3999
+ createCanonicalStyle: createDefaultMarkerStyle,
4000
+ styleField: "markerStyle",
4001
+ },
4002
+ polygon: {
4003
+ cardinality: "singleton",
4004
+ createCanonicalStyle: createDefaultPolygonStyle,
4005
+ styleField: "polygonStyle",
4006
+ },
4007
+ polyline: {
4008
+ cardinality: "singleton",
4009
+ createCanonicalStyle: createDefaultPolylineStyle,
4010
+ styleField: "polylineStyle",
4011
+ },
4012
+ region: { cardinality: "multiple" },
4013
+ };
4014
+ function isStyleBackedAnnotationRendererKind(kind) {
4015
+ return annotationRendererRegistry[kind].cardinality === "singleton";
4016
+ }
4017
+ /** Presentation fields owned by the given renderer kinds, in the same order. */
4018
+ function resolveAnnotationRendererStyleFields(kinds) {
4019
+ return kinds.flatMap((kind) => {
4020
+ const metadata = annotationRendererRegistry[kind];
4021
+ return "styleField" in metadata ? [metadata.styleField] : [];
4022
+ });
4023
+ }
4024
+
4025
+ /**
4026
+ * Resolves built-in renderer descriptors into the existing specialized style
4027
+ * fields. The browser backend deliberately keeps ownership of its box, mask,
4028
+ * label, polygon, polyline, and keypoint pipelines; this normalizer only
4029
+ * supplies those pipelines with their configured style.
4030
+ */
4031
+ function resolveAnnotationRendererPresentation(presentation) {
4032
+ const renderers = presentation.renderers;
4033
+ if (renderers === undefined) {
4034
+ return presentation;
4035
+ }
4036
+ const resolved = {
4037
+ boxStyle: null,
4038
+ boxCornerStyle: null,
4039
+ ellipseStyle: null,
4040
+ keypointStyle: null,
4041
+ labelStyle: null,
4042
+ maskHaloStyle: null,
4043
+ maskStyle: null,
4044
+ markerStyle: null,
4045
+ polygonStyle: null,
4046
+ polylineStyle: null,
4047
+ };
4048
+ const rendererIds = new Set();
4049
+ const rendererKinds = new Set();
4050
+ for (const renderer of renderers) {
4051
+ if (rendererIds.has(renderer.id)) {
4052
+ throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer id "${renderer.id}".`);
4053
+ }
4054
+ rendererIds.add(renderer.id);
4055
+ if (annotationRendererRegistry[renderer.kind].cardinality === "singleton" &&
4056
+ rendererKinds.has(renderer.kind)) {
4057
+ throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer kind "${renderer.kind}".`);
4058
+ }
4059
+ rendererKinds.add(renderer.kind);
4060
+ if (isStyleBackedAnnotationRenderer(renderer)) {
4061
+ applyRendererStyle(resolved, presentation, renderer);
4062
+ }
4063
+ }
4064
+ return {
4065
+ ...presentation,
4066
+ ...resolved,
4067
+ };
4068
+ }
4069
+ function isStyleBackedAnnotationRenderer(renderer) {
4070
+ return isStyleBackedAnnotationRendererKind(renderer.kind);
4071
+ }
4072
+ function applyRendererStyle(resolved, configured, renderer) {
4073
+ const { createCanonicalStyle, styleField } = annotationRendererRegistry[renderer.kind];
4074
+ const configuredStyle = configured[styleField];
4075
+ const style = renderer.style !== undefined
4076
+ ? renderer.style
4077
+ : configuredStyle !== undefined
4078
+ ? configuredStyle
4079
+ : createCanonicalStyle();
4080
+ // The registry pairs each kind with the presentation field holding the same
4081
+ // style contract, but TypeScript cannot correlate that pairing across a
4082
+ // lookup on a union, so the write is asserted once here.
4083
+ resolved[styleField] = style;
4084
+ }
4085
+
4086
+ function createSourceAwarePresentation(globalPresentation = {}, sources, options = {}) {
3544
4087
  if (!sources.some((source) => source.presentation !== undefined)) {
3545
4088
  return globalPresentation;
3546
4089
  }
3547
4090
  const sourcePresentations = new Map(sources.map((source) => [source.id, source.presentation]));
4091
+ const enabledStyleFields = options.enabledRendererKinds === undefined
4092
+ ? undefined
4093
+ : new Set(resolveAnnotationRendererStyleFields(options.enabledRendererKinds));
4094
+ const shouldApplySourceStyle = (style) => hasSourceStyle(sources, style) &&
4095
+ (enabledStyleFields === undefined || enabledStyleFields.has(style));
3548
4096
  return {
3549
4097
  ...globalPresentation,
3550
- boxStyle: hasSourceStyle(sources, "boxStyle")
4098
+ boxStyle: shouldApplySourceStyle("boxStyle")
3551
4099
  ? new SourceAwareBoxStyle(normalizeGlobalBoxStyle(globalPresentation.boxStyle), sourcePresentations)
3552
4100
  : globalPresentation.boxStyle,
3553
- labelStyle: hasSourceStyle(sources, "labelStyle")
4101
+ boxCornerStyle: shouldApplySourceStyle("boxCornerStyle")
4102
+ ? new SourceAwareBoxCornerStyle(globalPresentation.boxCornerStyle ?? null, sourcePresentations)
4103
+ : globalPresentation.boxCornerStyle,
4104
+ ellipseStyle: shouldApplySourceStyle("ellipseStyle")
4105
+ ? new SourceAwareEllipseStyle(globalPresentation.ellipseStyle ?? null, sourcePresentations)
4106
+ : globalPresentation.ellipseStyle,
4107
+ labelStyle: shouldApplySourceStyle("labelStyle")
3554
4108
  ? new SourceAwareLabelStyle(normalizeGlobalLabelStyle(globalPresentation.labelStyle), sourcePresentations)
3555
4109
  : globalPresentation.labelStyle,
3556
- maskStyle: hasSourceStyle(sources, "maskStyle")
4110
+ maskStyle: shouldApplySourceStyle("maskStyle")
3557
4111
  ? new SourceAwareMaskStyle(normalizeGlobalMaskStyle(globalPresentation.maskStyle), sourcePresentations)
3558
4112
  : globalPresentation.maskStyle,
3559
- polygonStyle: hasSourceStyle(sources, "polygonStyle")
4113
+ markerStyle: shouldApplySourceStyle("markerStyle")
4114
+ ? new SourceAwareMarkerStyle(normalizeGlobalMarkerStyle(globalPresentation.markerStyle), sourcePresentations)
4115
+ : globalPresentation.markerStyle,
4116
+ maskHaloStyle: shouldApplySourceStyle("maskHaloStyle")
4117
+ ? new SourceAwareMaskHaloStyle(normalizeGlobalMaskHaloStyle(globalPresentation.maskHaloStyle), sourcePresentations)
4118
+ : globalPresentation.maskHaloStyle,
4119
+ polygonStyle: shouldApplySourceStyle("polygonStyle")
3560
4120
  ? new SourceAwarePolygonStyle(normalizeGlobalPolygonStyle(globalPresentation.polygonStyle), sourcePresentations)
3561
4121
  : globalPresentation.polygonStyle,
3562
- polylineStyle: hasSourceStyle(sources, "polylineStyle")
4122
+ polylineStyle: shouldApplySourceStyle("polylineStyle")
3563
4123
  ? new SourceAwarePolylineStyle(normalizeGlobalPolylineStyle(globalPresentation.polylineStyle), sourcePresentations)
3564
4124
  : globalPresentation.polylineStyle,
3565
- keypointStyle: hasSourceStyle(sources, "keypointStyle")
4125
+ keypointStyle: shouldApplySourceStyle("keypointStyle")
3566
4126
  ? new SourceAwareKeypointStyle(normalizeGlobalKeypointStyle(globalPresentation.keypointStyle), sourcePresentations)
3567
4127
  : globalPresentation.keypointStyle,
3568
4128
  };
3569
4129
  }
4130
+ class SourceAwareBoxCornerStyle {
4131
+ globalStyle;
4132
+ sourcePresentations;
4133
+ constructor(globalStyle, sourcePresentations) {
4134
+ this.globalStyle = globalStyle;
4135
+ this.sourcePresentations = sourcePresentations;
4136
+ }
4137
+ resolve(detection, context) {
4138
+ return resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, "boxCornerStyle")?.resolve(detection, context);
4139
+ }
4140
+ }
3570
4141
  class SourceAwareBoxStyle {
3571
4142
  globalStyle;
3572
4143
  sourcePresentations;
@@ -3579,6 +4150,18 @@ class SourceAwareBoxStyle {
3579
4150
  return style?.resolve(detection, context);
3580
4151
  }
3581
4152
  }
4153
+ class SourceAwareEllipseStyle {
4154
+ globalStyle;
4155
+ sourcePresentations;
4156
+ constructor(globalStyle, sourcePresentations) {
4157
+ this.globalStyle = globalStyle;
4158
+ this.sourcePresentations = sourcePresentations;
4159
+ }
4160
+ resolve(detection, context) {
4161
+ const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, "ellipseStyle");
4162
+ return style?.resolve(detection, context);
4163
+ }
4164
+ }
3582
4165
  class SourceAwareLabelStyle {
3583
4166
  globalStyle;
3584
4167
  sourcePresentations;
@@ -3606,6 +4189,30 @@ class SourceAwareMaskStyle {
3606
4189
  return style?.resolve(detection, context);
3607
4190
  }
3608
4191
  }
4192
+ class SourceAwareMaskHaloStyle {
4193
+ globalStyle;
4194
+ sourcePresentations;
4195
+ constructor(globalStyle, sourcePresentations) {
4196
+ this.globalStyle = globalStyle;
4197
+ this.sourcePresentations = sourcePresentations;
4198
+ }
4199
+ resolve(detection, context) {
4200
+ const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, "maskHaloStyle");
4201
+ return style?.resolve(detection, context);
4202
+ }
4203
+ }
4204
+ class SourceAwareMarkerStyle {
4205
+ globalStyle;
4206
+ sourcePresentations;
4207
+ constructor(globalStyle, sourcePresentations) {
4208
+ this.globalStyle = globalStyle;
4209
+ this.sourcePresentations = sourcePresentations;
4210
+ }
4211
+ resolve(detection, context) {
4212
+ const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, "markerStyle");
4213
+ return style?.resolve(detection, context);
4214
+ }
4215
+ }
3609
4216
  class SourceAwarePolygonStyle {
3610
4217
  globalStyle;
3611
4218
  sourcePresentations;
@@ -3661,6 +4268,12 @@ function normalizeGlobalLabelStyle(style) {
3661
4268
  function normalizeGlobalMaskStyle(style) {
3662
4269
  return style === undefined ? new BaseMaskStyle() : style;
3663
4270
  }
4271
+ function normalizeGlobalMaskHaloStyle(style) {
4272
+ return style === undefined ? createDefaultMaskHaloStyle() : style;
4273
+ }
4274
+ function normalizeGlobalMarkerStyle(style) {
4275
+ return style === undefined ? new BaseMarkerStyle() : style;
4276
+ }
3664
4277
  function normalizeGlobalPolygonStyle(style) {
3665
4278
  return style === undefined ? new BasePolygonStyle() : style;
3666
4279
  }
@@ -4157,6 +4770,180 @@ function writePaletteEntry(palette, id, color, alpha) {
4157
4770
  palette[offset + 3] = Math.max(0, Math.min(alpha, 1));
4158
4771
  }
4159
4772
 
4773
+ const DEFAULT_ELLIPSE_SEGMENT_COUNT = 48;
4774
+ const FULL_TURN = Math.PI * 2;
4775
+ const GEOMETRY_EPSILON = 1e-9;
4776
+ const MIN_ELLIPSE_SEGMENT_COUNT = 4;
4777
+ const MAX_ELLIPSE_SEGMENT_COUNT = 512;
4778
+ /**
4779
+ * Samples an ellipse or elliptical arc into a deterministic polyline so every
4780
+ * backend rasterizes the same structural geometry, including dashed strokes.
4781
+ *
4782
+ * A closed ellipse returns `segmentCount` points without repeating the first
4783
+ * point; an open arc returns `segmentCount + 1` points including both
4784
+ * endpoints.
4785
+ */
4786
+ function sampleEllipseArc(ellipse, segmentCount = DEFAULT_ELLIPSE_SEGMENT_COUNT) {
4787
+ validateEllipseGeometry(ellipse);
4788
+ assertIntegerAtLeast(segmentCount, MIN_ELLIPSE_SEGMENT_COUNT, "segmentCount");
4789
+ const rotation = ellipse.rotation ?? 0;
4790
+ const startAngle = ellipse.startAngle ?? 0;
4791
+ const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;
4792
+ const range = endAngle - startAngle;
4793
+ const closed = ellipse.startAngle === undefined;
4794
+ const pointCount = closed ? segmentCount : segmentCount + 1;
4795
+ const cosRotation = Math.cos(rotation);
4796
+ const sinRotation = Math.sin(rotation);
4797
+ const points = [];
4798
+ for (let index = 0; index < pointCount; index += 1) {
4799
+ const angle = startAngle + (range * index) / segmentCount;
4800
+ const localX = Math.cos(angle) * ellipse.radiusX;
4801
+ const localY = Math.sin(angle) * ellipse.radiusY;
4802
+ points.push({
4803
+ x: ellipse.center.x + localX * cosRotation - localY * sinRotation,
4804
+ y: ellipse.center.y + localX * sinRotation + localY * cosRotation,
4805
+ });
4806
+ }
4807
+ return { closed, points };
4808
+ }
4809
+ /**
4810
+ * Chooses a bounded segment count from the ellipse's on-screen radius. The
4811
+ * result targets sub-pixel chord error without making tiny markers expensive.
4812
+ */
4813
+ function resolveEllipseSegmentCount(ellipse, viewportScale = 1, maxErrorPixels = 0.5) {
4814
+ validateEllipseGeometry(ellipse);
4815
+ assertPositiveFinite(viewportScale, "viewportScale");
4816
+ assertPositiveFinite(maxErrorPixels, "maxErrorPixels");
4817
+ const screenRadius = Math.max(ellipse.radiusX, ellipse.radiusY) * viewportScale;
4818
+ assertPositiveFinite(screenRadius, "screenRadius");
4819
+ const clampedError = Math.min(maxErrorPixels, screenRadius);
4820
+ const maxStep = clampedError === screenRadius
4821
+ ? Math.PI / 2
4822
+ : 2 * Math.acos(1 - clampedError / screenRadius);
4823
+ const startAngle = ellipse.startAngle ?? 0;
4824
+ const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;
4825
+ const arcLength = Math.abs(endAngle - startAngle);
4826
+ const segmentCount = Math.ceil(arcLength / maxStep);
4827
+ return Math.min(MAX_ELLIPSE_SEGMENT_COUNT, Math.max(MIN_ELLIPSE_SEGMENT_COUNT, segmentCount));
4828
+ }
4829
+ /**
4830
+ * Resolves a marker instruction into media-space geometry. Screen-space sizes
4831
+ * are divided by the viewport scale, matching stroke-width semantics.
4832
+ */
4833
+ function resolveMarkerGeometry(marker, viewportScale = 1) {
4834
+ assertFinitePoint(marker.center, "center");
4835
+ assertPositiveFinite(marker.size, "size");
4836
+ assertPositiveFinite(viewportScale, "viewportScale");
4837
+ if (marker.rotation !== undefined) {
4838
+ assertFinite(marker.rotation, "rotation");
4839
+ }
4840
+ const size = marker.sizeSpace === MarkerSizeSpace.Screen
4841
+ ? marker.size / viewportScale
4842
+ : marker.size;
4843
+ const radius = size / 2;
4844
+ const rotation = marker.rotation ?? 0;
4845
+ if (marker.shape === MarkerShape.Circle) {
4846
+ return { center: marker.center, kind: "circle", radius };
4847
+ }
4848
+ if (marker.shape === MarkerShape.Cross) {
4849
+ return {
4850
+ closed: false,
4851
+ kind: "subpaths",
4852
+ subpaths: [
4853
+ [
4854
+ rotatePoint(marker.center, -radius, -radius, rotation),
4855
+ rotatePoint(marker.center, radius, radius, rotation),
4856
+ ],
4857
+ [
4858
+ rotatePoint(marker.center, radius, -radius, rotation),
4859
+ rotatePoint(marker.center, -radius, radius, rotation),
4860
+ ],
4861
+ ],
4862
+ };
4863
+ }
4864
+ if (marker.shape === MarkerShape.Square) {
4865
+ return {
4866
+ closed: true,
4867
+ kind: "subpaths",
4868
+ subpaths: [
4869
+ [
4870
+ rotatePoint(marker.center, -radius, -radius, rotation),
4871
+ rotatePoint(marker.center, radius, -radius, rotation),
4872
+ rotatePoint(marker.center, radius, radius, rotation),
4873
+ rotatePoint(marker.center, -radius, radius, rotation),
4874
+ ],
4875
+ ],
4876
+ };
4877
+ }
4878
+ // Triangle: equilateral, inscribed in the marker circle, pointing toward
4879
+ // positive y at rotation 0.
4880
+ const baseOffsetY = -radius / 2;
4881
+ const baseOffsetX = radius * (Math.sqrt(3) / 2);
4882
+ return {
4883
+ closed: true,
4884
+ kind: "subpaths",
4885
+ subpaths: [
4886
+ [
4887
+ rotatePoint(marker.center, 0, radius, rotation),
4888
+ rotatePoint(marker.center, baseOffsetX, baseOffsetY, rotation),
4889
+ rotatePoint(marker.center, -baseOffsetX, baseOffsetY, rotation),
4890
+ ],
4891
+ ],
4892
+ };
4893
+ }
4894
+ function validateEllipseGeometry(ellipse) {
4895
+ assertFinitePoint(ellipse.center, "center");
4896
+ assertPositiveFinite(ellipse.radiusX, "radiusX");
4897
+ assertPositiveFinite(ellipse.radiusY, "radiusY");
4898
+ if (ellipse.rotation !== undefined) {
4899
+ assertFinite(ellipse.rotation, "rotation");
4900
+ }
4901
+ const hasStart = ellipse.startAngle !== undefined;
4902
+ const hasEnd = ellipse.endAngle !== undefined;
4903
+ if (hasStart !== hasEnd) {
4904
+ throw new RangeError("startAngle and endAngle must be provided together");
4905
+ }
4906
+ if (hasStart && hasEnd) {
4907
+ assertFinite(ellipse.startAngle, "startAngle");
4908
+ assertFinite(ellipse.endAngle, "endAngle");
4909
+ const range = Math.abs(ellipse.endAngle - ellipse.startAngle);
4910
+ if (range <= GEOMETRY_EPSILON || range >= FULL_TURN - GEOMETRY_EPSILON) {
4911
+ throw new RangeError("ellipse arc range must be greater than 0 and less than one full turn");
4912
+ }
4913
+ }
4914
+ }
4915
+ function assertFinitePoint(point, name) {
4916
+ assertFinite(point.x, `${name}.x`);
4917
+ assertFinite(point.y, `${name}.y`);
4918
+ }
4919
+ function assertFinite(value, name) {
4920
+ if (!Number.isFinite(value)) {
4921
+ throw new RangeError(`${name} must be finite`);
4922
+ }
4923
+ }
4924
+ function assertPositiveFinite(value, name) {
4925
+ assertFinite(value, name);
4926
+ if (value <= 0) {
4927
+ throw new RangeError(`${name} must be greater than 0`);
4928
+ }
4929
+ }
4930
+ function assertIntegerAtLeast(value, minimum, name) {
4931
+ if (!Number.isInteger(value) || value < minimum) {
4932
+ throw new RangeError(`${name} must be an integer of at least ${minimum}`);
4933
+ }
4934
+ }
4935
+ function rotatePoint(center, offsetX, offsetY, rotation) {
4936
+ if (rotation === 0) {
4937
+ return { x: center.x + offsetX, y: center.y + offsetY };
4938
+ }
4939
+ const cosRotation = Math.cos(rotation);
4940
+ const sinRotation = Math.sin(rotation);
4941
+ return {
4942
+ x: center.x + offsetX * cosRotation - offsetY * sinRotation,
4943
+ y: center.y + offsetX * sinRotation + offsetY * cosRotation,
4944
+ };
4945
+ }
4946
+
4160
4947
  function includeDefined(values) {
4161
4948
  return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
4162
4949
  }
@@ -4273,5 +5060,5 @@ var MediaSessionActivityStatus;
4273
5060
  MediaSessionActivityStatus["Waiting"] = "waiting";
4274
5061
  })(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));
4275
5062
 
4276
- export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, 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, MaskRenderMode, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, SUPERVISION_ROBOFLOW_COLOR, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveMaskStyleOpacity, resolveStyleValue, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames };
5063
+ export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, 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, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, RegionRendererComposeMode, RegionRendererRegionKind, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames };
4277
5064
  //# sourceMappingURL=index.js.map