From d8a25023b4ce50890177ec7054f6e0311e816b0b Mon Sep 17 00:00:00 2001 From: Chris Jordan Date: Tue, 17 Sep 2024 14:46:47 -0400 Subject: [PATCH] temp3 --- src/annotation/annotation_layer_state.ts | 2 +- src/annotation/frontend_source.ts | 9 +- src/annotation/index.ts | 58 +++-- src/annotation/renderlayer.ts | 8 +- src/datasource/graphene/frontend.ts | 2 +- src/layer/annotation/index.ts | 277 +++++++++++++++-------- src/layer/annotation/style.css | 18 +- src/ui/annotations.ts | 19 +- 8 files changed, 266 insertions(+), 127 deletions(-) diff --git a/src/annotation/annotation_layer_state.ts b/src/annotation/annotation_layer_state.ts index c9da60d6b..d2c0aa56a 100644 --- a/src/annotation/annotation_layer_state.ts +++ b/src/annotation/annotation_layer_state.ts @@ -132,7 +132,7 @@ void main() { export class AnnotationDisplayState extends RefCounted { annotationProperties = new WatchableValue< - AnnotationPropertySpec[] | undefined + readonly Readonly[] | undefined >(undefined); shader = makeTrackableFragmentMain(DEFAULT_FRAGMENT_MAIN); shaderControls = new ShaderControlState( diff --git a/src/annotation/frontend_source.ts b/src/annotation/frontend_source.ts index 1d6bc9423..f150a4d12 100644 --- a/src/annotation/frontend_source.ts +++ b/src/annotation/frontend_source.ts @@ -56,6 +56,7 @@ import { SliceViewChunkSource, } from "#src/sliceview/frontend.js"; import { StatusMessage } from "#src/status.js"; +import type { WatchableValue } from "#src/trackable_value.js"; import type { Borrowed, Owned } from "#src/util/disposable.js"; import { ENDIANNESS, Endianness } from "#src/util/endian.js"; import * as matrix from "#src/util/matrix.js"; @@ -517,7 +518,9 @@ export class MultiscaleAnnotationSource spatiallyIndexedSources = new Set>(); rank: number; readonly relationships: readonly string[]; - readonly properties: Readonly[]; + readonly properties: WatchableValue< + readonly Readonly[] + >; readonly annotationPropertySerializers: AnnotationPropertySerializer[]; constructor( public chunkManager: Borrowed, @@ -529,10 +532,10 @@ export class MultiscaleAnnotationSource ) { super(); this.rank = options.rank; - this.properties = options.properties; + this.properties.value = options.properties; this.annotationPropertySerializers = makeAnnotationPropertySerializers( this.rank, - this.properties, + this.properties.value, ); const segmentFilteredSources: Owned[] = (this.segmentFilteredSources = []); diff --git a/src/annotation/index.ts b/src/annotation/index.ts index 5b02bc02a..7bf122438 100644 --- a/src/annotation/index.ts +++ b/src/annotation/index.ts @@ -23,6 +23,7 @@ import type { CoordinateSpaceTransform, WatchableCoordinateSpaceTransform, } from "#src/coordinate_transform.js"; +import { WatchableValue } from "#src/trackable_value.js"; import { arraysEqual } from "#src/util/array.js"; import { packColor, @@ -532,7 +533,7 @@ export function formatAnnotationPropertyValue( export function parseAnnotationPropertyId(obj: unknown) { const s = verifyString(obj); - if (s.match(/^[a-z][a-zA-Z0-9_ ]*$/) === null) { + if (s.match(/^[a-zA-Z0-9_ -]+$/) === null) { throw new Error(`Invalid property identifier: ${JSON.stringify(obj)}`); } return s; @@ -617,13 +618,19 @@ function parseAnnotationPropertySpec(obj: unknown): AnnotationPropertySpec { } function annotationPropertySpecToJson(spec: AnnotationPropertySpec) { + console.log("annotationPropertySpecToJson", spec); const defaultValue = spec.default; - const tag = isAnnotationNumericPropertySpec(spec) ? spec.tag : undefined; + const isNumeric = isAnnotationNumericPropertySpec(spec); + const tag = isNumeric ? spec.tag : undefined; + const enum_values = isNumeric ? spec.enumValues : undefined; + const enum_labels = isNumeric ? spec.enumLabels : undefined; return { id: spec.identifier, description: spec.description, type: spec.type, tag, + enum_values, + enum_labels, default: defaultValue === 0 ? undefined @@ -1013,7 +1020,7 @@ export const annotationTypeHandlers: Record< export interface AnnotationSchema { rank: number; relationships: readonly string[]; - properties: readonly AnnotationPropertySpec[]; + properties: WatchableValue[]>; } export function annotationToJson( @@ -1033,8 +1040,8 @@ export function annotationToJson( segments.map((x) => x.toString()), ); } - if (schema.properties.length !== 0) { - const propertySpecs = schema.properties; + const propertySpecs = schema.properties.value; + if (propertySpecs.length !== 0) { result.props = annotation.properties.map((prop, i) => annotationPropertyTypeHandlers[propertySpecs[i].type].serializeJson(prop), ); @@ -1074,9 +1081,9 @@ function restoreAnnotation( ); }); const properties = verifyObjectProperty(obj, "props", (propsObj) => { - const propSpecs = schema.properties; + const propSpecs = schema.properties.value; if (propsObj === undefined) return propSpecs.map((x) => x.default); - return parseArray(expectArray(propsObj, schema.properties.length), (x, i) => + return parseArray(expectArray(propsObj, propSpecs.length), (x, i) => annotationPropertyTypeHandlers[propSpecs[i].type].deserializeJson(x), ); }); @@ -1121,16 +1128,20 @@ export class AnnotationSource annotationPropertySerializers: AnnotationPropertySerializer[]; + // new WatchableValue([]) + constructor( rank: number, public readonly relationships: readonly string[] = [], - public readonly properties: Readonly[] = [], + public readonly properties: WatchableValue< + readonly Readonly[] + > = new WatchableValue([]), ) { super(); this.rank_ = rank; this.annotationPropertySerializers = makeAnnotationPropertySerializers( rank, - properties, + properties.value, ); } @@ -1274,7 +1285,9 @@ export class LocalAnnotationSource extends AnnotationSource { constructor( public watchableTransform: WatchableCoordinateSpaceTransform, - public properties: AnnotationPropertySpec[], + public readonly properties: WatchableValue< + AnnotationPropertySpec[] + > = new WatchableValue([]), relationships: string[], ) { super(watchableTransform.value.sourceRank, relationships, properties); @@ -1282,24 +1295,43 @@ export class LocalAnnotationSource extends AnnotationSource { this.registerDisposer( watchableTransform.changed.add(() => this.ensureUpdated()), ); + + this.registerDisposer( + properties.changed.add(() => { + this.updateAnnotationPropertySerializers(); + this.changed.dispatch(); + }), + ); } updateAnnotationPropertySerializers() { this.annotationPropertySerializers = makeAnnotationPropertySerializers( this.rank_, - this.properties, + this.properties.value, ); } addProperty(property: AnnotationPropertySpec) { - this.properties.push(property); - this.updateAnnotationPropertySerializers(); + this.properties.value.push(property); + this.properties.changed.dispatch(); for (const annotation of this) { annotation.properties.push(property.default); } this.changed.dispatch(); } + removeProperty(identifier: string) { + const propertyIndex = this.properties.value.findIndex( + (x) => x.identifier === identifier, + ); + this.properties.value.splice(propertyIndex, 1); + this.properties.changed.dispatch(); + for (const annotation of this) { + annotation.properties.splice(propertyIndex, 1); + } + this.changed.dispatch(); + } + ensureUpdated() { const transform = this.watchableTransform.value; const { curCoordinateTransform } = this; diff --git a/src/annotation/renderlayer.ts b/src/annotation/renderlayer.ts index 836fa8dd1..431bc4dca 100644 --- a/src/annotation/renderlayer.ts +++ b/src/annotation/renderlayer.ts @@ -483,7 +483,9 @@ function AnnotationRenderLayer< for (const oldHelper of renderHelpers) { oldHelper.dispose(); } - const { properties } = this.base.source; + const { + properties: { value: properties }, + } = this.base.source; const { displayState } = this.base.state; for (const annotationType of annotationTypes) { const handler = getAnnotationTypeRenderHandler(annotationType); @@ -780,7 +782,9 @@ function AnnotationRenderLayer< transformPickedValue(pickState: PickState) { const { pickedAnnotationBuffer } = pickState; if (pickedAnnotationBuffer === undefined) return undefined; - const { properties } = this.base.source; + const { + properties: { value: properties }, + } = this.base.source; if (properties.length === 0) return undefined; const { pickedAnnotationBufferBaseOffset, diff --git a/src/datasource/graphene/frontend.ts b/src/datasource/graphene/frontend.ts index 9c496c852..8b33d4acf 100644 --- a/src/datasource/graphene/frontend.ts +++ b/src/datasource/graphene/frontend.ts @@ -785,7 +785,7 @@ function makeColoredAnnotationState( const { subsourceEntry } = loadedSubsource; const source = new LocalAnnotationSource( loadedSubsource.loadedDataSource.transform, - [], + new WatchableValue([]), ["associated segments"], ); diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index f07256f3e..171716e21 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -23,6 +23,7 @@ import type { AnnotationPropertySpec } from "#src/annotation/index.js"; import { annotationPropertySpecsToJson, AnnotationType, + isAnnotationNumericPropertySpec, LocalAnnotationSource, parseAnnotationPropertySpecs, } from "#src/annotation/index.js"; @@ -48,6 +49,7 @@ import { TrackableBooleanCheckbox } from "#src/trackable_boolean.js"; import { makeCachedLazyDerivedWatchableValue, TrackableValue, + WatchableValue, } from "#src/trackable_value.js"; import type { AnnotationLayerView, @@ -95,7 +97,7 @@ import { VirtualList } from "#src/widget/virtual_list.js"; const POINTS_JSON_KEY = "points"; const ANNOTATIONS_JSON_KEY = "annotations"; -const TAGS_JSON_KEY = "tags"; +// const TAGS_JSON_KEY = "tags"; const ANNOTATION_PROPERTIES_JSON_KEY = "annotationProperties"; const ANNOTATION_RELATIONSHIPS_JSON_KEY = "annotationRelationships"; const CROSS_SECTION_RENDER_SCALE_JSON_KEY = "crossSectionAnnotationSpacing"; @@ -398,71 +400,83 @@ const TOOL_ID = "foofoofoo"; class TagTool extends LayerTool { constructor( - public tag: string, + public propertyIdentifier: string, layer: AnnotationUserLayer, - toggle?: boolean, ) { - super(layer, toggle); + super(layer, true); + // this.registerDisposer( + // tag.changed.add(() => { + // console.log("tag changed!"); + // }), + // ); } activate(activation: ToolActivation) { - console.log("I want to tag", this.tag); - const { localAnnotations } = this.layer; - if (localAnnotations) { - const ourSelectionState = - this.layer.manager.root.selectionState.value?.layers.find( - (x) => x.layer === this.layer, - ); - if (ourSelectionState && ourSelectionState.state.annotationId) { - console.log("annotationId", ourSelectionState.state.annotationId); - const identifier = `tag_${this.tag}`; - const existing = localAnnotations.properties.find( - (x) => x.identifier === identifier, - ); - if (!existing) { - localAnnotations.addProperty({ - type: "uint8", - tag: true, - enumValues: [0, 1], - enumLabels: ["", this.tag], - default: 0, - description: this.tag, - identifier, - }); - localAnnotations.changed.dispatch(); - // this.layer.specificationChanged.dispatch(); // add property to JSON (or we could create the properties from the tags which might ensure greater consistency) - } + activation; + // console.log("I want to tag", this.tag); + // const { localAnnotations } = this.layer; + // if (localAnnotations) { + // const ourSelectionState = + // this.layer.manager.root.selectionState.value?.layers.find( + // (x) => x.layer === this.layer, + // ); + // if (ourSelectionState && ourSelectionState.state.annotationId) { + // console.log("annotationId", ourSelectionState.state.annotationId); + // const identifier = `tag_${this.index}`; + // const existing = localAnnotations.properties.find( + // (x) => x.identifier === identifier, + // ); + // if (!existing) { + // const existingProperty = localAnnotations.properties.find( + // (x) => x.identifier === identifier, + // ); - const annotation = localAnnotations.get( - ourSelectionState.state.annotationId, - ); + // // TODO, need to respond to tag changes + // localAnnotations.addProperty({ + // type: "uint8", + // tag: true, + // enumValues: [0, 1], + // enumLabels: ["", this.tag.value], + // default: 0, + // description: this.tag.value, + // identifier, + // }); + // localAnnotations.changed.dispatch(); + // // this.layer.specificationChanged.dispatch(); // add property to JSON (or we could create the properties from the tags which might ensure greater consistency) + // } - if (annotation) { - console.log("annotation", annotation); - const propertyIndex = localAnnotations.properties.findIndex( - (x) => x.identifier === identifier, - ); - if (propertyIndex > -1) { - annotation.properties[propertyIndex] = - 1 - annotation.properties[propertyIndex]; - localAnnotations.changed.dispatch(); - this.layer.manager.root.selectionState.changed.dispatch(); // TODO, this is probably not the best way to handle it - } - } - } - } - activation.cancel(); + // const annotation = localAnnotations.get( + // ourSelectionState.state.annotationId, + // ); + + // if (annotation) { + // console.log("annotation", annotation); + // const propertyIndex = localAnnotations.properties.findIndex( + // (x) => x.identifier === identifier, + // ); + // if (propertyIndex > -1) { + // annotation.properties[propertyIndex] = + // 1 - annotation.properties[propertyIndex]; + // localAnnotations.changed.dispatch(); + // this.layer.manager.root.selectionState.changed.dispatch(); // TODO, this is probably not the best way to handle it + // } + // } + // } + // } + // activation.cancel(); } toJSON() { - return `${TOOL_ID}_${this.tag}`; + return `${TOOL_ID}_${this.propertyIdentifier}`; } get description() { - return `tag ${this.tag}`; + return `tag ${this.propertyIdentifier}`; } } +TagTool; + class TagsTab extends Tab { tools = new Set(); @@ -470,18 +484,39 @@ class TagsTab extends Tab { super(); const { element } = this; element.classList.add("neuroglancer-tags-tab"); - const { tags } = layer; + // const { tags } = layer; const addTagControl = document.createElement("div"); addTagControl.classList.add("neuroglancer-add-tag-control"); const inputElement = document.createElement("input"); inputElement.required = true; + + const { localAnnotations } = layer; + if (!localAnnotations) return; + const { properties } = localAnnotations; + + const getTagProperties = () => { + const numericProps = properties.value.filter( + isAnnotationNumericPropertySpec, + ); // for type + return numericProps.filter((x) => x.tag); + }; + const addNewTagButton = makeAddButton({ title: "Add additional tag", onClick: () => { const { value } = inputElement; - if (inputElement.validity.valid && !tags.value.includes(value)) { - tags.value.push(value); - tags.changed.dispatch(); + if (inputElement.validity.valid) { + if (!getTagProperties().find((x) => x.description === value)) { + localAnnotations.addProperty({ + type: "uint8", + tag: true, + enumValues: [0, 1], + enumLabels: ["", value], + default: 0, + description: value, + identifier: self.crypto.randomUUID(), // TODO + }); + } inputElement.value = ""; } }, @@ -494,24 +529,48 @@ class TagsTab extends Tab { element.appendChild(tagsContainer); const listSource: VirtualListSource = { - length: tags.value.length, + length: getTagProperties().length, render: (index: number) => { + console.log("RENDER"); const el = document.createElement("div"); + const tag = getTagProperties()[index]; + // const tag = tags[index]; el.classList.add("neuroglancer-tag-list-entry"); - const tag = tags.value[index]; const tool = makeToolButton(this, layer.toolBinder, { - toolJson: `${TOOL_ID}_${tag}`, - label: tag, + toolJson: `${TOOL_ID}_${index}`, + // label: tag, title: `Tag selected annotation with ${tag}`, }); el.append(tool); + const inputElement = document.createElement("input"); + inputElement.required = true; + el.append(inputElement); + inputElement.value = tag.description || ""; + inputElement.addEventListener("change", () => { + const { value } = inputElement; + if ( + getTagProperties().find( + (x) => + x.enumLabels && + x.enumLabels.length > 1 && + x.enumLabels[1] === value, + ) + ) { + inputElement.value = tag.enumLabels![1] || ""; // revert + return; + } + console.log("IE change"); + tag.description = value; + tag.enumLabels![1] = value; + properties.changed.dispatch(); + }); const end = document.createElement("div"); const deleteButton = makeDeleteButton({ title: "Delete tag", onClick: (event) => { event.stopPropagation(); event.preventDefault(); - tags.value = tags.value.filter((x) => x !== tag); + localAnnotations.removeProperty(tag.identifier); }, }); deleteButton.classList.add("neuroglancer-tag-list-entry-delete"); @@ -531,8 +590,8 @@ class TagsTab extends Tab { list.body.classList.add("neuroglancer-tag-list"); this.registerDisposer( - tags.changed.add(() => { - listSource.length = tags.value.length; + properties.changed.add(() => { + listSource.length = getTagProperties().length; listSource.changed!.dispatch([ { retainCount: 0, @@ -542,10 +601,12 @@ class TagsTab extends Tab { ]); }), ); - tags.changed.dispatch(); + properties.changed.dispatch(); // just to get the list to update, is it needed? } } +TrackableValue; + const Base = UserLayerWithAnnotationsMixin(UserLayer); export class AnnotationUserLayer extends Base { localAnnotations: LocalAnnotationSource | undefined; @@ -553,7 +614,13 @@ export class AnnotationUserLayer extends Base { private localAnnotationRelationships: string[]; private localAnnotationsJson: any = undefined; private pointAnnotationsJson: any = undefined; - tags: TrackableValue = new TrackableValue([], verifyStringArray); + // tags: TrackableValue[]> = new TrackableValue( + // [], + // (a: any) => { + // const tagsAsStringArray = verifyStringArray(a); + // return tagsAsStringArray.map((x) => new WatchableValue(x)); + // }, + // ); linkedSegmentationLayers = this.registerDisposer( new LinkedSegmentationLayers( this.manager.rootLayers, @@ -584,33 +651,41 @@ export class AnnotationUserLayer extends Base { this.annotationProjectionRenderScaleTarget.changed.add( this.specificationChanged.dispatch, ); - const registeredTools = new Set(); - this.tags.changed.add(() => { - for (const tag of this.tags.value) { - if (!registeredTools.has(tag)) { - registerTool(AnnotationUserLayer, `${TOOL_ID}_${tag}`, (layer) => { - return new TagTool(tag, layer, true); - }); - registeredTools.add(tag); - } - } - for (const tag of registeredTools) { - if (!this.tags.value.includes(tag)) { - unregisterTool(AnnotationUserLayer, `${TOOL_ID}_${tag}`); - registeredTools.delete(tag); + registerTool; + unregisterTool; - for (const [key, tool] of this.toolBinder.bindings.entries()) { - if (tool instanceof TagTool && tool.tag === tag) { - this.toolBinder.deleteTool(key); - } - } + // let counter = 0; - console.log("local bindings", this.toolBinder.bindings); - } - } - this.specificationChanged.dispatch(); - }); + // const tools = new Map(); + + // this.tags.changed.add(() => { + // for (const [idx, tag] of this.tags.value.entries()) { + // if (!tools.has(idx)) { + // registerTool(AnnotationUserLayer, `${TOOL_ID}_${idx}`, (layer) => { + // const tool = new TagTool(id, layer); + // tools.set(idx, tool); + // return tool; + // }); + // } + // } + // unregisterTool; + // // for (const tag of registeredTools) { + // // if (!this.tags.value.includes(tag)) { + // // unregisterTool(AnnotationUserLayer, `${TOOL_ID}_${tag}`); + // // registeredTools.delete(tag); + + // // for (const [key, tool] of this.toolBinder.bindings.entries()) { + // // if (tool instanceof TagTool && tool.tag === tag) { + // // this.toolBinder.deleteTool(key); + // // } + // // } + + // // console.log("local bindings", this.toolBinder.bindings); + // // } + // // } + // this.specificationChanged.dispatch(); + // }); this.tabs.add("rendering", { label: "Rendering", order: -100, @@ -627,7 +702,14 @@ export class AnnotationUserLayer extends Base { restoreState(specification: any) { console.log("restore state of annotation source"); // restore tags before super so tag tools are registered - this.tags.restoreState(specification[TAGS_JSON_KEY] || []); + // this.tags.restoreState(specification[TAGS_JSON_KEY] || []); + // for (const tag of this.tags.value) { + // this.registerDisposer( + // tag.changed.add(() => { + // this.tags.changed.dispatch(); + // }), + // ); + // } super.restoreState(specification); this.linkedSegmentationLayers.restoreState(specification); this.localAnnotationsJson = specification[ANNOTATIONS_JSON_KEY]; @@ -719,11 +801,13 @@ export class AnnotationUserLayer extends Base { activateDataSubsources(subsources: Iterable) { let hasLocalAnnotations = false; - let properties: AnnotationPropertySpec[] | undefined; + let properties: readonly AnnotationPropertySpec[] | undefined; for (const loadedSubsource of subsources) { const { subsourceEntry } = loadedSubsource; const { local } = subsourceEntry.subsource; - const setProperties = (newProperties: AnnotationPropertySpec[]) => { + const setProperties = ( + newProperties: readonly AnnotationPropertySpec[], + ) => { if ( properties !== undefined && stableStringify(newProperties) !== stableStringify(properties) @@ -749,7 +833,7 @@ export class AnnotationUserLayer extends Base { const localAnnotations = (this.localAnnotations = new LocalAnnotationSource( loadedSubsource.loadedDataSource.transform, - this.localAnnotationProperties ?? [], + new WatchableValue(this.localAnnotationProperties ?? []), this.localAnnotationRelationships, )); try { @@ -764,7 +848,7 @@ export class AnnotationUserLayer extends Base { refCounted.registerDisposer( this.localAnnotations.changed.add(() => { this.localAnnotationProperties = - this.localAnnotations?.properties; + this.localAnnotations?.properties.value; this.specificationChanged.dispatch(); }), ); @@ -801,7 +885,7 @@ export class AnnotationUserLayer extends Base { } const { annotation } = subsourceEntry.subsource; if (annotation !== undefined) { - if (!setProperties(annotation.properties)) continue; + if (!setProperties(annotation.properties.value)) continue; loadedSubsource.activate(() => { const state = new AnnotationLayerState({ localPosition: this.localPosition, @@ -916,9 +1000,10 @@ export class AnnotationUserLayer extends Base { x[SHADER_CONTROLS_JSON_KEY] = this.annotationDisplayState.shaderControls.toJSON(); Object.assign(x, this.linkedSegmentationLayers.toJSON()); - if (this.tabs.value?.length) { - x[TAGS_JSON_KEY] = this.tags.toJSON(); - } + // if (this.tags.value.length) { + // x[TAGS_JSON_KEY] = this.tags.value.map((x) => x.value); + // console.log("FOO", x[TAGS_JSON_KEY]); + // } return x; } diff --git a/src/layer/annotation/style.css b/src/layer/annotation/style.css index d51ecb30c..9cf2b1b94 100644 --- a/src/layer/annotation/style.css +++ b/src/layer/annotation/style.css @@ -50,11 +50,17 @@ color: #999; } +.neuroglancer-tag-list > div { + display: grid; + row-gap: 5px; +} + /* .neuroglancer-add-tag-control { display: flex; } */ -.neuroglancer-add-tag-control > input { +.neuroglancer-add-tag-control > input, +.neuroglancer-tag-list-entry > input { background-color: #151515; color: white; font-family: monospace; @@ -64,13 +70,15 @@ outline: 0px; } -.neuroglancer-tag-list > div { +/* .neuroglancer-tag-list > div { display: grid; - grid-template-columns: min-content auto; -} + grid-template-columns: min-content auto min-content; +} */ .neuroglancer-tag-list-entry { - display: contents; + display: grid; + grid-template-columns: min-content auto min-content; + align-items: center; white-space: nowrap; } diff --git a/src/ui/annotations.ts b/src/ui/annotations.ts index 7cdd47015..324b364f7 100644 --- a/src/ui/annotations.ts +++ b/src/ui/annotations.ts @@ -963,7 +963,9 @@ export class PlacePointTool extends PlaceAnnotationTool { relatedSegments: getSelectedAssociatedSegments(annotationLayer), point, type: AnnotationType.POINT, - properties: annotationLayer.source.properties.map((x) => x.default), + properties: annotationLayer.source.properties.value.map( + (x) => x.default, + ), }; const reference = annotationLayer.source.add( annotation, @@ -1115,7 +1117,7 @@ abstract class PlaceTwoCornerAnnotationTool extends TwoStepAnnotationTool { description: "", pointA: point, pointB: point, - properties: annotationLayer.source.properties.map((x) => x.default), + properties: annotationLayer.source.properties.value.map((x) => x.default), }; } @@ -1239,7 +1241,7 @@ class PlaceEllipsoidTool extends TwoStepAnnotationTool { segments: getSelectedAssociatedSegments(annotationLayer), center: point, radii: vec3.fromValues(0, 0, 0), - properties: annotationLayer.source.properties.map((x) => x.default), + properties: annotationLayer.source.properties.value.map((x) => x.default), }; } @@ -1658,7 +1660,7 @@ export function UserLayerWithAnnotationsMixin< new AnnotationPropertySerializer( rank, numGeometryBytes, - properties, + properties.value, ); const annotationIndex = state.annotationIndex!; const annotationCount = state.annotationCount!; @@ -1677,7 +1679,9 @@ export function UserLayerWithAnnotationsMixin< annotationIndex, annotationCount, isLittleEndian, - (annotation.properties = new Array(properties.length)), + (annotation.properties = new Array( + properties.value.length, + )), ); if (annotationLayer.source.hasNonSerializedProperties()) { statusText = "Loading..."; @@ -1775,7 +1779,10 @@ export function UserLayerWithAnnotationsMixin< positionGrid.appendChild(button); } - const { relationships, properties } = annotationLayer.source; + const { + relationships, + properties: { value: properties }, + } = annotationLayer.source; const sourceReadonly = annotationLayer.source.readonly; // Add the ID to the annotation details.