import type { ImageAnnotation, ImageAnnotationTarget } from '../model'; import type { AnnotoriousOpts } from '../AnnotoriousOpts'; import { createSpatialTree } from './spatialTree'; import { toSvelteStore } from './SvelteStore'; import { createViewportState, type Annotation, type AnnotatorState, type Filter, type HoverState, type SelectionState } from '@annotorious/core'; import { createHoverState, createSelectionState, createStore } from '@annotorious/core'; import type { ImageAnnotationStore, SvelteImageAnnotationStore, SvelteImageAnnotatorState } from './ImageAnnotationStore'; export type ImageAnnotatorState = AnnotatorState & { store: ImageAnnotationStore; selection: SelectionState; hover: HoverState; } export const createImageAnnotatorState = ( opts: AnnotoriousOpts ): ImageAnnotatorState => { const store = createStore(); const tree = createSpatialTree(); const selection = createSelectionState(store, opts.userSelectAction, opts.adapter); const hover = createHoverState(store); const viewport = createViewportState(); store.observe(({ changes }) => { tree.set((changes.created || []).map(a => a.target as ImageAnnotationTarget), false); (changes.deleted || []).forEach(a => tree.remove(a.target as ImageAnnotationTarget)); (changes.updated || []).forEach(({ oldValue, newValue }) => tree.update(oldValue.target, newValue.target)); }); const getAt = (x: number, y: number, filter?: Filter, buffer?: number): I | undefined => { const targets = tree.getAt(x, y, buffer); if (filter) { // Resolve annotations first, so we can filter const annotations = targets.map(t => store.getAnnotation(t.annotation)!) .filter(Boolean) .filter(filter); return annotations[0]; } else { const top = targets[0]; return top ? store.getAnnotation(top.annotation) : undefined; } } const getIntersecting = (x: number, y: number, width: number, height: number) => tree.getIntersecting(x, y, width, height) .map(target => store.getAnnotation(target.annotation) as I) .filter(Boolean); // Race conditions may have deleted annotations concurrently return { store: { ...store, getAt, getIntersecting }, selection, hover, viewport } as ImageAnnotatorState; } export const createSvelteImageAnnotatorState = ( opts: AnnotoriousOpts ): SvelteImageAnnotatorState => { const state = createImageAnnotatorState(opts); return { ...state, store: toSvelteStore(state.store) as SvelteImageAnnotationStore } }