diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 392e96571a4090735d8407bf9a93592d0344d34b..0257abdbb7f26fc10a58b7ce42f19c7cd719c4eb 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -11,3 +11,4 @@ </style> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.2/dist/connectivity-component/connectivity-component.js" defer></script> +<link rel="stylesheet" href="icons/iav-icons.css"> diff --git a/.storybook/preview.js b/.storybook/preview.js index 957e7cbef107fa2c3191e57245241fb901983f17..52750e8cde9405a8fc3282ed73730bc13101605d 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -4,6 +4,14 @@ setCompodocJson(docJson); import 'src/theme.scss' +/** + * load custom icons + */ +import '!!file-loader?context=src/res&name=icons/iav-icons.css!src/res/icons/iav-icons.css' +import '!!file-loader?context=src/res&name=icons/iav-icons.ttf!src/res/icons/iav-icons.ttf' +import '!!file-loader?context=src/res&name=icons/iav-icons.woff!src/res/icons/iav-icons.woff' +import '!!file-loader?context=src/res&name=icons/iav-icons.svg!src/res/icons/iav-icons.svg' + export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, controls: { diff --git a/common/constants.js b/common/constants.js index 94a42b81abf59142a3d50f5a175d64208e02fbbd..c1fe2db5b6981123c9811587a0523a859b95c234 100644 --- a/common/constants.js +++ b/common/constants.js @@ -136,7 +136,11 @@ If you do not accept the Terms & Conditions you are not permitted to access or u LOADING_ANNOTATION_MSG: `Loading annotations... Please wait...`, ATLAS_SELECTOR_LABEL_SPACES: `Spaces`, - ATLAS_SELECTOR_LABEL_PARC_MAPS: `Parcellation maps` + ATLAS_SELECTOR_LABEL_PARC_MAPS: `Parcellation maps`, + + TOGGLE_LAYER_VISILITY: 'Toggle layer visility', + ORIENT_TO_LAYER: 'Orient to layer native orientation', + CONFIGURE_LAYER: 'Configure layer' } exports.QUICKTOUR_DESC ={ diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts index 8dbfce6e38bba1201cdd7d94adc310c0381cf1de..5dca7cbc394a3baa2360349fb9aafa7c08d6c5bc 100644 --- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts +++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.stories.ts @@ -117,10 +117,16 @@ const asyncLoader = async () => { const atlasDetail = await getAtlas(atlasId[species]) regionsDict[species] = {} - for (const parc of atlasDetail.parcellations) { - const parcDetail = await getParc(atlasDetail['@id'], parc['@id']) - regionsDict[species][parcDetail.name] = await getParcRegions(atlasDetail['@id'], parc['@id'], atlasDetail.spaces[0]["@id"] ) - } + await Promise.all( + atlasDetail.parcellations.map(async parc => { + try { + const parcDetail = await getParc(atlasDetail['@id'], parc['@id']) + regionsDict[species][parcDetail.name] = await getParcRegions(atlasDetail['@id'], parc['@id'], atlasDetail.spaces[0]["@id"] ) + } catch (e) { + console.warn(`fetching region detail for ${parc["@id"]} failed... Skipping...`) + } + }) + ) } return { diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts index f2e75e77f501517ac2f166f44a1b851042458489..ea321e012cccbbe69e27f0e8467b14b945876e00 100644 --- a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts +++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts @@ -1,44 +1,22 @@ import { ChangeDetectionStrategy, Component, Inject, Input, OnChanges, OnDestroy } from "@angular/core"; import { Store } from "@ngrx/store"; import { isMat4 } from "common/util" +import { CONST } from "common/constants" import { Observable } from "rxjs"; -import { atlasAppearance } from "src/state"; +import { atlasAppearance, atlasSelection } from "src/state"; import { NehubaViewerUnit } from ".."; import { NEHUBA_INSTANCE_INJTKN } from "../util"; +import { getExportNehuba } from "src/util/fn"; type Vec4 = [number, number, number, number] type Mat4 = [Vec4, Vec4, Vec4, Vec4] -const _VOL_DETAIL_MAP: Record<string, { shader: string, opacity: number }> = { - "PLI Fiber Orientation Red Channel": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(1.0 * x, x * 0., 0. * x )); } }", - opacity: 1 - }, - "PLI Fiber Orientation Green Channel": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0. * x, x * 1., 0. * x )); } }", - opacity: 0.5 - }, - "PLI Fiber Orientation Blue Channel": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0. * x, x * 0., 1.0 * x )); } }", - opacity: 0.25 - }, - "Blockface Image": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0.8 * x, x * 1., 0.8 * x )); } }", - opacity: 1.0 - }, - "PLI Transmittance": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x > 0.9) { emitTransparent(); } else { emitRGB(vec3(x * 1., x * 0.8, x * 0.8 )); } }", - opacity: 1.0 - }, - "T2w MRI": { - shader: "void main(){ float x = toNormalized(getDataValue()); if (x < 0.1) { emitTransparent(); } else { emitRGB(vec3(0.8 * x, 0.8 * x, x * 1. )); } }", - opacity: 1 - }, - "MRI Labels": { - shader: null, - opacity: 1 - } -} +export const idMat4: Mat4 = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], +] @Component({ selector: 'ng-layer-ctl', @@ -51,6 +29,8 @@ const _VOL_DETAIL_MAP: Record<string, { shader: string, opacity: number }> = { export class NgLayerCtrlCmp implements OnChanges, OnDestroy{ + public CONST = CONST + private onDestroyCb: (() => void)[] = [] private removeLayer: () => void @@ -77,7 +57,8 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{ this.opacity = Number(val) } - transform: Mat4 + transform: Mat4 = idMat4 + @Input('ng-layer-ctl-transform') set _transform(xform: string | Mat4) { const parsedResult = typeof xform === "string" @@ -111,12 +92,6 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{ } ngOnChanges(): void { - if (this.name in _VOL_DETAIL_MAP) { - const { shader, opacity } = _VOL_DETAIL_MAP[this.name] - this.shader = shader - this.opacity = opacity - } - if (this.name && this.source) { const { name } = this if (this.removeLayer) { @@ -145,6 +120,28 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{ } } + setOrientation(): void { + const { mat4, quat, vec3 } = getExportNehuba() + + /** + * glMatrix seems to store the matrix in transposed format + */ + + const incM = mat4.transpose(mat4.create(), mat4.fromValues(...this.transform.reduce((acc, curr) => [...acc, ...curr], []))) + const scale = mat4.getScaling(vec3.create(), incM) + const scaledM = mat4.scale(mat4.create(), incM, vec3.inverse(vec3.create(), scale)) + const q = mat4.getRotation(quat.create(0), scaledM) + + this.store.dispatch( + atlasSelection.actions.navigateTo({ + navigation: { + orientation: Array.from(q) + }, + animation: true + }) + ) + } + toggleVisibility(): void{ this.visible = !this.visible this.viewer.nehubaViewer.ngviewer.layerManager.getLayerByName(this.name).setVisible(this.visible) diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.stories.ts b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..b716f7d93824fd1e223450fde56655398aa91d0b --- /dev/null +++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.stories.ts @@ -0,0 +1,60 @@ +import { idMat4, NgLayerCtrlCmp } from "./ngLayerCtrl.component" +import { Meta, moduleMetadata, Story } from "@storybook/angular" +import { CommonModule } from "@angular/common" +import { MatButtonModule } from "@angular/material/button" +import { NEHUBA_INSTANCE_INJTKN } from "../util" +import { NEVER } from "rxjs" +import { action } from "@storybook/addon-actions" +import { MatTooltipModule } from "@angular/material/tooltip" +import { Store } from "@ngrx/store" + +export default { + component: NgLayerCtrlCmp, + decorators: [ + moduleMetadata({ + imports: [ + CommonModule, + MatButtonModule, + MatTooltipModule, + ], + providers: [ + { + provide: NEHUBA_INSTANCE_INJTKN, + useValue: NEVER, + }, + { + provide: Store, + useValue: { + dispatch: action('dispatch') + } + } + ] + }), + ] +} as Meta + + +const Template: Story<NgLayerCtrlCmp> = (args: any, { parameters }) => { + + const { + 'ng-layer-ctl-name': name, + 'ng-layer-ctl-transform': transform + } = args + + const { + pName, + pXform + } = parameters + + return { + props: { + name: name || pName || 'default name', + transform: transform || pXform || idMat4, + } + } +} + +export const NgLayerTune = Template.bind({}) +NgLayerTune.parameters = { + pXform: [[-0.74000001,0,0,38134608],[0,-0.26530117,-0.6908077,13562314],[0,-0.6908077,0.26530117,-3964904],[0,0,0,1]] +} diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html index e3442c7b4323dac373e0b9110549b8c97a206a3e..b53ec540ad7fd2bcf7df004327d9cc3f2b3ba51b 100644 --- a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html +++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.template.html @@ -1,6 +1,8 @@ <div [ngClass]="{ 'text-muted': !visible }"> - <button mat-icon-button (click)="toggleVisibility()"> + <button mat-icon-button + [matTooltip]="CONST.TOGGLE_LAYER_VISILITY" + (click)="toggleVisibility()"> <i [ngClass]="visible ? 'fa-eye' : 'fa-eye-slash'" class="far"></i> </button> @@ -8,7 +10,17 @@ {{ name }} </span> - <button mat-icon-button (click)="showOpacityCtrl = !showOpacityCtrl"> + <button + mat-icon-button + [matTooltip]="CONST.ORIENT_TO_LAYER" + (click)="setOrientation()"> + <i class="iavic iavic-rotation"></i> + </button> + + <button + mat-icon-button + [matTooltip]="CONST.CONFIGURE_LAYER" + (click)="showOpacityCtrl = !showOpacityCtrl"> <i class="fas fa-cog"></i> </button>