diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index 18602a9e6e5f5ff5fed3f5877fb3586cac054c41..cc8e857a378d6b1c2a000a041f8055e8be66f0f8 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -56,17 +56,19 @@ type TIAVAtlas = { } & THasId)[] } & THasId -type NgLayerObj = { - [key: string]: { - [key: string]: { - [key: string]: { - source: string - transform: number[][] - type: 'segmentation' | 'image' - } - } - } -} +type TNehubaConfig = Record<string, { + source: string + transform: number[][] + type: 'segmentation' | 'image' +}> + +type TViewerConfig = TNehubaConfig + +/** + * key value pair of + * atlasId -> templateId -> viewerConfig + */ +type TAtlasTmplViewerConfig = Record<string, Record<string, TViewerConfig>> export const spaceMiscInfoMap = new Map([ ['minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588', { @@ -527,11 +529,10 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" shareReplay(1) ) - //ToDo improve the readability - private ngLayerObj: NgLayerObj = {} + private atlasTmplConfig: TAtlasTmplViewerConfig = {} - getNehubaConfigFromAtlasTmplIds(atlasId: string, templateId: string) { - const atlasLayers = this.ngLayerObj[atlasId] + async getViewerConfig(atlasId: string, templateId: string, parcId: string) { + const atlasLayers = this.atlasTmplConfig[atlasId] const templateLayers = atlasLayers && atlasLayers[templateId] return templateLayers || {} } @@ -541,7 +542,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" return forkJoin( atlases.map(atlas => this.getSpacesAndParc(atlas['@id']).pipe( switchMap(({ templateSpaces, parcellations }) => { - this.ngLayerObj[atlas["@id"]] = {} + this.atlasTmplConfig[atlas["@id"]] = {} return forkJoin( templateSpaces.map( tmpl => { @@ -556,7 +557,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" name: 'Julich-Brain Probabilistic Cytoarchitectonic Maps (v2.9)' }) } - this.ngLayerObj[atlas["@id"]][tmpl.id] = {} + this.atlasTmplConfig[atlas["@id"]][tmpl.id] = {} return tmpl.availableParcellations.map( parc => this.getRegions(atlas['@id'], parc.id, tmpl.id).pipe( tap(regions => { @@ -579,7 +580,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" const ngId = getNgId(atlas['@id'], tmpl.id, parc.id, dedicatedMap[0]['@id']) region['ngId'] = ngId region['labelIndex'] = dedicatedMap[0].detail['neuroglancer/precomputed'].labelIndex - this.ngLayerObj[atlas["@id"]][tmpl.id][ngId] = { + this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngId] = { source: `precomputed://${dedicatedMap[0].url}`, type: "segmentation", transform: dedicatedMap[0].detail['neuroglancer/precomputed'].transform @@ -643,7 +644,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" const key = 'whole brain' const ngIdKey = getNgId(atlas['@id'], tmpl.id, parseId(parc.id), key) - this.ngLayerObj[atlas["@id"]][tmpl.id][ngIdKey] = { + this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngIdKey] = { source: `precomputed://${vol.url}`, type: "segmentation", transform: vol.detail['neuroglancer/precomputed'].transform @@ -660,7 +661,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" }] for (const { key, mapIndex } of mapIndexKey) { const ngIdKey = getNgId(atlas['@id'], tmpl.id, parseId(parc.id), key) - this.ngLayerObj[atlas["@id"]][tmpl.id][ngIdKey] = { + this.atlasTmplConfig[atlas["@id"]][tmpl.id][ngIdKey] = { source: `precomputed://${precomputedVols[mapIndex].url}`, type: "segmentation", transform: precomputedVols[mapIndex].detail['neuroglancer/precomputed'].transform @@ -681,7 +682,7 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" } ).reduce(flattenReducer, []) ).pipe( - mapTo({ templateSpaces, parcellations, ngLayerObj: this.ngLayerObj }) + mapTo({ templateSpaces, parcellations, ngLayerObj: this.atlasTmplConfig }) ) }), map(({ templateSpaces, parcellations, ngLayerObj }) => { diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts index 3d3df1491bad099d2ae5231b095199f4d4dae64f..c1ccd10ead072ec2f35685a7350047d889ec6311 100644 --- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts +++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.spec.ts @@ -20,12 +20,31 @@ describe('> viewerCtrlCmp.component.ts', () => { let fixture: ComponentFixture<ViewerCtrlCmp> let loader: HarnessLoader let mockStore: MockStore + let mockNehubaViewer = { - updateUserLandmarks: jasmine.createSpy() + updateUserLandmarks: jasmine.createSpy(), + nehubaViewer: { + ngviewer: { + layerManager: { + getLayerByName: jasmine.createSpy('getLayerByName'), + get managedLayers() { + return [] + }, + set managedLayers(val) { + return + } + }, + display: { + scheduleRedraw: jasmine.createSpy('scheduleRedraw') + } + } + } } afterEach(() => { mockNehubaViewer.updateUserLandmarks.calls.reset() + mockNehubaViewer.nehubaViewer.ngviewer.layerManager.getLayerByName.calls.reset() + mockNehubaViewer.nehubaViewer.ngviewer.display.scheduleRedraw.calls.reset() }) beforeEach( async () => { @@ -43,12 +62,16 @@ describe('> viewerCtrlCmp.component.ts', () => { provideMockStore(), { provide: NEHUBA_INSTANCE_INJTKN, - useValue: new BehaviorSubject(mockNehubaViewer) + useFactory: () => { + return new BehaviorSubject(mockNehubaViewer).asObservable() + } }, { provide: PureContantService, - useValue: { - backendUrl: `http://localhost:3000/` + useFactory: () => { + return { + getViewerConfig: jasmine.createSpy('getViewerConfig') + } } } ] @@ -213,5 +236,119 @@ describe('> viewerCtrlCmp.component.ts', () => { ) }) }) + + describe('> flagDelin', () => { + let toggleParcVsblSpy: jasmine.Spy + beforeEach(() => { + fixture = TestBed.createComponent(ViewerCtrlCmp) + toggleParcVsblSpy = spyOn(fixture.componentInstance as any, 'toggleParcVsbl') + fixture.detectChanges() + }) + it('> calls toggleParcVsbl', () => { + toggleParcVsblSpy.and.callFake(() => {}) + fixture.componentInstance.flagDelin = false + expect(toggleParcVsblSpy).toHaveBeenCalled() + }) + }) + describe('> toggleParcVsbl', () => { + let getViewerConfigSpy: jasmine.Spy + let getLayerByNameSpy: jasmine.Spy + beforeEach(() => { + const pureCstSvc = TestBed.inject(PureContantService) + getLayerByNameSpy = mockNehubaViewer.nehubaViewer.ngviewer.layerManager.getLayerByName + getViewerConfigSpy = pureCstSvc.getViewerConfig as jasmine.Spy + fixture = TestBed.createComponent(ViewerCtrlCmp) + fixture.detectChanges() + }) + + it('> calls pureSvc.getViewerConfig', async () => { + getViewerConfigSpy.and.returnValue({}) + await fixture.componentInstance['toggleParcVsbl']() + expect(getViewerConfigSpy).toHaveBeenCalled() + }) + + describe('> if _flagDelin is true', () => { + beforeEach(() => { + fixture.componentInstance['_flagDelin'] = true + fixture.componentInstance['hiddenLayerNames'] = [ + 'foo', + 'bar', + 'baz' + ] + }) + it('> go through all hideen layer names and set them to true', async () => { + const setVisibleSpy = jasmine.createSpy('setVisible') + getLayerByNameSpy.and.returnValue({ + setVisible: setVisibleSpy + }) + await fixture.componentInstance['toggleParcVsbl']() + expect(getLayerByNameSpy).toHaveBeenCalledTimes(3) + for (const arg of ['foo', 'bar', 'baz']) { + expect(getLayerByNameSpy).toHaveBeenCalledWith(arg) + } + expect(setVisibleSpy).toHaveBeenCalledTimes(3) + expect(setVisibleSpy).toHaveBeenCalledWith(true) + expect(setVisibleSpy).not.toHaveBeenCalledWith(false) + }) + it('> hiddenLayerNames resets', async () => { + await fixture.componentInstance['toggleParcVsbl']() + expect(fixture.componentInstance['hiddenLayerNames']).toEqual([]) + }) + }) + + describe('> if _flagDelin is false', () => { + let managedLayerSpyProp: jasmine.Spy + let setVisibleSpy: jasmine.Spy + beforeEach(() => { + fixture.componentInstance['_flagDelin'] = false + setVisibleSpy = jasmine.createSpy('setVisible') + getLayerByNameSpy.and.returnValue({ + setVisible: setVisibleSpy + }) + getViewerConfigSpy.and.resolveTo({ + 'foo': {}, + 'bar': {}, + 'baz': {} + }) + managedLayerSpyProp = spyOnProperty(mockNehubaViewer.nehubaViewer.ngviewer.layerManager, 'managedLayers') + managedLayerSpyProp.and.returnValue([{ + visible: true, + name: 'foo' + }, { + visible: false, + name: 'bar' + }, { + visible: true, + name: 'baz' + }]) + }) + + afterEach(() => { + managedLayerSpyProp.calls.reset() + }) + + it('> calls schedulRedraw', async () => { + await fixture.componentInstance['toggleParcVsbl']() + expect(mockNehubaViewer.nehubaViewer.ngviewer.display.scheduleRedraw).toHaveBeenCalled() + }) + + it('> only calls setVisible false on visible layers', async () => { + await fixture.componentInstance['toggleParcVsbl']() + expect(getLayerByNameSpy).toHaveBeenCalledTimes(2) + + for (const arg of ['foo', 'baz']) { + expect(getLayerByNameSpy).toHaveBeenCalledWith(arg) + } + expect(setVisibleSpy).toHaveBeenCalledTimes(2) + expect(setVisibleSpy).toHaveBeenCalledWith(false) + expect(setVisibleSpy).not.toHaveBeenCalledWith(true) + }) + + it('> sets hiddenLayerNames correctly', async () => { + await fixture.componentInstance['toggleParcVsbl']() + expect(fixture.componentInstance['hiddenLayerNames']).toEqual(['foo', 'baz']) + }) + }) + }) }) }) diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts index 64ba0b4122331d384ca6684a0b0cb862d940f053..dad94866475668c3e8677a37d9e7fd5e2b36e87a 100644 --- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts +++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.component.ts @@ -72,6 +72,12 @@ export class ViewerCtrlCmp{ select(selectorAuxMeshes), ) + private nehubaInst: NehubaViewerUnit + + get ngViewer() { + return this.nehubaInst?.nehubaViewer.ngviewer || (window as any).viewer + } + constructor( private store$: Store<any>, formBuilder: FormBuilder, @@ -88,11 +94,12 @@ export class ViewerCtrlCmp{ this.customLandmarks$, this.nehubaInst$, ]).pipe( - filter(([_, neubaInst]) => !!neubaInst), + filter(([_, nehubaInst]) => !!nehubaInst), ).subscribe(([landmarks, nehubainst]) => { this.setOctantRemoval(landmarks.length === 0) nehubainst.updateUserLandmarks(landmarks) - }) + }), + this.nehubaInst$.subscribe(nehubaInst => this.nehubaInst = nehubaInst) ) } else { console.warn(`NEHUBA_INSTANCE_INJTKN not provided`) @@ -161,32 +168,32 @@ export class ViewerCtrlCmp{ ) } - private toggleParcVsbl(){ - const templateLayers = this.pureConstantService.getNehubaConfigFromAtlasTmplIds(this.selectedAtlasId, this.selectedTemplateId) - const visibleParcLayers = templateLayers? - ((window as any).viewer.layerManager.managedLayers) - .filter(({ visible }) => visible) - .filter(l => Object.keys(templateLayers).includes(l.name)) - .filter(layer => !this.auxMeshesNamesSet.has(layer.name)) - : [] + private async toggleParcVsbl(){ + const viewerConfig = await this.pureConstantService.getViewerConfig(this.selectedAtlasId, this.selectedTemplateId, null) if (this.flagDelin) { for (const name of this.hiddenLayerNames) { - const l = (window as any).viewer.layerManager.getLayerByName(name) + const l = this.ngViewer.layerManager.getLayerByName(name) l && l.setVisible(true) } this.hiddenLayerNames = [] } else { this.hiddenLayerNames = [] - for (const { name } of visibleParcLayers) { - const l = (window as any).viewer.layerManager.getLayerByName(name) + const segLayerNames: string[] = [] + for (const layer of this.ngViewer.layerManager.managedLayers) { + if (layer.visible && layer.name in viewerConfig) { + segLayerNames.push(layer.name) + } + } + for (const name of segLayerNames) { + const l = this.ngViewer.layerManager.getLayerByName(name) l && l.setVisible(false) this.hiddenLayerNames.push( name ) } } - - setTimeout(() => { - (window as any).viewer.display.scheduleRedraw() + + requestAnimationFrame(() => { + this.ngViewer.display.scheduleRedraw() }) }