diff --git a/src/api/service.ts b/src/api/service.ts index 49b22c9ec2dc5e59648dca26fcd13e21716cd490..6adceb099682923f5306647b857346dca23b1aa3 100644 --- a/src/api/service.ts +++ b/src/api/service.ts @@ -3,17 +3,30 @@ import { select, Store } from "@ngrx/store"; import { Subject } from "rxjs"; import { distinctUntilChanged, filter, map, switchMap, take } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi"; -import { SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate, Point } from "src/atlasComponents/sapi/sxplrTypes"; +import { Point } from "src/atlasComponents/sapi/sxplrTypes"; import { SxplrCoordinatePointExtension } from "src/atlasComponents/sapi/typeV3"; import { MainState, atlasSelection, userInteraction, annotation, atlasAppearance } from "src/state" import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util"; import { CANCELLABLE_DIALOG, CANCELLABLE_DIALOG_OPTS } from "src/util/interfaces"; import { Booth, BoothResponder, createBroadcastingJsonRpcChannel, JRPCRequest, JRPCResp } from "./jsonrpc" +import { translateV3Entities } from "src/atlasComponents/sapi/translateV3" +import { PathReturn } from "src/atlasComponents/sapi/typeV3" export type NAMESPACE_TYPE = "sxplr" export const namespace: NAMESPACE_TYPE = "sxplr" const nameSpaceRegex = new RegExp(`^${namespace}`) +/** + * plugin type + * previously, full json was used + * in future, sxplr jsons will be used + * but for backwards compatibility, for now, full json will be retrieved and returned + */ +type PTAtlas = PathReturn<"/atlases/{atlas_id}"> +type PTSpace = PathReturn<"/spaces/{space_id}"> +type PTParcellation = PathReturn<"/parcellations/{parcellation_id}"> +type PTRegion = PathReturn<"/regions/{region_id}"> + type AddableLayer = atlasAppearance.const.NgLayerCustomLayer type AtId = { @@ -21,7 +34,7 @@ type AtId = { } type RequestUserTypes = { - region: SxplrRegion + region: PTRegion point: Point confirm: void input: string @@ -39,15 +52,15 @@ type RequestUser<T extends keyof RequestUserTypes> = { export type ApiBoothEvents = { getAllAtlases: { request: null - response: SxplrAtlas[] + response: PTAtlas[] } getSupportedTemplates: { request: null - response: SxplrTemplate[] + response: PTSpace[] } getSupportedParcellations: { request: null - response: SxplrParcellation[] + response: PTParcellation[] } selectAtlas: { @@ -73,7 +86,7 @@ export type ApiBoothEvents = { type: 'region' | 'point' message: string } - response: SxplrRegion | Point + response: PTRegion | Point } addAnnotations: { @@ -136,11 +149,11 @@ export type HeartbeatEvents = { } export type BroadCastingApiEvents = { - atlasSelected: SxplrAtlas - templateSelected: SxplrTemplate - parcellationSelected: SxplrParcellation - allRegions: SxplrRegion[] - regionsSelected: SxplrRegion[] + atlasSelected: PTAtlas + templateSelected: PTSpace + parcellationSelected: PTParcellation + allRegions: PTRegion[] + regionsSelected: PTRegion[] navigation: MainState['[state.atlasSelection]']['navigation'] } @@ -188,13 +201,13 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ const { type } = this.requestUserQueue[0] if (type === "region") { - let moRegion: SxplrRegion + let moRegion: PTRegion this.store.pipe( select(userInteraction.selectors.mousingOverRegions), filter(val => val.length > 0), map(val => val[0]), take(1) - ).subscribe(region => moRegion = region) + ).subscribe(region => moRegion = translateV3Entities.retrieveRegion(region)) if (!!moRegion) { this.fulfillUserRequest(null, moRegion) return false @@ -250,27 +263,27 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ this.store.pipe( select(atlasSelection.selectors.selectedAtlas) ).subscribe(atlas => { - this.broadcastCh.emit('atlasSelected', atlas) + this.broadcastCh.emit('atlasSelected', translateV3Entities.retrieveAtlas(atlas)) }) this.store.pipe( select(atlasSelection.selectors.selectedParcellation) ).subscribe(parcellation => { - this.broadcastCh.emit('parcellationSelected', parcellation) + this.broadcastCh.emit('parcellationSelected', translateV3Entities.retrieveParcellation(parcellation)) }) this.store.pipe( select(atlasSelection.selectors.selectedTemplate) ).subscribe(template => { - this.broadcastCh.emit('templateSelected', template) + this.broadcastCh.emit('templateSelected', translateV3Entities.retrieveTemplate(template)) }) this.store.pipe( select(atlasSelection.selectors.selectedRegions) ).subscribe(regions => { - this.broadcastCh.emit('regionsSelected', regions) + this.broadcastCh.emit('regionsSelected', regions.map(reg => translateV3Entities.retrieveRegion(reg))) }) this.store.pipe( select(atlasSelection.selectors.selectedParcAllRegions) ).subscribe(regions => { - this.broadcastCh.emit('allRegions', regions) + this.broadcastCh.emit('allRegions', regions.map(reg => translateV3Entities.retrieveRegion(reg))) }) this.store.pipe( select(atlasSelection.selectors.navigation) @@ -296,7 +309,7 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ ).toPromise() return { id: event.id, - result: atlases, + result: atlases.map(atlas => translateV3Entities.retrieveAtlas(atlas)), jsonrpc: '2.0' } } @@ -310,7 +323,7 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ return { id: event.id, jsonrpc: '2.0', - result: parcs + result: parcs.map(parc => translateV3Entities.retrieveParcellation(parc)) } } case 'getSupportedTemplates': { @@ -323,7 +336,7 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ return { id: event.id, jsonrpc: '2.0', - result: spaces + result: spaces.map(spc =>translateV3Entities.retrieveTemplate(spc)) } } case 'selectAtlas': { diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts index 0bc24b95167e2ea75e17af99b52237f0120b88cc..15bfc561e3f86608e4b18f0de67970d1391d8af5 100644 --- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts +++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ComponentFactoryResolver, Inject, Injector, Input, OnDestroy, Optional, Pipe, PipeTransform, ViewChild, ViewContainerRef } from "@angular/core"; +import { AfterViewInit, Component, Injector, Input, OnDestroy, Pipe, PipeTransform, Type, ViewChild, ViewContainerRef } from "@angular/core"; import { IAnnotationGeometry, UDPATE_ANNOTATION_TOKEN } from "../tools/type"; import { Point } from '../tools/point' import { Polygon } from '../tools/poly' @@ -30,16 +30,13 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{ private chSubs: Subscription[] = [] private subs: Subscription[] = [] - public templateSpaces: { - ['@id']: string - }[] = [] ngOnChanges(){ while(this.chSubs.length > 0) this.chSubs.pop().unsubscribe() this.formGrp = new UntypedFormGroup({ name: new UntypedFormControl(this.managedAnnotation.name), spaceId: new UntypedFormControl({ - value: this.managedAnnotation.space["@id"], + value: this.managedAnnotation.space.id, disabled: true }), desc: new UntypedFormControl(this.managedAnnotation.desc), @@ -65,7 +62,6 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{ private store: Store<any>, private snackbar: MatSnackBar, private svc: ModularUserAnnotationToolService, - private cfr: ComponentFactoryResolver, private injector: Injector, ){ } @@ -79,7 +75,6 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{ }) throw new Error(`Edit component not found!`) } - const cf = this.cfr.resolveComponentFactory(editCmp) const injector = Injector.create({ providers: [{ @@ -88,7 +83,7 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{ }], parent: this.injector }) - this.editAnnotationVCR.createComponent(cf, null, injector) + this.editAnnotationVCR.createComponent(editCmp as Type<unknown>, {injector}) } } diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html index 0f35d07e6d352172a7790ad68662c8eb48aaccfe..c362a2edda62fe300e9b4d3d455ea065edd430d3 100644 --- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html +++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.template.html @@ -4,7 +4,7 @@ Space </mat-label> <mat-select formControlName="spaceId"> - <mat-option *ngFor="let tmpl of tmpls$ | async" [value]="tmpl['@id']"> + <mat-option *ngFor="let tmpl of tmpls$ | async" [value]="tmpl.id"> {{ tmpl.name }} </mat-option> </mat-select> diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts index 166efee58d09603f68f710bf2576b11c13943c5e..4a18bd4f3dc806ea1bef95f1f6339a17b63f6ead 100644 --- a/src/atlasComponents/userAnnotations/tools/service.ts +++ b/src/atlasComponents/userAnnotations/tools/service.ts @@ -1,9 +1,9 @@ -import { Injectable, OnDestroy } from "@angular/core"; +import { Injectable, OnDestroy, Type } from "@angular/core"; import { ARIA_LABELS } from 'common/constants' import { Inject, Optional } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, of, Subject, Subscription } from "rxjs"; -import {map, switchMap, filter, shareReplay, pairwise } from "rxjs/operators"; +import {map, switchMap, filter, shareReplay, pairwise, withLatestFrom } from "rxjs/operators"; import { NehubaViewerUnit } from "src/viewerModule/nehuba"; import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util"; import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TNgAnnotationLine, TCallback } from "./type"; @@ -17,18 +17,10 @@ import { actions } from "src/state/atlasSelection"; import { atlasSelection } from "src/state"; import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"; import { AnnotationLayer } from "src/atlasComponents/annotations"; +import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"; const LOCAL_STORAGE_KEY = 'userAnnotationKey' -const IAV_VOXEL_SIZES_NM = { - 'minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9': [25000, 25000, 25000], - 'minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8': [39062.5, 39062.5, 39062.5], - 'minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588': [21166.666015625, 20000, 21166.666015625], - 'minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992': [1000000, 1000000, 1000000,], - 'minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2': [1000000, 1000000, 1000000], - 'minds/core/referencespace/v1.0.0/MEBRAINS_T1.masked': [1000000, 1000000, 1000000] -} - type TAnnotationMetadata = { id: string name: string @@ -152,8 +144,8 @@ export class ModularUserAnnotationToolService implements OnDestroy{ name: string iconClass: string toolInstance: AbsToolClass<any> - target?: ClassInterface<IAnnotationGeometry> - editCmp?: ClassInterface<any> + target?: Type<IAnnotationGeometry> + editCmp?: Type<unknown> onDestoryCallBack: () => void }[] = [] private mousePosReal: [number, number, number] @@ -267,6 +259,19 @@ export class ModularUserAnnotationToolService implements OnDestroy{ } } + #voxelSize = this.store.pipe( + select(atlasSelection.selectors.selectedTemplate), + switchMap(tmpl => translateV3Entities.translateSpaceToVolumeImage(tmpl)), + map(volImages => { + if (volImages.length === 0) { + return null + } + const volImage = volImages[0] + const { real, voxel } = volImage.info + return [0, 1, 2].map(idx => real[idx]/voxel[idx]) as [number, number, number] + }) + ) + constructor( private store: Store<any>, private snackbar: MatSnackBar, @@ -451,15 +456,14 @@ export class ModularUserAnnotationToolService implements OnDestroy{ */ this.subscription.push( store.pipe( - select(atlasSelection.selectors.viewerMode) - ).subscribe(viewerMode => { + select(atlasSelection.selectors.viewerMode), + withLatestFrom(this.#voxelSize), + ).subscribe(([viewerMode, voxelSize]) => { this.currMode = viewerMode if (viewerMode === ModularUserAnnotationToolService.VIEWER_MODE) { if (this.annotationLayer) this.annotationLayer.setVisible(true) else { - const viewer = (window as any).viewer - const voxelSize = IAV_VOXEL_SIZES_NM[this.selectedTmpl["@id"]] - if (!voxelSize) throw new Error(`voxelSize of ${this.selectedTmpl["@id"]} cannot be found!`) + if (!voxelSize) throw new Error(`voxelSize of ${this.selectedTmpl.id} cannot be found!`) if (this.annotationLayer) { this.annotationLayer.dispose() } @@ -601,7 +605,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{ this.forcedAnnotationRefresh$.next(null) } - public getEditAnnotationCmp(annotation: IAnnotationGeometry): ClassInterface<any>{ + public getEditAnnotationCmp(annotation: IAnnotationGeometry): Type<unknown>{ const foundTool = this.registeredTools.find(t => t.target && annotation instanceof t.target) return foundTool && foundTool.editCmp } diff --git a/src/plugin/broadcast.md b/src/plugin/broadcast.md index bcc96cd4add3e291b62df5557c1afdc0f9a9501d..ac3d7b98487d2ecd192659463dea415537fdc166 100644 --- a/src/plugin/broadcast.md +++ b/src/plugin/broadcast.md @@ -18,7 +18,7 @@ Broadcasting messages never expects a response (and thus will never contain and - payload ```ts - SapiAtlasModel + PTAtlas ``` @@ -28,7 +28,7 @@ Broadcasting messages never expects a response (and thus will never contain and - payload ```ts - SapiSpaceModel + PTSpace ``` @@ -38,7 +38,7 @@ Broadcasting messages never expects a response (and thus will never contain and - payload ```ts - SapiParcellationModel + PTParcellation ``` @@ -48,7 +48,7 @@ Broadcasting messages never expects a response (and thus will never contain and - payload ```ts - SapiRegionModel[] + PTRegion[] ``` @@ -58,7 +58,17 @@ Broadcasting messages never expects a response (and thus will never contain and - payload ```ts - SapiRegionModel[] + PTRegion[] + ``` + + + +### `sxplr.on.navigation` + +- payload + + ```ts + MainState['[state.atlasSelection]']['navigation'] ``` diff --git a/src/plugin/plugin.module.ts b/src/plugin/plugin.module.ts index ea1ec730ae3867562e47a66399e7fd5ecc531f17..998ca4056ecbb7a069133b021bc3e0aab8605fdc 100644 --- a/src/plugin/plugin.module.ts +++ b/src/plugin/plugin.module.ts @@ -26,17 +26,6 @@ import { PluginPortal } from "./pluginPortal/pluginPortal.component"; exports: [ PluginBannerUI, ], - providers: [ - { - provide: APPEND_SCRIPT_TOKEN, - useFactory: appendScriptFactory, - deps: [ DOCUMENT ] - }, - { - provide: REMOVE_SCRIPT_TOKEN, - useFactory: removeScriptFactory, - deps: [ DOCUMENT ] - }, - ] + providers: [] }) export class PluginModule{} \ No newline at end of file diff --git a/src/plugin/request.md b/src/plugin/request.md index 47daf4525b3873b10f93a87d75464e0e8cb97f13..c979339a3d9f71fcb3f7b311eb792f8b6e95feca 100644 --- a/src/plugin/request.md +++ b/src/plugin/request.md @@ -49,7 +49,7 @@ window.addEventListener('pagehide', () => { - response ```ts - SapiAtlasModel[] + PTAtlas[] ``` @@ -64,7 +64,7 @@ window.addEventListener('pagehide', () => { - response ```ts - SapiSpaceModel[] + PTSpace[] ``` @@ -79,7 +79,7 @@ window.addEventListener('pagehide', () => { - response ```ts - SapiParcellationModel[] + PTParcellation[] ``` @@ -154,7 +154,7 @@ window.addEventListener('pagehide', () => { - response ```ts - SapiRegionModel | OpenMINDSCoordinatePoint + PTRegion | Point ``` diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts index 746a3363a857b01a3f5bd0f06def5c6f9145c834..e1806ae87856ee7dd6756f15e122ce54e3bc1409 100644 --- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts +++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts @@ -645,7 +645,7 @@ export class NehubaViewerUnit implements OnDestroy { position, positionReal, zoom, - } = newViewerState + } = newViewerState || {} if ( perspectiveZoom ) { this.nehubaViewer.ngviewer.perspectiveNavigationState.zoomFactor.restoreState(perspectiveZoom)