supervision 0.1.6 → 0.1.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.
- package/dist/index.js +205 -83
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts +3 -2
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-vector-layer.d.ts +2 -1
- package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
- package/dist/tracking.worker.js.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +124 -28
- package/node_modules/supervision-js-core/dist/index.js.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-editing-engine.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/detections.d.ts +7 -0
- package/node_modules/supervision-js-core/dist/types/detections.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/editing.d.ts +6 -0
- package/node_modules/supervision-js-core/dist/types/editing.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/interaction-style.d.ts +2 -0
- package/node_modules/supervision-js-core/dist/types/interaction-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/interaction.d.ts +9 -1
- package/node_modules/supervision-js-core/dist/types/interaction.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copySortedDetectionFrames, detectionFrameOverlapsRange, projectDetectionFrameForTracking, createSortTracker, createOCSortTracker, createCBIoUTracker, createByteTrackTracker, MediaErrorKind, includeDefined, MediaSourceStatus, MediaRendererPlaybackState, MediaRendererFit, createDefaultAnnotationPresentation, createProjectedDetectionFrameSource, createArrayDetectionFrameSource, createBufferedDetectionTimeline, resolveAnnotationRendererPresentation, createIdleDetectionBufferState, MediaInteractionMode, StrokeAlignment, BaseBoxStyle, centerRectToTopLeftRect, BoxShape, BaseFocusStyle, decodeCompressedRleMask, extractMaskRectRuns, AnnotationGestureStateKind, AnnotationHandleKind, getDetectionRect, DetectionPickTarget, createDetectionPickKey, rebaseDetectionPickToFrame, pickDetectionAtPoint, followDetectionPickAcrossFrames, haveSameDetectionPickIdentities, pickAnnotationHandle, getAnnotationHandles, createIdMaskFrame, encodeBinaryMask, rasterizePolygonToMask, LabelPlacement, BasePolygonStyle, BasePolylineStyle, BaseKeypointStyle,
|
|
1
|
+
import { copySortedDetectionFrames, detectionFrameOverlapsRange, projectDetectionFrameForTracking, createSortTracker, createOCSortTracker, createCBIoUTracker, createByteTrackTracker, MediaErrorKind, includeDefined, MediaSourceStatus, MediaRendererPlaybackState, MediaRendererFit, createDefaultAnnotationPresentation, createProjectedDetectionFrameSource, createArrayDetectionFrameSource, createBufferedDetectionTimeline, resolveAnnotationRendererPresentation, createIdleDetectionBufferState, MediaInteractionMode, StrokeAlignment, BaseBoxStyle, centerRectToTopLeftRect, BoxShape, BaseFocusStyle, decodeCompressedRleMask, extractMaskRectRuns, AnnotationGestureStateKind, AnnotationHandleKind, getDetectionRect, DetectionPickTarget, createDetectionPickKey, rebaseDetectionPickToFrame, pickDetectionAtPoint, followDetectionPickAcrossFrames, haveSameDetectionPickIdentities, pickAnnotationHandle, getAnnotationHandles, createIdMaskFrame, encodeBinaryMask, rasterizePolygonToMask, LabelPlacement, BasePolygonStyle, BasePolylineStyle, BaseKeypointStyle, ShapeInstructionKind, sampleEllipseArc, resolveEllipseSegmentCount, KeypointMarkerShape, MarkerSizeSpace, MarkerShape, resolveMarkerGeometry, BaseInteractionStyle, DetectionInteractionState, MAX_ID_MASK_PALETTE_ENTRIES, DetectionBufferStatus, canReuseMaskStyleArtifacts, getBufferedDetectionTimelineFrameSnapshot, MAX_ID_MASK_STROKE_WIDTH, resolveMaskStyleOpacity, pickDetectionByMaskId, BoxStrokeAlignment, resolveStyleValue, resolveAnnotationStyleState, lightenColor, createViewportController, MediaSessionMode, DetectionFrameRetentionMode, createMemoryColdDetectionFrameStore, createWritableDetectionFrameSource, createCompositeDetectionFrameSource, MediaSessionActivityStatus, MediaSessionActivityKind, MediaSessionStatus, createSourceAwarePresentation, projectDetectionFrames, DetectionFrameSelectionMode } from 'supervision-js-core';
|
|
2
2
|
export { BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, RegionRendererComposeMode, RegionRendererRegionKind, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, TrackingGeometry, annotationRendererKinds, annotationRenderers, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createWritableDetectionFrameSource, detectionPostProcessors, normalizeDetectionClassName, pickDetectionAtPoint, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, resolveDetectionClassColorStyle } from 'supervision-js-core';
|
|
3
3
|
|
|
4
4
|
const DEFAULT_DATABASE_NAME = "supervision-js-detection-frames";
|
|
@@ -4445,6 +4445,7 @@ function createPixiInteractionLayer(options) {
|
|
|
4445
4445
|
includeMasks: options.pickMaskDetectionAtPoint === undefined,
|
|
4446
4446
|
maskMediaDimensions: options.getMediaDimensions?.(),
|
|
4447
4447
|
padding: pickPadding,
|
|
4448
|
+
viewportScale: options.getViewportScale?.() ?? 1,
|
|
4448
4449
|
});
|
|
4449
4450
|
if (geometryPick && POINT_PRECISE_PICK_TARGETS.has(geometryPick.target)) {
|
|
4450
4451
|
return geometryPick;
|
|
@@ -4526,19 +4527,17 @@ function createPixiInteractionLayer(options) {
|
|
|
4526
4527
|
const nextKey = createDetectionPickKey(nextPick);
|
|
4527
4528
|
if (nextKey === hoveredPickKey) {
|
|
4528
4529
|
hoveredPick = nextPick;
|
|
4529
|
-
if (container) {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4530
|
+
if (container && !selectedHandleAt(pointerPoint)) {
|
|
4531
|
+
container.cursor = nextPick
|
|
4532
|
+
? hoverCursor(nextPick)
|
|
4533
|
+
: resolveIdleCursor();
|
|
4533
4534
|
}
|
|
4534
4535
|
return;
|
|
4535
4536
|
}
|
|
4536
4537
|
hoveredPick = nextPick;
|
|
4537
4538
|
hoveredPickKey = nextKey;
|
|
4538
|
-
if (container) {
|
|
4539
|
-
|
|
4540
|
-
container.cursor = nextPick ? "pointer" : resolveIdleCursor();
|
|
4541
|
-
}
|
|
4539
|
+
if (container && !selectedHandleAt(pointerPoint)) {
|
|
4540
|
+
container.cursor = nextPick ? hoverCursor(nextPick) : resolveIdleCursor();
|
|
4542
4541
|
}
|
|
4543
4542
|
options.interaction.onHover?.(nextPick);
|
|
4544
4543
|
notifyStateChange();
|
|
@@ -4609,12 +4608,28 @@ function createPixiInteractionLayer(options) {
|
|
|
4609
4608
|
return;
|
|
4610
4609
|
const editingState = options.editingEngine?.getState();
|
|
4611
4610
|
if (editingState?.kind === AnnotationGestureStateKind.Moving) {
|
|
4612
|
-
container.cursor = "
|
|
4611
|
+
container.cursor = "move";
|
|
4613
4612
|
return;
|
|
4614
4613
|
}
|
|
4614
|
+
if (editingState?.kind === AnnotationGestureStateKind.Resizing &&
|
|
4615
|
+
selectedPick) {
|
|
4616
|
+
// Keep the grabbed handle's cursor for the whole drag.
|
|
4617
|
+
const active = getAnnotationHandles(selectedPick.detection, options.getViewportScale?.() ?? 1).find((handle) => handle.id === editingState.activeHandleId);
|
|
4618
|
+
if (active) {
|
|
4619
|
+
container.cursor = active.cursor;
|
|
4620
|
+
return;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4615
4623
|
const handle = selectedHandleAt(point);
|
|
4616
4624
|
container.cursor =
|
|
4617
|
-
handle?.cursor ??
|
|
4625
|
+
handle?.cursor ??
|
|
4626
|
+
(hoveredPick ? hoverCursor(hoveredPick) : resolveIdleCursor());
|
|
4627
|
+
}
|
|
4628
|
+
/** Editable geometry drags, so it advertises `move`; a keypoint is a point target. */
|
|
4629
|
+
function hoverCursor(pick) {
|
|
4630
|
+
if (!options.editingEngine || pick.detection.locked || pick.detection.mask)
|
|
4631
|
+
return "pointer";
|
|
4632
|
+
return pick.target === DetectionPickTarget.Keypoint ? "pointer" : "move";
|
|
4618
4633
|
}
|
|
4619
4634
|
function resolveIdleCursor() {
|
|
4620
4635
|
return options.editingEngine?.hasCreationTool() ? "crosshair" : "default";
|
|
@@ -5374,35 +5389,37 @@ function createPixiVectorLayer(options) {
|
|
|
5374
5389
|
}
|
|
5375
5390
|
if (polyline)
|
|
5376
5391
|
drawPixiPath(graphics, polyline.points, false, polyline.stroke, viewportScale);
|
|
5377
|
-
if (
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5392
|
+
if (keypoints)
|
|
5393
|
+
drawPixiKeypointInstruction(graphics, keypoints, viewportScale);
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
function drawPixiKeypointInstruction(graphics, instruction, viewportScale) {
|
|
5397
|
+
for (const edge of instruction.edges) {
|
|
5398
|
+
if (edge.shadowStroke)
|
|
5399
|
+
drawPixiPath(graphics, [edge.from, edge.to], false, edge.shadowStroke, viewportScale);
|
|
5400
|
+
drawPixiPath(graphics, [edge.from, edge.to], false, edge.stroke, viewportScale);
|
|
5401
|
+
}
|
|
5402
|
+
for (const marker of instruction.markers) {
|
|
5403
|
+
if (marker.shape === KeypointMarkerShape.Cross) {
|
|
5404
|
+
drawMarkerInstruction(graphics, {
|
|
5405
|
+
center: marker.point,
|
|
5406
|
+
kind: ShapeInstructionKind.Marker,
|
|
5407
|
+
shape: MarkerShape.Cross,
|
|
5408
|
+
size: marker.radius * 2,
|
|
5409
|
+
sizeSpace: MarkerSizeSpace.Screen,
|
|
5410
|
+
stroke: asOpenStroke(marker.stroke ?? { alpha: 1, color: 0xffffff, width: 2 }),
|
|
5411
|
+
}, viewportScale);
|
|
5412
|
+
}
|
|
5413
|
+
else {
|
|
5414
|
+
drawMarkerInstruction(graphics, {
|
|
5415
|
+
center: marker.point,
|
|
5416
|
+
fill: marker.fill,
|
|
5417
|
+
kind: ShapeInstructionKind.Marker,
|
|
5418
|
+
shape: MarkerShape.Circle,
|
|
5419
|
+
size: marker.radius * 2,
|
|
5420
|
+
sizeSpace: MarkerSizeSpace.Screen,
|
|
5421
|
+
stroke: marker.stroke,
|
|
5422
|
+
}, viewportScale);
|
|
5406
5423
|
}
|
|
5407
5424
|
}
|
|
5408
5425
|
}
|
|
@@ -5562,7 +5579,9 @@ function createPixiInteractionPresentationLayer(options) {
|
|
|
5562
5579
|
return;
|
|
5563
5580
|
}
|
|
5564
5581
|
syntheticFrame = {
|
|
5565
|
-
detections: activePicks.map(({ detection }) => detection
|
|
5582
|
+
detections: activePicks.map(({ detection }) => detection.id === undefined
|
|
5583
|
+
? detection
|
|
5584
|
+
: { ...detection, id: undefined }),
|
|
5566
5585
|
frameIndex: context.frame.frameIndex,
|
|
5567
5586
|
mediaTime: context.frame.mediaTime,
|
|
5568
5587
|
};
|
|
@@ -5598,6 +5617,13 @@ function createPixiInteractionPresentationLayer(options) {
|
|
|
5598
5617
|
for (const pick of picks) {
|
|
5599
5618
|
const key = createDetectionPickKey(pick.pick);
|
|
5600
5619
|
if (!key || seenKeys.has(key)) {
|
|
5620
|
+
if (key &&
|
|
5621
|
+
pick.context.state === DetectionInteractionState.Hovered &&
|
|
5622
|
+
pick.pick.target === DetectionPickTarget.Keypoint) {
|
|
5623
|
+
const duplicateIndex = deduped.findIndex((candidate) => createDetectionPickKey(candidate.pick) === key);
|
|
5624
|
+
if (duplicateIndex !== -1)
|
|
5625
|
+
deduped[duplicateIndex] = pick;
|
|
5626
|
+
}
|
|
5601
5627
|
continue;
|
|
5602
5628
|
}
|
|
5603
5629
|
seenKeys.add(key);
|
|
@@ -5614,6 +5640,7 @@ function createPixiInteractionPresentationLayer(options) {
|
|
|
5614
5640
|
const context = {
|
|
5615
5641
|
detectionIndex: activePick.detectionIndex,
|
|
5616
5642
|
frame: activePick.frame,
|
|
5643
|
+
geometryIndex: activePick.geometryIndex,
|
|
5617
5644
|
mediaTime,
|
|
5618
5645
|
point: activePick.point,
|
|
5619
5646
|
state,
|
|
@@ -8859,9 +8886,12 @@ function lowerEllipseInstruction(instruction) {
|
|
|
8859
8886
|
return { ...instruction, kind: ShapeInstructionKind.Ellipse };
|
|
8860
8887
|
}
|
|
8861
8888
|
|
|
8862
|
-
function createPixiAnnotationOverlayLayer(editingEngine, initialStyle) {
|
|
8889
|
+
function createPixiAnnotationOverlayLayer(editingEngine, initialStyle, initialKeypointStyle) {
|
|
8863
8890
|
let graphics;
|
|
8864
8891
|
let style = resolveAnnotationOverlayStyle(initialStyle);
|
|
8892
|
+
let keypointStyle = initialKeypointStyle === undefined
|
|
8893
|
+
? new BaseKeypointStyle()
|
|
8894
|
+
: initialKeypointStyle;
|
|
8865
8895
|
return {
|
|
8866
8896
|
attachGraphics(next) {
|
|
8867
8897
|
graphics = next;
|
|
@@ -8870,20 +8900,24 @@ function createPixiAnnotationOverlayLayer(editingEngine, initialStyle) {
|
|
|
8870
8900
|
if (next !== undefined)
|
|
8871
8901
|
style = resolveAnnotationOverlayStyle(next);
|
|
8872
8902
|
},
|
|
8903
|
+
setKeypointStyle(next) {
|
|
8904
|
+
if (next !== undefined)
|
|
8905
|
+
keypointStyle = next;
|
|
8906
|
+
},
|
|
8873
8907
|
draw(context) {
|
|
8874
8908
|
if (!graphics)
|
|
8875
8909
|
return;
|
|
8876
8910
|
graphics.clear();
|
|
8877
8911
|
drawExternalPreview(graphics, context.previewOverlay, context.viewportScale, style);
|
|
8878
|
-
drawEditingPreview(graphics, editingEngine, context.viewportScale, style);
|
|
8879
|
-
drawSelectionHandles(graphics, context, style);
|
|
8912
|
+
drawEditingPreview(graphics, editingEngine, context.frame, context.viewportScale, style, keypointStyle);
|
|
8913
|
+
drawSelectionHandles(graphics, context, style, editingEngine);
|
|
8880
8914
|
drawMarquee(graphics, context.marquee, style);
|
|
8881
8915
|
drawGuides(graphics, editingEngine, context, style);
|
|
8882
8916
|
drawLoading(graphics, context, style);
|
|
8883
8917
|
},
|
|
8884
8918
|
};
|
|
8885
8919
|
}
|
|
8886
|
-
function drawEditingPreview(graphics, engine, viewportScale, style) {
|
|
8920
|
+
function drawEditingPreview(graphics, engine, frame, viewportScale, style, keypointStyle) {
|
|
8887
8921
|
const state = engine?.getState();
|
|
8888
8922
|
const detection = state?.preview;
|
|
8889
8923
|
if (!detection)
|
|
@@ -8896,16 +8930,18 @@ function drawEditingPreview(graphics, engine, viewportScale, style) {
|
|
|
8896
8930
|
const boxFill = resolveStyleValue(style.editingPreview.boxFill, detection, styleContext);
|
|
8897
8931
|
const polygonFill = resolveStyleValue(style.editingPreview.polygonFill, detection, styleContext);
|
|
8898
8932
|
const closeZoneStroke = resolveStyleValue(style.editingPreview.closeZoneStroke, detection, styleContext);
|
|
8933
|
+
// A skeleton's rect is part of its geometry, so it previews alongside the
|
|
8934
|
+
// keypoints; polygon, polyline and mask rects are derived bounds and are not.
|
|
8899
8935
|
if (detection.rect &&
|
|
8900
8936
|
!detection.mask &&
|
|
8901
8937
|
!detection.polygon &&
|
|
8902
8938
|
!detection.polyline) {
|
|
8903
8939
|
const { x, y, width, height } = detection.rect;
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8940
|
+
// The source detection is hidden while it is edited, so the preview
|
|
8941
|
+
// carries the box fill as well as its outline.
|
|
8942
|
+
graphics
|
|
8943
|
+
.roundRect(x - width / 2, y - height / 2, width, height, resolveScreenLength(1, viewportScale))
|
|
8944
|
+
.fill(boxFill);
|
|
8909
8945
|
drawPixiPath(graphics, [
|
|
8910
8946
|
{ x: x - width / 2, y: y - height / 2 },
|
|
8911
8947
|
{ x: x + width / 2, y: y - height / 2 },
|
|
@@ -8925,6 +8961,22 @@ function drawEditingPreview(graphics, engine, viewportScale, style) {
|
|
|
8925
8961
|
}
|
|
8926
8962
|
if (detection.polyline)
|
|
8927
8963
|
drawPixiPath(graphics, detection.polyline.points, false, stroke, viewportScale);
|
|
8964
|
+
if (detection.keypoints && keypointStyle) {
|
|
8965
|
+
const previewFrame = {
|
|
8966
|
+
detections: [detection],
|
|
8967
|
+
frameIndex: frame?.frameIndex,
|
|
8968
|
+
mediaTime: frame?.mediaTime ?? 0,
|
|
8969
|
+
};
|
|
8970
|
+
const instruction = keypointStyle.resolve(detection, {
|
|
8971
|
+
detectionIndex: 0,
|
|
8972
|
+
frame: previewFrame,
|
|
8973
|
+
mediaTime: previewFrame.mediaTime,
|
|
8974
|
+
selected: true,
|
|
8975
|
+
viewportScale,
|
|
8976
|
+
});
|
|
8977
|
+
if (instruction)
|
|
8978
|
+
drawPixiKeypointInstruction(graphics, instruction, viewportScale);
|
|
8979
|
+
}
|
|
8928
8980
|
}
|
|
8929
8981
|
const CREATION_VERTEX_RADIUS = 6;
|
|
8930
8982
|
const CREATION_VERTEX_STROKE_WIDTH = 1.5;
|
|
@@ -9013,7 +9065,7 @@ function resolveCloseHighlightColor(creationColor, preferredHighlight) {
|
|
|
9013
9065
|
? preferredHighlight
|
|
9014
9066
|
: 0xffffff;
|
|
9015
9067
|
}
|
|
9016
|
-
function drawSelectionHandles(graphics, context, style) {
|
|
9068
|
+
function drawSelectionHandles(graphics, context, style, engine) {
|
|
9017
9069
|
const selected = new Set(context.selectedDetectionIds);
|
|
9018
9070
|
for (const [detectionIndex, detection] of (context.frame?.detections ?? []).entries()) {
|
|
9019
9071
|
if (detection.id === undefined || !selected.has(detection.id))
|
|
@@ -9027,15 +9079,26 @@ function drawSelectionHandles(graphics, context, style) {
|
|
|
9027
9079
|
};
|
|
9028
9080
|
const fill = resolveStyleValue(style.selectionHandle.fill, detection, styleContext) ?? { alpha: 1, color: 0xffffff };
|
|
9029
9081
|
const stroke = resolveStyleValue(style.selectionHandle.stroke, detection, styleContext) ?? { alpha: 1, color: 0x2563eb, width: 2 };
|
|
9030
|
-
|
|
9082
|
+
// Handles ride on the live preview so they follow a move or reshape.
|
|
9083
|
+
const state = engine?.getState();
|
|
9084
|
+
const source = state &&
|
|
9085
|
+
(state.kind === AnnotationGestureStateKind.Moving ||
|
|
9086
|
+
state.kind === AnnotationGestureStateKind.Resizing) &&
|
|
9087
|
+
state.preview &&
|
|
9088
|
+
state.activeDetectionId === detection.id
|
|
9089
|
+
? state.preview
|
|
9090
|
+
: detection;
|
|
9091
|
+
for (const handle of getAnnotationHandles(source, context.viewportScale)) {
|
|
9092
|
+
const alpha = handle.kind === AnnotationHandleKind.AddVertex
|
|
9093
|
+
? style.selectionHandle.addVertexAlpha
|
|
9094
|
+
: handle.kind === AnnotationHandleKind.Keypoint
|
|
9095
|
+
? style.selectionHandle.keypointAlpha
|
|
9096
|
+
: 1;
|
|
9097
|
+
if (alpha <= 0)
|
|
9098
|
+
continue;
|
|
9031
9099
|
graphics.circle(handle.point.x, handle.point.y, handle.radius);
|
|
9032
|
-
graphics.fill({
|
|
9033
|
-
|
|
9034
|
-
alpha: handle.kind === "addVertex"
|
|
9035
|
-
? style.selectionHandle.addVertexAlpha
|
|
9036
|
-
: fill.alpha,
|
|
9037
|
-
});
|
|
9038
|
-
graphics.stroke(resolvePixiStroke(stroke, context.viewportScale));
|
|
9100
|
+
graphics.fill({ ...fill, alpha: fill.alpha * alpha });
|
|
9101
|
+
graphics.stroke(resolvePixiStroke({ ...stroke, alpha: stroke.alpha * alpha }, context.viewportScale));
|
|
9039
9102
|
}
|
|
9040
9103
|
}
|
|
9041
9104
|
}
|
|
@@ -9118,6 +9181,7 @@ const DEFAULT_ANNOTATION_OVERLAY_STYLE = {
|
|
|
9118
9181
|
fill: { alpha: 1, color: 0xffffff },
|
|
9119
9182
|
stroke: { alpha: 1, color: 0x2563eb, width: 2 },
|
|
9120
9183
|
addVertexAlpha: 0.55,
|
|
9184
|
+
keypointAlpha: 1,
|
|
9121
9185
|
},
|
|
9122
9186
|
marquee: { fill: { alpha: 0.2, color: 0x3b82f6 } },
|
|
9123
9187
|
guide: {
|
|
@@ -9310,12 +9374,28 @@ async function createPixiMediaScene(options) {
|
|
|
9310
9374
|
let mediaWidth = 0;
|
|
9311
9375
|
let visibilityVersion = 0;
|
|
9312
9376
|
let visibilityMaskStyleCache = null;
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9377
|
+
// While a move, resize, vertex or keypoint gesture edits a detection, the
|
|
9378
|
+
// base layers hide it and the annotation overlay draws the live preview in
|
|
9379
|
+
// its place, so the edit never shows next to its own stale copy. Masks have
|
|
9380
|
+
// no overlay preview and stay drawn.
|
|
9381
|
+
let editingDetectionId = null;
|
|
9382
|
+
const resolveContextState = (detection) => {
|
|
9383
|
+
const state = resolveAnnotationStyleState(detection, currentVisibility);
|
|
9384
|
+
return editingDetectionId !== null &&
|
|
9385
|
+
detection.id === editingDetectionId &&
|
|
9386
|
+
!state.hidden
|
|
9387
|
+
? { ...state, hidden: true }
|
|
9388
|
+
: state;
|
|
9389
|
+
};
|
|
9390
|
+
// Labels have no overlay preview either: they stay drawn and follow a move
|
|
9391
|
+
// through the fast-translate path, like regions.
|
|
9392
|
+
const resolveLabelContextState = (detection) => {
|
|
9393
|
+
const state = resolveAnnotationStyleState(detection, currentVisibility);
|
|
9394
|
+
return {
|
|
9395
|
+
...state,
|
|
9396
|
+
hidden: currentVisibility?.labelsHidden === true || state.hidden,
|
|
9397
|
+
};
|
|
9398
|
+
};
|
|
9319
9399
|
// The vector layer consumes one shape style; renderer kinds that lower to
|
|
9320
9400
|
// shape instructions compose with the internal hook here.
|
|
9321
9401
|
function resolveVectorShapeStyle() {
|
|
@@ -9388,9 +9468,11 @@ async function createPixiMediaScene(options) {
|
|
|
9388
9468
|
},
|
|
9389
9469
|
onAssetError: options.diagnostics?.onAssetError,
|
|
9390
9470
|
regionRenderers: options.regionRenderers,
|
|
9391
|
-
|
|
9471
|
+
// Regions (badges, icons) have no overlay preview: they stay drawn and
|
|
9472
|
+
// follow a move through the fast-translate path instead.
|
|
9473
|
+
resolveContextState: (detection) => resolveAnnotationStyleState(detection, currentVisibility),
|
|
9392
9474
|
});
|
|
9393
|
-
const annotationOverlayLayer = createPixiAnnotationOverlayLayer(options.editingEngine, options.annotationOverlayStyle);
|
|
9475
|
+
const annotationOverlayLayer = createPixiAnnotationOverlayLayer(options.editingEngine, options.annotationOverlayStyle, options.keypointStyle);
|
|
9394
9476
|
const maskBrushPreview = options.maskBrush
|
|
9395
9477
|
? createPixiMaskBrushPreview({
|
|
9396
9478
|
CanvasSource,
|
|
@@ -9528,34 +9610,52 @@ async function createPixiMediaScene(options) {
|
|
|
9528
9610
|
Text,
|
|
9529
9611
|
UniformGroup,
|
|
9530
9612
|
interactionStyle: options.interactionStyle,
|
|
9531
|
-
|
|
9613
|
+
// The detection under an editing gesture is drawn live by the base
|
|
9614
|
+
// layers (move) or the overlay preview (reshape); its hover/selection
|
|
9615
|
+
// redraw would sit at the stale position.
|
|
9616
|
+
isDetectionVisible: (detection) => !resolveAnnotationStyleState(detection, currentVisibility).hidden &&
|
|
9617
|
+
!isUnderEditingGesture(detection),
|
|
9532
9618
|
})
|
|
9533
9619
|
: undefined;
|
|
9534
9620
|
let fastTranslatedDetectionId = null;
|
|
9535
9621
|
const unsubscribeFastTranslate = options.editingEngine?.subscribeFastTranslate((id, dx, dy) => {
|
|
9536
9622
|
fastTranslatedDetectionId = id;
|
|
9537
|
-
boxLayer.translateDetection(id, dx, dy);
|
|
9538
|
-
vectorLayer.translateDetection(id, dx, dy);
|
|
9539
9623
|
regionLayer.translateDetection(id, dx, dy);
|
|
9540
9624
|
labelLayer?.translateDetection(id, dx, dy);
|
|
9541
9625
|
});
|
|
9542
|
-
const
|
|
9543
|
-
if (state.kind !== AnnotationGestureStateKind.Idle ||
|
|
9544
|
-
fastTranslatedDetectionId === null) {
|
|
9545
|
-
return;
|
|
9546
|
-
}
|
|
9547
|
-
const id = fastTranslatedDetectionId;
|
|
9548
|
-
fastTranslatedDetectionId = null;
|
|
9549
|
-
boxLayer.translateDetection(id, 0, 0);
|
|
9550
|
-
vectorLayer.translateDetection(id, 0, 0);
|
|
9551
|
-
regionLayer.translateDetection(id, 0, 0);
|
|
9552
|
-
labelLayer?.translateDetection(id, 0, 0);
|
|
9626
|
+
const redrawDetectionLayers = (id) => {
|
|
9553
9627
|
boxLayer.invalidateDetection(id);
|
|
9554
9628
|
vectorLayer.invalidateDetection(id);
|
|
9555
9629
|
boxLayer.drawFrame(currentMediaTime, viewportScale);
|
|
9556
9630
|
vectorLayer.drawFrame(currentMediaTime, viewportScale);
|
|
9557
9631
|
regionLayer.drawFrame(currentMediaTime, viewportScale);
|
|
9558
9632
|
labelLayer?.drawFrame(currentMediaTime, viewportScale);
|
|
9633
|
+
};
|
|
9634
|
+
const unsubscribeEditingState = options.editingEngine?.subscribe((state) => {
|
|
9635
|
+
const nextEditingId = (state.kind === AnnotationGestureStateKind.Moving ||
|
|
9636
|
+
state.kind === AnnotationGestureStateKind.Resizing) &&
|
|
9637
|
+
state.activeDetectionId !== null &&
|
|
9638
|
+
state.preview &&
|
|
9639
|
+
!state.preview.mask
|
|
9640
|
+
? state.activeDetectionId
|
|
9641
|
+
: null;
|
|
9642
|
+
if (nextEditingId !== editingDetectionId) {
|
|
9643
|
+
const previous = editingDetectionId;
|
|
9644
|
+
editingDetectionId = nextEditingId;
|
|
9645
|
+
if (previous !== null)
|
|
9646
|
+
redrawDetectionLayers(previous);
|
|
9647
|
+
if (nextEditingId !== null)
|
|
9648
|
+
redrawDetectionLayers(nextEditingId);
|
|
9649
|
+
drawInteractionPresentationLayer(currentMediaTime);
|
|
9650
|
+
}
|
|
9651
|
+
if (state.kind === AnnotationGestureStateKind.Idle &&
|
|
9652
|
+
fastTranslatedDetectionId !== null) {
|
|
9653
|
+
regionLayer.translateDetection(fastTranslatedDetectionId, 0, 0);
|
|
9654
|
+
labelLayer?.translateDetection(fastTranslatedDetectionId, 0, 0);
|
|
9655
|
+
fastTranslatedDetectionId = null;
|
|
9656
|
+
regionLayer.drawFrame(currentMediaTime, viewportScale);
|
|
9657
|
+
labelLayer?.drawFrame(currentMediaTime, viewportScale);
|
|
9658
|
+
}
|
|
9559
9659
|
});
|
|
9560
9660
|
const handleKeyDown = (event) => {
|
|
9561
9661
|
if (event.key === "Tab" && interactionLayer) {
|
|
@@ -9984,6 +10084,7 @@ async function createPixiMediaScene(options) {
|
|
|
9984
10084
|
polylineStyle: presentation.polylineStyle,
|
|
9985
10085
|
keypointStyle: presentation.keypointStyle,
|
|
9986
10086
|
});
|
|
10087
|
+
annotationOverlayLayer.setKeypointStyle(presentation.keypointStyle);
|
|
9987
10088
|
regionLayer.setRenderers(presentation.renderers?.filter((renderer) => renderer.kind === "region") ?? []);
|
|
9988
10089
|
if (presentation.boxCornerStyle !== undefined ||
|
|
9989
10090
|
presentation.ellipseStyle !== undefined ||
|
|
@@ -10269,10 +10370,10 @@ async function createPixiMediaScene(options) {
|
|
|
10269
10370
|
const interactionState = interactionLayer?.getState();
|
|
10270
10371
|
focusLayer.drawFrame({
|
|
10271
10372
|
frame,
|
|
10272
|
-
hoveredPick: filterVisiblePick(interactionState?.hoveredPick ?? null),
|
|
10373
|
+
hoveredPick: withEditingPreview(filterVisiblePick(interactionState?.hoveredPick ?? null)),
|
|
10273
10374
|
idMaskArtifact: maskLayer?.getActiveIdMaskFrameTexture() ?? null,
|
|
10274
10375
|
mediaTime,
|
|
10275
|
-
selectedPick: filterVisiblePick(interactionState?.selectedPick ?? null),
|
|
10376
|
+
selectedPick: withEditingPreview(filterVisiblePick(interactionState?.selectedPick ?? null)),
|
|
10276
10377
|
viewportScale,
|
|
10277
10378
|
});
|
|
10278
10379
|
}
|
|
@@ -10365,6 +10466,27 @@ async function createPixiMediaScene(options) {
|
|
|
10365
10466
|
? pick
|
|
10366
10467
|
: null;
|
|
10367
10468
|
}
|
|
10469
|
+
function editingPreviewFor(detection) {
|
|
10470
|
+
const state = options.editingEngine?.getState();
|
|
10471
|
+
return state &&
|
|
10472
|
+
(state.kind === AnnotationGestureStateKind.Moving ||
|
|
10473
|
+
state.kind === AnnotationGestureStateKind.Resizing) &&
|
|
10474
|
+
state.preview &&
|
|
10475
|
+
detection.id !== undefined &&
|
|
10476
|
+
state.activeDetectionId === detection.id
|
|
10477
|
+
? state.preview
|
|
10478
|
+
: null;
|
|
10479
|
+
}
|
|
10480
|
+
function isUnderEditingGesture(detection) {
|
|
10481
|
+
return editingPreviewFor(detection) !== null;
|
|
10482
|
+
}
|
|
10483
|
+
/** Point a pick at the live editing preview so focus follows the gesture. */
|
|
10484
|
+
function withEditingPreview(pick) {
|
|
10485
|
+
if (!pick)
|
|
10486
|
+
return null;
|
|
10487
|
+
const preview = editingPreviewFor(pick.detection);
|
|
10488
|
+
return preview ? { ...pick, detection: preview } : pick;
|
|
10489
|
+
}
|
|
10368
10490
|
}
|
|
10369
10491
|
function canReuseMaskVisibilityArtifacts(previous, next) {
|
|
10370
10492
|
return ((previous?.annotationsHidden === true) ===
|