diff --git a/docs/releases/v2.14.5.md b/docs/releases/v2.14.5.md index 243ce4cf33361cdd240846c1b922bb61666e3921..8ebec4a4287fd3b225e23e2a906f83a8e30cd5e0 100644 --- a/docs/releases/v2.14.5.md +++ b/docs/releases/v2.14.5.md @@ -10,7 +10,8 @@ - Added legend to region hierarchy - Allow latest queried concept in feature view - Allow experimental flag to be set to be on at runtime (this also shows the button, allow toggling of experimental features) -- Added code snippet to limited panels +- Feature: added supported for .annot fsaverage label +- (experimental) Added code snippet to limited panels - (experimental) allow addition of custom linear coordinate space - (experimental) show BigBrain slice number diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts index 03659664a737629229c78ae3a5e6c04c1660fd06..b0eb95d8be166dd449c86cadb2662bc2964749d9 100644 --- a/src/atlasComponents/sapi/translateV3.ts +++ b/src/atlasComponents/sapi/translateV3.ts @@ -398,13 +398,19 @@ class TranslateV3 { } async translateLabelledMapToThreeLabel(map:PathReturn<"/map">) { - const threeLabelMap: Record<string, { laterality: 'left' | 'right', url: string, region: LabelledMap[] }> = {} - const registerLayer = (url: string, laterality: 'left' | 'right', region: string, label: number) => { + const threeLabelMap: Record<string, { + laterality: 'left' | 'right' + url: string + region: LabelledMap[] + clType: 'baselayer/threesurfer-label/gii-label' | 'baselayer/threesurfer-label/annot' + }> = {} + const registerLayer = (url: string, clType: 'baselayer/threesurfer-label/gii-label' | 'baselayer/threesurfer-label/annot', laterality: 'left' | 'right', region: string, label: number) => { if (!threeLabelMap[url]) { threeLabelMap[url] = { laterality, region: [], url, + clType } } @@ -416,18 +422,26 @@ class TranslateV3 { for (const regionname in map.indices) { for (const { volume: volIdx, fragment, label } of map.indices[regionname]) { const volume = map.volumes[volIdx || 0] - if (!volume.formats.includes("gii-label")) { - // Does not support gii-label... skipping! - continue + let clType: 'baselayer/threesurfer-label/gii-label' | 'baselayer/threesurfer-label/annot' | null = null + let providedVolume: typeof volume['providedVolumes'][string] | null = null + if (volume.formats.includes("gii-label")) { + clType = 'baselayer/threesurfer-label/gii-label' + providedVolume = volume.providedVolumes["gii-label"] + } + if (volume.formats.includes("freesurfer-annot")) { + clType = 'baselayer/threesurfer-label/annot' + providedVolume = volume.providedVolumes["freesurfer-annot"] } - const { ["gii-label"]: giiLabel } = volume.providedVolumes - + if (!providedVolume || !clType) { + // does not support baselayer threesurfer label, skipping + continue + } if (!fragment || !["left hemisphere", "right hemisphere"].includes(fragment)) { console.warn(`either fragment not defined, or fragment is not '{left|right} hemisphere'. Skipping!`) continue } - if (!giiLabel[fragment]) { + if (!providedVolume[fragment]) { // Does not support gii-label... skipping! continue } @@ -438,7 +452,7 @@ class TranslateV3 { console.warn(`cannot determine the laterality! skipping`) continue } - registerLayer(giiLabel[fragment], laterality, regionname, label) + registerLayer(providedVolume[fragment], clType, laterality, regionname, label) } } return threeLabelMap diff --git a/src/index.html b/src/index.html index 3d3708ae31b94d2ecca8c544cc467f841e621e6b..8158adec9af469dbf1fccc909f49c26ed7e297dc 100644 --- a/src/index.html +++ b/src/index.html @@ -12,7 +12,7 @@ <link rel="stylesheet" href="version.css"> <link rel="icon" type="image/png" href="assets/favicons/favicon-128-light.png"/> <script src="extra_js.js"></script> - <script src="https://unpkg.com/three-surfer@0.0.13/dist/bundle.js" defer></script> + <script src="https://unpkg.com/three-surfer@0.0.15/dist/bundle.js" defer></script> <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.26/dist/ng-layer-tune/ng-layer-tune.esm.js"></script> <script src="https://cdn.plot.ly/plotly-2.24.1.min.js" charset="utf-8"></script> <title>Siibra Explorer</title> diff --git a/src/state/atlasAppearance/const.ts b/src/state/atlasAppearance/const.ts index ed44978229e5f957ea50d0c7764e6fcb545fab6a..16195d9e3392acfc46c6a172504c5eaa0d6dfaf3 100644 --- a/src/state/atlasAppearance/const.ts +++ b/src/state/atlasAppearance/const.ts @@ -22,7 +22,7 @@ export type ThreeSurferCustomLayer = { } & CustomLayerBase export type ThreeSurferCustomLabelLayer = { - clType: 'baselayer/threesurfer-label' + clType: 'baselayer/threesurfer-label/gii-label' | 'baselayer/threesurfer-label/annot' source: string laterality: 'left' | 'right' } & CustomLayerBase diff --git a/src/viewerModule/threeSurfer/store/effects.ts b/src/viewerModule/threeSurfer/store/effects.ts index 86992a1c3330ed1b3150467d04aa4f984cde169e..7eef500a7653419655e9d384edd2c6f50b87262c 100644 --- a/src/viewerModule/threeSurfer/store/effects.ts +++ b/src/viewerModule/threeSurfer/store/effects.ts @@ -43,7 +43,8 @@ export class ThreeSurferEffects { map( cl => cl.filter(layer => layer.clType === "baselayer/threesurfer" || - layer.clType === "baselayer/threesurfer-label" + layer.clType === "baselayer/threesurfer-label/annot" || + layer.clType === "baselayer/threesurfer-label/gii-label" ) as ThreeSurferCustomLayer[] ) ) @@ -121,9 +122,9 @@ export class ThreeSurferEffects { switchMap(({ labels }) => { const labelMaps: ThreeSurferCustomLabelLayer[] = [] for (const key in labels) { - const { laterality, url } = labels[key] + const { laterality, url, clType } = labels[key] labelMaps.push({ - clType: 'baselayer/threesurfer-label', + clType, id: `${url}-${laterality}`, laterality, source: url diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts index 4a5c1238064d88be6953b2e65411e7c00e25862d..74d86122db39c3c39af97b85165d55e2b5cdfa56 100644 --- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts +++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts @@ -1,6 +1,6 @@ import { Component, Output, EventEmitter, ElementRef, OnDestroy, AfterViewInit, Optional, ChangeDetectionStrategy } from "@angular/core"; import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface"; -import { BehaviorSubject, combineLatest, concat, forkJoin, from, merge, NEVER, Observable, of, Subject } from "rxjs"; +import { BehaviorSubject, combineLatest, concat, forkJoin, from, merge, NEVER, Observable, of, Subject, throwError } from "rxjs"; import { catchError, debounceTime, distinctUntilChanged, filter, map, scan, shareReplay, startWith, switchMap, tap, withLatestFrom } from "rxjs/operators"; import { ComponentStore, LockError } from "src/viewerModule/componentStore"; import { select, Store } from "@ngrx/store"; @@ -95,6 +95,17 @@ type TThreeSurfer = { loadColormap: (url: string) => Promise<GiiInstance> setupAnimation: () => void dispose: () => void + loadVertexData: (url: string) => Promise<{ + vertex: number[] + labels: { + index: number + name: string + color: number[] + vertices: number[] + }[] + readonly vertexLabels: Uint16Array + readonly colormap: Map<number, number[]> + }> control: any camera: any customColormap: WeakMap<TThreeGeometry, any> @@ -252,7 +263,9 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit ) private vertexIndexLayers$: Observable<ThreeSurferCustomLabelLayer[]> = this.customLayers$.pipe( - map(layers => layers.filter(l => l.clType === "baselayer/threesurfer-label") as ThreeSurferCustomLabelLayer[]), + map(layers => layers.filter(l => + l.clType === "baselayer/threesurfer-label/gii-label" || l.clType === "baselayer/threesurfer-label/annot" + ) as ThreeSurferCustomLabelLayer[]), distinctUntilChanged(arrayEqual((o, n) => o.id === n.id)), ) @@ -265,22 +278,37 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit ), switchMap(layers => forkJoin( - layers.map(layer => - from( - this.tsRef.loadColormap(layer.source) - ).pipe( - map(giiInstance => { - let vertexIndices: number[] = giiInstance[0].getData() - if (giiInstance[0].attributes.DataType === 'NIFTI_TYPE_INT16') { - vertexIndices = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(vertexIndices) - } - return { - indexLayer: layer, - vertexIndices - } - }) - ) - ) + layers.map(layer => { + if (layer.clType === "baselayer/threesurfer-label/gii-label") { + return from( + this.tsRef.loadColormap(layer.source) + ).pipe( + map(giiInstance => { + let vertexIndices: number[] = giiInstance[0].getData() + if (giiInstance[0].attributes.DataType === 'NIFTI_TYPE_INT16') { + vertexIndices = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(vertexIndices) + } + return { + indexLayer: layer, + vertexIndices + } + }) + ) + } + if (layer.clType === "baselayer/threesurfer-label/annot") { + return from( + this.tsRef.loadVertexData(layer.source) + ).pipe( + map(v => { + return { + indexLayer: layer, + vertexIndices: v.vertexLabels + } + }) + ) + } + return throwError(() => new Error(`layer is neither annot nor gii-label`)) + }) ) ), map(layers => { @@ -317,8 +345,13 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit const { label, region } = curr let key : 'left' | 'right' - if ( /left/i.test(region.name) ) key = 'left' - if ( /right/i.test(region.name) ) key = 'right' + if ( + /left/i.test(region.name) || /^lh/i.test(region.name) + ) key = 'left' + if ( + /right/i.test(region.name) || /^rh/i.test(region.name) + ) key = 'right' + if (!key) { /** * TODO