From e84b6a77fdc9eb305ecbfab0cc1e6edf7d39db33 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Wed, 7 Aug 2019 11:16:04 +0200 Subject: [PATCH] chore: layer browser should work properly --- src/res/ext/allenMouse.json | 2 +- src/res/ext/allenMouseNehubaConfig.json | 180 +++++++++++++++++- src/res/ext/bigbrain.json | 2 +- src/services/effect/effect.ts | 5 +- src/services/state/viewerState.store.ts | 26 ++- src/ui/layerbrowser/layerbrowser.component.ts | 49 +---- .../layerbrowser/layerbrowser.template.html | 2 +- .../nehubaContainer.component.ts | 10 +- .../nehubaViewer/nehubaViewer.component.ts | 9 +- src/util/pipes/filterNgLayer.pipe.ts | 5 + 10 files changed, 237 insertions(+), 53 deletions(-) diff --git a/src/res/ext/allenMouse.json b/src/res/ext/allenMouse.json index a3e9d578d..713844ea3 100644 --- a/src/res/ext/allenMouse.json +++ b/src/res/ext/allenMouse.json @@ -2,7 +2,7 @@ "name": "Allen adult mouse brain reference atlas V3", "type": "template", "species": "Mouse", - "ngId": "", + "ngId": "stpt", "useTheme": "dark", "nehubaConfigURL": "nehubaConfig/allenMouseNehubaConfig", "parcellations": [ diff --git a/src/res/ext/allenMouseNehubaConfig.json b/src/res/ext/allenMouseNehubaConfig.json index 186836a02..db59ce9f9 100644 --- a/src/res/ext/allenMouseNehubaConfig.json +++ b/src/res/ext/allenMouseNehubaConfig.json @@ -1 +1,179 @@ -{"globals":{"hideNullImageValues":true,"useNehubaLayout":true,"useNehubaMeshLayer":true,"useCustomSegmentColors":true},"zoomWithoutCtrl":true,"hideNeuroglancerUI":true,"rightClickWithCtrl":true,"rotateAtViewCentre":true,"enableMeshLoadingControl":true,"zoomAtViewCentre":true,"restrictUserNavigation":true,"dataset":{"imageBackground":[0,0,0,0],"initialNgState":{"showDefaultAnnotations":false,"layers":{"stpt":{"type":"image","source":"precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/templates/v3/stpt","transform":[[1,0,0,-5737500],[0,1,0,-6637500],[0,0,1,-4037500],[0,0,0,1]]},"nissl":{"type":"image","source":"precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/templates/v3/nissl","transform":[[1,0,0,-5737500],[0,1,0,-6637500],[0,0,1,-4037500],[0,0,0,1]],"visible":false},"atlas":{"type":"segmentation","source":"precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/parcellations/v3_reduced","transform":[[1,0,0,-5737500],[0,1,0,-6637500],[0,0,1,-4037500],[0,0,0,1]]}},"navigation":{"pose":{"position":{"voxelSize":[25000,25000,25000],"voxelCoordinates":[5.0738067626953,-3.9425048828125,-2.8185577392578]}},"zoomFactor":38990.5839902911},"perspectiveOrientation":[0.2650355100631714,0.7529539465904236,-0.5376962423324585,-0.27147674560546875],"perspectiveZoom":159399.12281678425}},"layout":{"useNehubaPerspective":{"fixedZoomPerspectiveSlices":{"sliceViewportWidth":400,"sliceViewportHeight":400,"sliceZoom":41075.159536383755,"sliceViewportSizeMultiplier":2},"removePerspectiveSlicesBackground":{"color":[0.01,0.01,0.01,0.01],"mode":"<="},"centerToOrigin":true,"mesh":{"removeBasedOnNavigation":true,"flipRemovedOctant":true},"drawSubstrates":{"color":[0,0,0.5,0.2]},"restrictZoomLevel":{"maxZoom":364235.1741652613,"minZoom":89881.68746896513}}}} \ No newline at end of file +{ + "globals": { + "hideNullImageValues": true, + "useNehubaLayout": true, + "useNehubaMeshLayer": true, + "useCustomSegmentColors": true + }, + "zoomWithoutCtrl": true, + "hideNeuroglancerUI": true, + "rightClickWithCtrl": true, + "rotateAtViewCentre": true, + "enableMeshLoadingControl": true, + "zoomAtViewCentre": true, + "restrictUserNavigation": true, + "dataset": { + "imageBackground": [ + 0, + 0, + 0, + 0 + ], + "initialNgState": { + "showDefaultAnnotations": false, + "layers": { + "stpt": { + "type": "image", + "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/templates/v3/stpt", + "transform": [ + [ + 1, + 0, + 0, + -5737500 + ], + [ + 0, + 1, + 0, + -6637500 + ], + [ + 0, + 0, + 1, + -4037500 + ], + [ + 0, + 0, + 0, + 1 + ] + ] + }, + "nissl": { + "type": "image", + "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/templates/v3/nissl", + "transform": [ + [ + 1, + 0, + 0, + -5737500 + ], + [ + 0, + 1, + 0, + -6637500 + ], + [ + 0, + 0, + 1, + -4037500 + ], + [ + 0, + 0, + 0, + 1 + ] + ], + "visible": false + }, + "atlas": { + "type": "segmentation", + "source": "precomputed://https://neuroglancer.humanbrainproject.org/precomputed/AMBA/parcellations/v3_reduced", + "transform": [ + [ + 1, + 0, + 0, + -5737500 + ], + [ + 0, + 1, + 0, + -6637500 + ], + [ + 0, + 0, + 1, + -4037500 + ], + [ + 0, + 0, + 0, + 1 + ] + ] + } + }, + "navigation": { + "pose": { + "position": { + "voxelSize": [ + 25000, + 25000, + 25000 + ], + "voxelCoordinates": [ + 5.0738067626953, + -3.9425048828125, + -2.8185577392578 + ] + } + }, + "zoomFactor": 38990.5839902911 + }, + "perspectiveOrientation": [ + 0.2650355100631714, + 0.7529539465904236, + -0.5376962423324585, + -0.27147674560546875 + ], + "perspectiveZoom": 159399.12281678425 + } + }, + "layout": { + "useNehubaPerspective": { + "fixedZoomPerspectiveSlices": { + "sliceViewportWidth": 400, + "sliceViewportHeight": 400, + "sliceZoom": 41075.159536383755, + "sliceViewportSizeMultiplier": 2 + }, + "removePerspectiveSlicesBackground": { + "color": [ + 0.01, + 0.01, + 0.01, + 0.01 + ], + "mode": "<=" + }, + "centerToOrigin": true, + "mesh": { + "removeBasedOnNavigation": true, + "flipRemovedOctant": true + }, + "drawSubstrates": { + "color": [ + 0, + 0, + 0.5, + 0.2 + ] + }, + "restrictZoomLevel": { + "maxZoom": 364235.1741652613, + "minZoom": 89881.68746896513 + } + } + } +} \ No newline at end of file diff --git a/src/res/ext/bigbrain.json b/src/res/ext/bigbrain.json index 0ba32ab3a..6ceacae84 100644 --- a/src/res/ext/bigbrain.json +++ b/src/res/ext/bigbrain.json @@ -3,7 +3,7 @@ "type": "template", "species": "Human", "useTheme": "light", - "nehubaId": " grey value: ", + "ngId": " grey value: ", "nehubaConfigURL": "nehubaConfig/bigbrainNehubaConfig", "parcellations": [ { diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts index c8c7340f1..9a1a06769 100644 --- a/src/services/effect/effect.ts +++ b/src/services/effect/effect.ts @@ -1,8 +1,7 @@ import { Injectable, OnDestroy } from "@angular/core"; import { Effect, Actions, ofType } from "@ngrx/effects"; -import { Observable, Subscription, merge, fromEvent, combineLatest } from "rxjs"; -import { SHOW_KG_TOS } from "../state/uiState.store"; -import { withLatestFrom, map, tap, switchMap, filter } from "rxjs/operators"; +import { Subscription, merge, fromEvent } from "rxjs"; +import { withLatestFrom, map, filter } from "rxjs/operators"; import { Store, select } from "@ngrx/store"; import { SELECT_PARCELLATION, SELECT_REGIONS, NEWVIEWER, UPDATE_PARCELLATION, SELECT_REGIONS_WITH_ID } from "../state/viewerState.store"; import { worker } from 'src/atlasViewer/atlasViewer.workerService.service' diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts index 57ea4802e..0b1edc38b 100644 --- a/src/services/state/viewerState.store.ts +++ b/src/services/state/viewerState.store.ts @@ -1,5 +1,6 @@ import { Action } from '@ngrx/store' import { UserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service'; +import { NgLayerInterface } from 'src/atlasViewer/atlasViewer.component'; export interface ViewerStateInterface{ fetchedTemplates : any[] @@ -13,6 +14,8 @@ export interface ViewerStateInterface{ navigation : any | null dedicatedView : string[] + + loadedNgLayers: NgLayerInterface[] } export interface AtlasAction extends Action{ @@ -36,7 +39,8 @@ export interface AtlasAction extends Action{ export function viewerState( state:Partial<ViewerStateInterface> = { landmarksSelected : [], - fetchedTemplates : [] + fetchedTemplates : [], + loadedNgLayers: [] }, action:AtlasAction ){ @@ -129,6 +133,24 @@ export function viewerState( userLandmarks: action.landmarks } } + case NEHUBA_LAYER_CHANGED: { + if (!window['viewer']) { + return { + ...state, + loadedNgLayers: [] + } + } else { + return { + ...state, + loadedNgLayers: (window['viewer'].layerManager.managedLayers as any[]).map(obj => ({ + name : obj.name, + type : obj.initialSpecification.type, + source : obj.sourceUrl, + visible : obj.visible + }) as NgLayerInterface) + } + } + } default : return state } @@ -150,3 +172,5 @@ export const SELECT_REGIONS_WITH_ID = `SELECT_REGIONS_WITH_ID` export const SELECT_LANDMARKS = `SELECT_LANDMARKS` export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS` export const USER_LANDMARKS = `USER_LANDMARKS` + +export const NEHUBA_LAYER_CHANGED = `NEHUBA_LAYER_CHANGED` diff --git a/src/ui/layerbrowser/layerbrowser.component.ts b/src/ui/layerbrowser/layerbrowser.component.ts index ca537076a..18c2cb75b 100644 --- a/src/ui/layerbrowser/layerbrowser.component.ts +++ b/src/ui/layerbrowser/layerbrowser.component.ts @@ -3,7 +3,7 @@ import { NgLayerInterface } from "../../atlasViewer/atlasViewer.component"; import { Store, select } from "@ngrx/store"; import { ViewerStateInterface, isDefined, REMOVE_NG_LAYER, FORCE_SHOW_SEGMENT, safeFilter, getNgIds } from "../../services/stateStore.service"; import { Subscription, Observable } from "rxjs"; -import { filter, distinctUntilChanged, map, delay, buffer } from "rxjs/operators"; +import { filter, map } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; @Component({ @@ -20,7 +20,7 @@ export class LayerBrowser implements OnDestroy{ /** * TODO make untangle nglayernames and its dependency on ng */ - ngLayers : NgLayerInterface[] = [] + loadedNgLayers$: Observable<NgLayerInterface[]> lockedLayers : string[] = [] public forceShowSegmentCurrentState : boolean | null = null @@ -80,36 +80,9 @@ export class LayerBrowser implements OnDestroy{ map(state => state.forceShowSegment) ) - - /** - * TODO leakage? after change of template still hanging the reference? - */ - this.subscriptions.push( - this.store.pipe( - select('viewerState'), - select('templateSelected'), - distinctUntilChanged((o,n) => o.templateSelected.name === n.templateSelected.name), - filter(templateSelected => !!templateSelected), - map(templateSelected => Object.keys(templateSelected.nehubaConfig.dataset.initialNgState.layers)), - buffer(this.store.pipe( - select('ngViewerState'), - select('nehubaReady'), - filter(flag => flag) - )), - delay(0), - map(arr => arr[arr.length - 1]) - ).subscribe((lockedLayerNames:string[]) => { - /** - * TODO - * if layerbrowser is init before nehuba - * window['viewer'] will return undefined - */ - this.lockedLayers = lockedLayerNames - - this.ngLayersChangeHandler() - this.disposeHandler = window['viewer'].layerManager.layersChanged.add(() => this.ngLayersChangeHandler()) - window['viewer'].registerDisposer(this.disposeHandler) - }) + this.loadedNgLayers$ = this.store.pipe( + select('viewerState'), + select('loadedNgLayers') ) this.subscriptions.push( @@ -121,15 +94,6 @@ export class LayerBrowser implements OnDestroy{ this.subscriptions.forEach(s => s.unsubscribe()) } - ngLayersChangeHandler(){ - this.ngLayers = (window['viewer'].layerManager.managedLayers as any[]).map(obj => ({ - name : obj.name, - type : obj.initialSpecification.type, - source : obj.sourceUrl, - visible : obj.visible - }) as NgLayerInterface) - } - public classVisible(layer:any):boolean{ return typeof layer.visible === 'undefined' ? true @@ -140,8 +104,9 @@ export class LayerBrowser implements OnDestroy{ if(!this.lockedLayers){ /* locked layer undefined. always return false */ return false - }else + }else{ return this.lockedLayers.findIndex(l => l === ngLayer.name) >= 0 + } } toggleVisibility(layer:any){ diff --git a/src/ui/layerbrowser/layerbrowser.template.html b/src/ui/layerbrowser/layerbrowser.template.html index e307ecb73..6ac015c16 100644 --- a/src/ui/layerbrowser/layerbrowser.template.html +++ b/src/ui/layerbrowser/layerbrowser.template.html @@ -1,4 +1,4 @@ -<ng-container *ngIf="ngLayers$ | async | filterNgLayer : ngLayers as filteredNgLayers; else noLayerPlaceHolder"> +<ng-container *ngIf="ngLayers$ | async | filterNgLayer : (loadedNgLayers$ | async) as filteredNgLayers; else noLayerPlaceHolder"> <ng-container *ngIf="filteredNgLayers.length > 0; else noLayerPlaceHolder"> <div class="layerContainer overflow-hidden" diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index b002afb11..44dfc7159 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -11,7 +11,7 @@ import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; import { pipeFromArray } from "rxjs/internal/util/pipe"; import { NEHUBA_READY } from "src/services/state/ngViewerState.store"; import { MOUSE_OVER_SEGMENTS } from "src/services/state/uiState.store"; -import { SELECT_REGIONS_WITH_ID } from "src/services/state/viewerState.store"; +import { SELECT_REGIONS_WITH_ID, NEHUBA_LAYER_CHANGED } from "src/services/state/viewerState.store"; const getProxyUrl = (ngUrl) => `nifti://${BACKEND_URL}preview/file?fileUrl=${encodeURIComponent(ngUrl.replace(/^nifti:\/\//,''))}` const getProxyOther = ({source}) => /AUTH_227176556f3c4bb38df9feea4b91200c/.test(source) @@ -827,6 +827,14 @@ export class NehubaContainer implements OnInit, OnDestroy{ this.nehubaViewer.debouncedViewerPositionChange.subscribe(this.handleEmittedNavigationChange.bind(this)) ) + this.nehubaViewerSubscriptions.push( + this.nehubaViewer.layersChanged.subscribe(() => { + this.store.dispatch({ + type: NEHUBA_LAYER_CHANGED + }) + }) + ) + this.nehubaViewerSubscriptions.push( /** * TODO when user selects new template, window.viewer diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index b0dbdd4c1..dfabd435b 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, Output, EventEmitter, ElementRef, NgZone, Renderer2 } from "@angular/core"; import 'third_party/export_nehuba/main.bundle.js' import 'third_party/export_nehuba/chunk_worker.bundle.js' -import { fromEvent, interval, Observable } from 'rxjs' +import { fromEvent, interval, Observable, Subscription } from 'rxjs' import { AtlasWorkerService } from "../../../atlasViewer/atlasViewer.workerService.service"; import { buffer, map, filter, debounceTime, take, takeUntil, scan, switchMap, takeWhile } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "../../../atlasViewer/atlasViewer.constantService.service"; @@ -23,6 +23,8 @@ import { getNgIdLabelIndexFromId } from "src/services/stateStore.service"; export class NehubaViewerUnit implements OnDestroy{ @Output() nehubaReady: EventEmitter<null> = new EventEmitter() + @Output() layersChanged: EventEmitter<null> = new EventEmitter() + private layersChangedHandler: any @Output() debouncedViewerPositionChange : EventEmitter<any> = new EventEmitter() @Output() mouseoverSegmentEmitter: EventEmitter<{ @@ -73,7 +75,7 @@ export class NehubaViewerUnit implements OnDestroy{ this._s9$ ] - ondestroySubscriptions: any[] = [] + ondestroySubscriptions: Subscription[] = [] touchStart$ : Observable<any> @@ -105,6 +107,9 @@ export class NehubaViewerUnit implements OnDestroy{ } this.patchNG() this.loadNehuba() + + this.layersChangedHandler = this.nehubaViewer.ngviewer.layerManager.layersChanged.add(() => this.layersChanged.emit(null)) + this.nehubaViewer.ngviewer.registerDisposer(this.layersChangedHandler) }) .catch(e => this.errorEmitter.emit(e)) diff --git a/src/util/pipes/filterNgLayer.pipe.ts b/src/util/pipes/filterNgLayer.pipe.ts index ab14b3ab8..798075159 100644 --- a/src/util/pipes/filterNgLayer.pipe.ts +++ b/src/util/pipes/filterNgLayer.pipe.ts @@ -1,6 +1,11 @@ import { Pipe, PipeTransform } from "@angular/core"; import { NgLayerInterface } from "src/atlasViewer/atlasViewer.component"; +/** + * TODO deprecate + * use regular pipe to achieve the same effect + */ + @Pipe({ name: 'filterNgLayer' }) -- GitLab