diff --git a/package.json b/package.json index 13c581b9212f316d80c4a01c3a31c90993862d06..5c35fe2e5aa2479354f7132fd953002e02436a8b 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "@ngrx/effects": "^9.1.1", "@ngrx/store": "^9.1.1", "@types/node": "12.12.39", - "export-nehuba": "0.0.2", + "export-nehuba": "0.0.4", "hbp-connectivity-component": "^0.3.14", "zone.js": "^0.10.2" } diff --git a/src/res/ext/MNI152NehubaConfig.json b/src/res/ext/MNI152NehubaConfig.json index 20f685aeaf022076f14029c38f69ca59acf91330..e85f80081fef30d59777297eb7351f9143e0f155 100644 --- a/src/res/ext/MNI152NehubaConfig.json +++ b/src/res/ext/MNI152NehubaConfig.json @@ -23,7 +23,7 @@ "mesh": { "removeBasedOnNavigation": true, "flipRemovedOctant": true, - "surfaceParcellation": false + "surfaceParcellation": true }, "removePerspectiveSlicesBackground": { "mode": "<", diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts index f14ef99c49fe88828f14cfb3bb25c39dcc2f744b..6bccc7902dbddb39d02fc9f7919653128cc61c38 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts @@ -30,7 +30,7 @@ import { WidgetModule } from 'src/widget' import { NehubaModule } from './nehuba.module' import { CommonModule } from '@angular/common' import { IMPORT_NEHUBA_INJECT_TOKEN } from './nehubaViewer/nehubaViewer.component' -import { viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper' +import { viewerStateCustomLandmarkSelector, viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper' import { RenderViewOriginDatasetLabelPipe } from '../parcellationRegion/region.base' import { By } from '@angular/platform-browser' import { ARIA_LABELS } from 'common/constants' @@ -38,7 +38,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations' import { RegionAccordionTooltipTextPipe } from '../util' import { hot } from 'jasmine-marbles' import { HttpClientTestingModule } from '@angular/common/http/testing' -import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors' +import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors' import { PANELS } from 'src/services/state/ngViewerState/constants' import { RegionalFeaturesModule } from '../regionalFeatures' @@ -636,5 +636,66 @@ describe('> nehubaContainer.component.ts', () => { }) }) + + describe('> on userLandmarks change', () => { + const lm1 = { + id: 'test-1', + position: [0, 0, 0] + } + const lm2 = { + id: 'test-2', + position: [1, 1,1 ] + } + it('> calls nehubaViewer.updateUserLandmarks', () => { + const fixture = TestBed.createComponent(NehubaContainer) + + fixture.componentInstance.nehubaViewer = { + updateUserLandmarks: () => {} + } as any + + const updateUserLandmarksSpy = spyOn( + fixture.componentInstance.nehubaViewer, + 'updateUserLandmarks' + ) + + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [ + lm1, + lm2 + ]) + fixture.detectChanges() + expect( + updateUserLandmarksSpy + ).toHaveBeenCalledWith([ + lm1, lm2 + ]) + }) + + it('> calls togglecotantREmoval', () => { + + const fixture = TestBed.createComponent(NehubaContainer) + + fixture.componentInstance.nehubaContainerDirective = { + toggleOctantRemoval: () => {}, + clear: () => {} + } as any + + const toggleOctantRemovalSpy = spyOn( + fixture.componentInstance.nehubaContainerDirective, + 'toggleOctantRemoval' + ) + + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateCustomLandmarkSelector, [ + lm1, + lm2 + ]) + mockStore.overrideSelector(ngViewerSelectorOctantRemoval, true) + fixture.detectChanges() + expect( + toggleOctantRemovalSpy + ).toHaveBeenCalledWith(false) + }) + }) }) }) diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index a0ca5fae81db51d42565d081eb8f8a218efa4d4b..2f4a7cbea773884e7ff990da39c0b975f3a49406 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -189,6 +189,11 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { @Output() public nehubaViewerLoaded: EventEmitter<boolean> = new EventEmitter() + @Output() + public forceUI$: Observable<{ target: 'perspective:octantRemoval', mode: boolean, message?: string }> + + public disableOctantRemoval$: Observable<{ message?: string, mode: boolean }> + public handleViewerLoadedEvent(flag: boolean){ this.viewerLoaded = flag this.nehubaViewerLoaded.emit(flag) @@ -365,6 +370,30 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { distinctUntilChanged(), ) + /** + * in future, perhaps add other force UI optinos here + */ + this.forceUI$ = this.userLandmarks$.pipe( + map(lm => { + if (lm.length > 0) { + return { + target: 'perspective:octantRemoval', + mode: false, + message: `octant control disabled: showing landmarks.` + } + } else { + return { + target: 'perspective:octantRemoval', + mode: null + } + } + }) + ) + + this.disableOctantRemoval$ = this.forceUI$.pipe( + filter(({ target }) => target === 'perspective:octantRemoval'), + ) + this.sliceRenderEvent$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe( map(ev => ev as CustomEvent) ) @@ -554,6 +583,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { }), ) + /** + * TODO deprecate, but document the method + */ this.subscriptions.push( combineLatest( this.fetchedSpatialDatasets$, @@ -586,7 +618,16 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { ) this.subscriptions.push( - this.userLandmarks$.subscribe(landmarks => { + this.userLandmarks$.pipe( + withLatestFrom( + this.nehubaViewerPerspectiveOctantRemoval$ + ) + ).subscribe(([landmarks, flag]) => { + if (this.nehubaContainerDirective) { + this.nehubaContainerDirective.toggleOctantRemoval( + landmarks.length > 0 ? false : flag + ) + } if (this.nehubaViewer) { this.nehubaViewer.updateUserLandmarks(landmarks) } diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 8ced2dd4687ad88468f3a7e3103b4d907ae8bb1d..c926768ee61a7744aa90ef3734e3baa1a54ae3c7 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -673,7 +673,10 @@ <!-- perspective specific control --> <ng-container *ngIf="panelIndex === 3"> - <ng-container *ngTemplateOutlet="perspectiveOctantRemovalTmpl; context: { state: (nehubaViewerPerspectiveOctantRemoval$ | async) }"> + <ng-container *ngTemplateOutlet="perspectiveOctantRemovalTmpl; context: { + state: (nehubaViewerPerspectiveOctantRemoval$ | async), + disableOctantRemoval: disableOctantRemoval$ | async + }"> </ng-container> </ng-container> @@ -701,23 +704,27 @@ </ng-template> -<ng-template #perspectiveOctantRemovalTmpl let-state="state"> - <button - (click)="setOctantRemoval(!state)" - mat-icon-button - [attr.aria-label]="ARIA_LABEL_TOGGLE_FRONTAL_OCTANT" - color="primary"> +<ng-template #perspectiveOctantRemovalTmpl let-state="state" let-disableOctantRemoval="disableOctantRemoval"> + <div class="d-inline-block" + [matTooltip]="disableOctantRemoval?.mode !== null ? disableOctantRemoval.message : null"> + <button + (click)="setOctantRemoval(!state)" + mat-icon-button + [disabled]="disableOctantRemoval?.mode !== null" + [attr.aria-label]="ARIA_LABEL_TOGGLE_FRONTAL_OCTANT" + color="primary"> - <!-- octant removal is true --> - <ng-template [ngIf]="nehubaViewerPerspectiveOctantRemoval$ | async" [ngIfElse]="octantRemovalOffTmpl"> - <i class="fas fa-eye-slash"></i> - </ng-template> + <!-- octant removal is true --> + <ng-template [ngIf]="disableOctantRemoval?.mode !== null ? disableOctantRemoval.mode : state" [ngIfElse]="octantRemovalOffTmpl"> + <i class="fas fa-eye-slash"></i> + </ng-template> - <!-- octant removal is false --> - <ng-template #octantRemovalOffTmpl> - <i class="fas fa-eye"></i> - </ng-template> - </button> + <!-- octant removal is false --> + <ng-template #octantRemovalOffTmpl> + <i class="fas fa-eye"></i> + </ng-template> + </button> + </div> </ng-template> diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 8393b9980d03f4aae65fe64dd28961c23ea1dc74..d1edab5e0f9fa80aa6347906b43c5d4ccceef803 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -250,7 +250,19 @@ export class NehubaViewerUnit implements OnInit, OnDestroy { /** * url may be null if user removes all landmarks */ - if (!url) { return } + if (!url) { + /** + * remove transparency from meshes in current layer(s) + */ + for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) { + const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey) + if (layer) { + layer.layer.displayState.objectAlpha.restoreState(1.0) + } + } + + return + } const _ = {} _[NG_USER_LANDMARK_LAYER_NAME] = { type: 'mesh', @@ -258,6 +270,16 @@ export class NehubaViewerUnit implements OnInit, OnDestroy { shader: this.userLandmarkShader, } this.loadLayer(_) + + /** + * adding transparency to meshes in current layer(s) + */ + for (const layerKey of this.multiNgIdsLabelIndexMap.keys()) { + const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerKey) + if (layer) { + layer.layer.displayState.objectAlpha.restoreState(0.2) + } + } }), ) } diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts index cb3f929af8b4724dbbf97d1e649932a06a77bc15..f4a1973ae240f4235f441159dc0892456c381f2f 100644 --- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts +++ b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts @@ -313,11 +313,7 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ this.nehubaViewerPerspectiveOctantRemoval$.pipe( distinctUntilChanged() ).subscribe(flag =>{ - const showPerspectiveSliceViews = this.nehubaViewerInstance?.nehubaViewer?.ngviewer?.showPerspectiveSliceViews - if (showPerspectiveSliceViews) showPerspectiveSliceViews.restoreState(flag) - else { - this.log && this.log.warn(`showPerspectiveSliceViews not defined`) - } + this.toggleOctantRemoval(flag) }) ) @@ -374,6 +370,14 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ } } + public toggleOctantRemoval(flag: boolean){ + const showPerspectiveSliceViews = this.nehubaViewerInstance?.nehubaViewer?.ngviewer?.showPerspectiveSliceViews + if (showPerspectiveSliceViews) showPerspectiveSliceViews.restoreState(flag) + else { + this.log && this.log.warn(`showPerspectiveSliceViews not defined`) + } + } + createNehubaInstance(template: any, lifeCycle: INehubaLifecycleHook = {}){ this.clear() this.iavNehubaViewerContainerViewerLoading.emit(true) diff --git a/src/util/worker.js b/src/util/worker.js index a46c50fccc19223a9f2fadd1c317eddea9b2d141..2153104bd9142fe1d7a24523a9e2e70ef11999a5 100644 --- a/src/util/worker.js +++ b/src/util/worker.js @@ -91,8 +91,7 @@ const parseLmToVtk = (landmarks, scale) => { const reduce = landmarks.reduce((acc,curr,idx) => { //curr : null | [number,number,number] | [ [number,number,number], [number,number,number], [number,number,number] ][] - if(curr === null) - return acc + if(curr === null) return acc if(!isNaN(curr[0])) /** * point primitive, render icosahedron @@ -162,12 +161,10 @@ const getLandmarksVtk = (action) => { const vtk = parseLmToVtk(landmarks, scale) - if(!vtk) - return + if(!vtk) return // when new set of landmarks are to be displayed, the old landmarks will be discarded - if(landmarkVtkUrl) - URL.revokeObjectURL(landmarkVtkUrl) + if(landmarkVtkUrl) URL.revokeObjectURL(landmarkVtkUrl) landmarkVtkUrl = URL.createObjectURL(new Blob( [encoder.encode(vtk)], {type : 'application/octet-stream'} )) postMessage({ @@ -201,8 +198,7 @@ const getuserLandmarksVtk = (action) => { } const vtk = parseLmToVtk(landmarks, scale) - if(!vtk) - return + if(!vtk) return if(userLandmarkVtkUrl) URL.revokeObjectURL(userLandmarkVtkUrl)