Skip to content

Commit

Permalink
feat(annotation): AnnotationLayerView now shows currently displayed a…
Browse files Browse the repository at this point in the history
…nnotations in MultiscaleAnnotationSource
  • Loading branch information
chrisj committed Sep 28, 2023
1 parent 29d78e4 commit 53821ff
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 11 deletions.
9 changes: 1 addition & 8 deletions src/neuroglancer/annotation/annotation_layer_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,7 @@ export class WatchableAnnotationRelationshipStates extends
nestedContext.registerDisposer(segmentationGroupState.changed.add(this.changed.dispatch));
nestedContext.registerDisposer(registerNested((groupContext, groupState) => {
const {visibleSegments} = groupState;
let wasEmpty = visibleSegments.size === 0;
groupContext.registerDisposer(visibleSegments.changed.add(() => {
const isEmpty = visibleSegments.size === 0;
if (isEmpty !== wasEmpty) {
wasEmpty = isEmpty;
this.changed.dispatch();
}
}));
groupContext.registerDisposer(visibleSegments.changed.add(this.changed.dispatch));
}, segmentationGroupState));
}, segmentationState));
});
Expand Down
73 changes: 72 additions & 1 deletion src/neuroglancer/annotation/frontend_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {Annotation, AnnotationId, AnnotationPropertySerializer, AnnotationProper
import {ANNOTATION_COMMIT_UPDATE_RESULT_RPC_ID, ANNOTATION_COMMIT_UPDATE_RPC_ID, ANNOTATION_GEOMETRY_CHUNK_SOURCE_RPC_ID, ANNOTATION_METADATA_CHUNK_SOURCE_RPC_ID, ANNOTATION_REFERENCE_ADD_RPC_ID, ANNOTATION_REFERENCE_DELETE_RPC_ID, ANNOTATION_SUBSET_GEOMETRY_CHUNK_SOURCE_RPC_ID, AnnotationGeometryChunkSpecification} from 'neuroglancer/annotation/base';
import {getAnnotationTypeRenderHandler} from 'neuroglancer/annotation/type_handler';
import {Chunk, ChunkManager, ChunkSource} from 'neuroglancer/chunk_manager/frontend';
import {getObjectKey} from 'neuroglancer/segmentation_display_state/base';
import {forEachVisibleSegment, getObjectKey} from 'neuroglancer/segmentation_display_state/base';
import {SliceViewSourceOptions} from 'neuroglancer/sliceview/base';
import {MultiscaleSliceViewChunkSource, SliceViewChunk, SliceViewChunkSource, SliceViewChunkSourceOptions, SliceViewSingleResolutionSource} from 'neuroglancer/sliceview/frontend';
import {StatusMessage} from 'neuroglancer/status';
Expand All @@ -29,6 +29,8 @@ import {NullarySignal, Signal} from 'neuroglancer/util/signal';
import {Buffer} from 'neuroglancer/webgl/buffer';
import {GL} from 'neuroglancer/webgl/context';
import {registerRPC, registerSharedObjectOwner, RPC, SharedObject} from 'neuroglancer/worker_rpc';
import {AnnotationLayerState} from 'neuroglancer/annotation/annotation_layer_state';
import {ChunkState} from 'neuroglancer/chunk_manager/base';

export interface AnnotationGeometryChunkSourceOptions extends SliceViewChunkSourceOptions {
spec: AnnotationGeometryChunkSpecification;
Expand Down Expand Up @@ -377,6 +379,37 @@ export function makeTemporaryChunk() {
{data: new Uint8Array(0), numPickIds: 0, typeToOffset, typeToIds, typeToIdMaps});
}

export function deserializeAnnotations(
serializedAnnotations: SerializedAnnotations,
rank: number, properties: Readonly<AnnotationPropertySpec>[]) {
const annotations: Annotation[] = [];
const annotationBuffer = serializedAnnotations.data;
let annotation: Annotation|undefined;
for (let [annotationType, annotationsOfType] of serializedAnnotations.typeToIdMaps.entries()) {
const handler = annotationTypeHandlers[annotationType as AnnotationType];
const numGeometryBytes = handler.serializedBytes(rank);
const baseOffset = annotationBuffer.byteOffset;
const dataView = new DataView(annotationBuffer.buffer);
const isLittleEndian = Endianness.LITTLE === ENDIANNESS;
const annotationPropertySerializer =
new AnnotationPropertySerializer(rank, numGeometryBytes, properties);
const annotationCount = annotationsOfType.size;
for (const [annotationId, annotationIndex] of annotationsOfType) {
annotation = handler.deserialize(
dataView,
baseOffset +
annotationPropertySerializer.propertyGroupBytes[0] *
annotationIndex,
isLittleEndian, rank, annotationId);
annotationPropertySerializer.deserialize(
dataView, baseOffset, annotationIndex, annotationCount, isLittleEndian,
annotation.properties = new Array(properties.length));
annotations.push(annotation);
}
}
return annotations;
}

export class MultiscaleAnnotationSource extends SharedObject implements
MultiscaleSliceViewChunkSource<AnnotationGeometryChunkSource>, AnnotationSourceSignals {
OPTIONS: {};
Expand Down Expand Up @@ -409,6 +442,44 @@ export class MultiscaleAnnotationSource extends SharedObject implements
}
}

activeAnnotations(state: AnnotationLayerState): Annotation[] {
const annotations: Annotation[] = [];
const {segmentFilteredSources, spatiallyIndexedSources, rank, properties, relationships} = this;
const {relationshipStates} = state.displayState;
let hasVisibleSegments = false;
for (let i = 0; i < relationships.length; i++) {
const relationship = relationships[i];
const state = relationshipStates.get(relationship)
if (state) {
const {showMatches: {value: showMatches}, segmentationState: {value: segmentationState}} = state;
if (!showMatches || !segmentationState) continue;
const chunks = segmentFilteredSources[i].chunks;
forEachVisibleSegment(segmentationState.segmentationGroupState.value, objectId => {
hasVisibleSegments = true;
const key = getObjectKey(objectId);
const chunk = chunks.get(key);
if (chunk !== undefined && chunk.state === ChunkState.GPU_MEMORY) {
const {data} = chunk;
if (data === undefined) return;
const {serializedAnnotations} = data;
annotations.push(...deserializeAnnotations(serializedAnnotations, rank, properties));
}
});
}
}
if (!hasVisibleSegments) {
for (const source of spatiallyIndexedSources) {
for (const [_key, chunk] of source.chunks) {
const {data} = chunk;
if (data === undefined) continue;
const {serializedAnnotations} = data;
annotations.push(...deserializeAnnotations(serializedAnnotations, rank, properties));
}
}
}
return annotations;
}

hasNonSerializedProperties() {
return this.relationships.length > 0;
}
Expand Down
15 changes: 13 additions & 2 deletions src/neuroglancer/ui/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,14 @@ export class AnnotationLayerView extends Tab {
(annotationId) => this.deleteAnnotationElement(annotationId, state)));
}
refCounted.registerDisposer(state.transform.changed.add(this.forceUpdateView));
refCounted.registerDisposer(state.displayState.relationshipStates.changed.add(this.forceUpdateView));
newAttachedAnnotationStates.set(
state, {refCounted, annotations: [], idToIndex: new Map(), listOffset: 0});
if (source instanceof MultiscaleAnnotationSource) {
refCounted.registerDisposer(source.chunkManager.chunkQueueManager.visibleChunksChanged.add(() => {
this.forceUpdateView();
}));
}
}
this.attachedAnnotationStates = newAttachedAnnotationStates;
attachedAnnotationStates.clear();
Expand Down Expand Up @@ -538,7 +544,7 @@ export class AnnotationLayerView extends Tab {
if (!state.source.readonly) isMutable = true;
if (state.chunkTransform.value.error !== undefined) continue;
const {source} = state;
const annotations = Array.from(source);
const annotations = source instanceof MultiscaleAnnotationSource ? source.activeAnnotations(state) : Array.from(source);
info.annotations = annotations;
const {idToIndex} = info;
idToIndex.clear();
Expand All @@ -551,8 +557,13 @@ export class AnnotationLayerView extends Tab {
}
const oldLength = this.virtualListSource.length;
this.updateListLength();
// TODO, what problems does this change cause?
// this prevents the scroll list position from resetting when updateView is run
const insertCount = Math.max(0, listElements.length - oldLength);
const deleteCount = Math.max(0, oldLength - listElements.length);
const retainCount = Math.min(listElements.length, oldLength);
this.virtualListSource.changed!.dispatch(
[{retainCount: 0, deleteCount: oldLength, insertCount: listElements.length}]);
[{retainCount, deleteCount, insertCount}]);
this.mutableControls.style.display = isMutable ? 'contents' : 'none';
this.resetOnUpdate();
}
Expand Down

0 comments on commit 53821ff

Please sign in to comment.