diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 1a22308b1..a6f9e0b1a 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1255,6 +1255,9 @@ export class SegmentationUserLayer extends Base { readonly selectedSpatialSkeletonNodeId = new WatchableValue< number | undefined >(undefined); + readonly hoveredSpatialSkeletonNodeId = new WatchableValue< + number | undefined + >(undefined); readonly spatialSkeletonVisibleChunksNeeded = new WatchableValue(0); readonly spatialSkeletonVisibleChunksAvailable = new WatchableValue(0); readonly spatialSkeletonVisibleChunksLoaded = new WatchableValue(false); @@ -1493,6 +1496,20 @@ export class SegmentationUserLayer extends Base { }, pin); }; + captureSelectionState( + state: this["selectionState"], + mouseState: MouseSelectionState, + ) { + super.captureSelectionState(state, mouseState); + const nodeId = getSpatialSkeletonNodeIdFromViewerHover(mouseState, this); + if (nodeId === undefined) return; + state.spatialSkeletonNodeId = String(nodeId); + const segmentId = mouseState.pickedSpatialSkeletonSegmentId; + if (segmentId !== undefined && Number.isSafeInteger(segmentId) && segmentId > 0) { + state.spatialSkeletonSegmentId = String(segmentId); + } + } + filterBySegmentLabel = (id: bigint) => { const augmented = augmentSegmentId(this.displayState, id); const { label } = augmented; @@ -1569,6 +1586,18 @@ export class SegmentationUserLayer extends Base { ), ); syncSelectedSpatialSkeletonNodeIdFromGlobalSelection(); + const syncHoveredSpatialSkeletonNodeId = () => { + this.hoveredSpatialSkeletonNodeId.value = + getSpatialSkeletonNodeIdFromViewerHover( + this.manager.layerSelectedValues.mouseState, + this, + ); + }; + this.registerDisposer( + this.manager.layerSelectedValues.changed.add( + syncHoveredSpatialSkeletonNodeId, + ), + ); this.displayState.selectedAlpha.changed.add( this.specificationChanged.dispatch, ); diff --git a/src/skeleton/frontend.ts b/src/skeleton/frontend.ts index 6beb66180..3ee52f454 100644 --- a/src/skeleton/frontend.ts +++ b/src/skeleton/frontend.ts @@ -2865,6 +2865,16 @@ export class SpatiallyIndexedSkeletonLayer ); } + resolveNodePickFromChunk( + chunk: SpatiallyIndexedSkeletonChunk, + pickedOffset: number, + ): number | undefined { + for (const [nodeId, vertexIndex] of chunk.nodeMap) { + if (vertexIndex === pickedOffset) return nodeId; + } + return undefined; + } + updateVisibleChunksForView( view: SpatiallyIndexedSkeletonView, transformedSources: readonly TransformedSource[][], @@ -3560,6 +3570,15 @@ export class PerspectiveViewSpatiallyIndexedSkeletonLayer extends PerspectiveVie if (segmentId !== undefined) { mouseState.pickedSpatialSkeletonSegmentId = segmentId; } + if (pickData.kind === "segment-node") { + const nodeId = this.base.resolveNodePickFromChunk( + pickData.chunk, + pickedOffset, + ); + if (nodeId !== undefined) { + mouseState.pickedSpatialSkeletonNodeId = nodeId; + } + } } } @@ -3865,6 +3884,15 @@ export class SliceViewPanelSpatiallyIndexedSkeletonLayer extends SliceViewPanelR if (segmentId !== undefined) { mouseState.pickedSpatialSkeletonSegmentId = segmentId; } + if (pickData.kind === "segment-node") { + const nodeId = this.base.resolveNodePickFromChunk( + pickData.chunk, + pickedOffset, + ); + if (nodeId !== undefined) { + mouseState.pickedSpatialSkeletonNodeId = nodeId; + } + } } } diff --git a/src/ui/spatial_skeleton_edit_tab.ts b/src/ui/spatial_skeleton_edit_tab.ts index 56ae48525..20c37a4ea 100644 --- a/src/ui/spatial_skeleton_edit_tab.ts +++ b/src/ui/spatial_skeleton_edit_tab.ts @@ -31,7 +31,6 @@ import svg_retweet from "ikonate/icons/retweet.svg?raw"; import svg_share_android from "ikonate/icons/share-android.svg?raw"; import svg_undo from "ikonate/icons/undo.svg?raw"; import type { SegmentationUserLayer } from "#src/layer/segmentation/index.js"; -import { getSpatialSkeletonNodeIdFromViewerHover } from "#src/layer/segmentation/selection.js"; import { executeSpatialSkeletonDeleteNode, executeSpatialSkeletonNodeTrueEndUpdate, @@ -316,7 +315,6 @@ export class SpatialSkeletonEditTab extends Tab { const renderedRowsByNodeId = new Map(); const renderedEntriesByNodeId = new Map(); const skeletonState = layer.spatialSkeletonState; - const mouseState = layer.manager.root.layerSelectedValues.mouseState; const navigationGraphCache = new Map< number, { @@ -528,7 +526,7 @@ export class SpatialSkeletonEditTab extends Tab { layer.getSpatialSkeletonNodeDisplayDescription(node); const getHoveredNodeIdFromViewer = () => { - return getSpatialSkeletonNodeIdFromViewerHover(mouseState, layer); + return layer.hoveredSpatialSkeletonNodeId.value; }; const applyRowInteractionState = ( @@ -1742,7 +1740,7 @@ export class SpatialSkeletonEditTab extends Tab { }), ); this.registerDisposer( - mouseState.changed.add(() => { + layer.hoveredSpatialSkeletonNodeId.changed.add(() => { updateHoveredViewerNode(); }), );