Newer
Older
import { Component, Input, Output, EventEmitter, ElementRef, OnChanges, OnDestroy, AfterViewInit } from "@angular/core";
import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
import { TThreeSurferConfig, TThreeSurferMode } from "../types";
import { retry, flattenRegions } from 'common/util'
type THandlingCustomEv = {
regions: ({ name?: string, error?: string })[]
event: CustomEvent
evMesh?: {
faceIndex: number
verticesIndicies: number[]
}
}
@Component({
selector: 'three-surfer-glue-cmp',
templateUrl: './threeSurfer.template.html',
styleUrls: [
'./threeSurfer.style.css'
]
})
export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, AfterViewInit, OnDestroy {
@Input()
selectedTemplate: any
@Input()
selectedParcellation: any
@Output()
viewerEvent = new EventEmitter<TViewerEvent<'threeSurfer'>>()
private domEl: HTMLElement
private config: TThreeSurferConfig
public modes: TThreeSurferMode[] = []
public selectedMode: string
private regionMap: Map<string, Map<number, any>> = new Map()
){
this.domEl = this.el.nativeElement
}
tsRef: any
threeSurfer: any
mesh: string
colormap: string
hemisphere: string
private unloadAllMeshes() {
while(this.loadedMeshes.length > 0) {
const m = this.loadedMeshes.pop()
}
}
public async loadMode(mode: TThreeSurferMode) {
this.unloadAllMeshes()
this.selectedMode = mode.name
const { meshes } = mode
for (const singleMesh of meshes) {
const tsM = await this.tsRef.loadMesh(
parseContext(mesh, [this.config['@context']])
)
const rMap = this.regionMap.get(hemisphere)
const applyCM = new Map()
for (const [ lblIdx, region ] of rMap.entries()) {
applyCM.set(lblIdx, (region.rgb || [200, 200, 200]).map(v => v/255))
}
const tsC = await this.tsRef.loadColormap(
parseContext(colormap, [this.config['@context']])
)
let colorIdx = tsC[0].getData()
if (tsC[0].attributes.DataType === 'NIFTI_TYPE_INT16') {
colorIdx = (window as any).ThreeSurfer.GiftiBase.castF32UInt16(colorIdx)
}
this.loadedMeshes.push({
threeSurfer: tsM,
colormap,
mesh,
hemisphere,
vIdxArr: colorIdx
})
this.tsRef.applyColorMap(tsM, colorIdx,
{
custom: applyCM
}
)
if (this.tsRef) {
this.ngOnDestroy()
this.ngAfterViewInit()
}
/**
* wait until threesurfer is defined in window
*/
await retry(async () => {
if (typeof (window as any).ThreeSurfer === 'undefined') throw new Error('ThreeSurfer not yet defined')
}, {
timeout: 160,
retries: 10,
})
this.config = this.selectedTemplate['three-surfer']
// somehow curv ... cannot be parsed properly by gifti parser... something about points missing
this.modes = this.config.modes.filter(m => !/curv/.test(m.name))
if (!this.tsRef) {
this.tsRef = new (window as any).ThreeSurfer(this.domEl, {highlightHovered: true})
this.onDestroyCb.push(
() => {
this.tsRef.dispose()
this.tsRef = null
}
)
}
const flattenedRegions = flattenRegions(this.selectedParcellation.regions)
for (const region of flattenedRegions) {
if (region.labelIndex) {
const hemisphere = /left/.test(region.name)
? 'left'
: /right/.test(region.name)
? 'right'
: null
if (!hemisphere) throw new Error(`region ${region.name} does not have hemisphere defined`)
if (!this.regionMap.has(hemisphere)) {
this.regionMap.set(hemisphere, new Map())
}
const rMap = this.regionMap.get(hemisphere)
rMap.set(region.labelIndex, region)
// load mode0 by default
this.loadMode(this.config.modes[0])
this.viewerEvent.emit({
type: EnumViewerEvt.VIEWERLOADED,
ngAfterViewInit(){
const customEvHandler = (ev: CustomEvent) => {
const evMesh = ev.detail?.mesh && {
faceIndex: ev.detail.mesh.faceIndex,
// typo in three-surfer
verticesIndicies: ev.detail.mesh.verticesIdicies
}
const custEv: THandlingCustomEv = {
event: ev,
regions: [],
evMesh
}
return this.handleMouseoverEvent(custEv)
}
const evGeom = ev.detail.mesh.geometry
const evVertIdx = ev.detail.mesh.verticesIdicies
const found = this.loadedMeshes.find(({ threeSurfer }) => threeSurfer === evGeom)
if (!found) return this.handleMouseoverEvent(custEv)
const { hemisphere: key, vIdxArr } = found
if (!key || !evVertIdx) {
return this.handleMouseoverEvent(custEv)
for (const vIdx of evVertIdx) {
labelIdxSet.add(
vIdxArr[vIdx]
)
}
if (labelIdxSet.size === 0) {
return this.handleMouseoverEvent(custEv)
const hemisphereMap = this.regionMap.get(key)
custEv.regions = Array.from(labelIdxSet).map(v => {
return {
error: `unknown#${v}`
}
})
return this.handleMouseoverEvent(custEv)
custEv.regions = Array.from(labelIdxSet)
.map(lblIdx => {
const ontoR = hemisphereMap.get(lblIdx)
if (ontoR) {
return ontoR
} else {
return {
error: `unkonwn#${lblIdx}`
return this.handleMouseoverEvent(custEv)
this.domEl.addEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME, customEvHandler)
() => this.domEl.removeEventListener((window as any).ThreeSurfer.CUSTOM_EVENTNAME, customEvHandler)
private handleMouseoverEvent(ev: THandlingCustomEv){
const { regions: mouseover, evMesh } = ev
this.viewerEvent.emit({
type: EnumViewerEvt.VIEWER_CTX,
data: {
viewerType: 'threeSurfer',
payload: {
fsversion: this.selectedMode,
faceIndex: evMesh?.faceIndex,
vertexIndices: evMesh?.verticesIndicies,
position: [],
_mouseoverRegion: mouseover.filter(el => !el.error)
}
}
})
this.mouseoverText = mouseover.length === 0 ?
null :
mouseover.map(
el => el.name || el.error
).join(' / ')