Newer
Older
import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output, TemplateRef, ViewChild } from "@angular/core";
import { select, Store } from "@ngrx/store";
import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
import { distinctUntilChanged } from "rxjs/operators";
import { ARIA_LABELS, CONST } from 'common/constants'
import { IViewer, TViewerEvent } from "../../viewer.interface";
import { NehubaMeshService } from "../mesh.service";
import { NehubaLayerControlService, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
import { getExportNehuba, getUuid } from "src/util/fn";
import { NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY } from "../layerCtrl.service/layerCtrl.util";
import { MatSnackBar } from "@angular/material/snack-bar";
import { getShader } from "src/util/constants";
import { EnumColorMapName } from "src/util/colorMaps";
import { MatDialog } from "@angular/material/dialog";
import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
import { SapiRegionModel } from "src/atlasComponents/sapi";
import { NehubaConfig } from "../config.service";
import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
import { linearTransform, TVALID_LINEAR_XFORM_DST, TVALID_LINEAR_XFORM_SRC } from "src/atlasComponents/sapi/core/space/interspaceLinearXform";
export const INVALID_FILE_INPUT = `Exactly one (1) file is required!`
@Component({
selector: 'iav-cmp-viewer-nehuba-glue',
templateUrl: './nehubaViewerGlue.template.html',
styleUrls: [
'./nehubaViewerGlue.style.css'
exportAs: 'iavCmpViewerNehubaGlue',
providers: [
{
provide: SET_MESHES_TO_LOAD,
useFactory: (meshService: NehubaMeshService) => meshService.loadMeshes$,
deps: [ NehubaMeshService ]
},
NehubaMeshService,
{
provide: SET_COLORMAP_OBS,
useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.setColorMap$,
deps: [ NehubaLayerControlService ]
},
{
provide: SET_LAYER_VISIBILITY,
useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.visibleLayer$,
deps: [ NehubaLayerControlService ]
},
{
provide: SET_SEGMENT_VISIBILITY,
useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.segmentVis$,
deps: [ NehubaLayerControlService ]
},
{
provide: NG_LAYER_CONTROL,
useFactory: (layerCtrl: NehubaLayerControlService) => layerCtrl.ngLayersController$,
deps: [ NehubaLayerControlService ]
},
NehubaLayerControlService,
NehubaMeshService,
],
changeDetection: ChangeDetectionStrategy.OnPush
export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy {
@ViewChild('layerCtrlTmpl', { static: true })
layerCtrlTmpl: TemplateRef<any>
while (this.onDestroyCb.length) this.onDestroyCb.pop()()
}
public viewerEvent = new EventEmitter<TViewerEvent<'nehuba'>>()
@Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
/**
* define onclick behaviour
*/
if (clickInterceptor) {
const { deregister, register } = clickInterceptor
const selOnhoverRegion = this.selectHoveredRegion.bind(this)
this.onDestroyCb.push(() => deregister(selOnhoverRegion))
}
/**
* on hover segment
*/
const onhovSegSub = this.store$.pipe(
})
this.onDestroyCb.push(() => onhovSegSub.unsubscribe())
private selectHoveredRegion(_ev: any): boolean{
/**
* If label indicies are not defined by the ontology, it will be a string in the format of `{ngId}#{labelIndex}`
*/
const trueOnhoverSegments = this.onhoverSegments && this.onhoverSegments.filter(v => typeof v === 'object')
if (!trueOnhoverSegments || (trueOnhoverSegments.length === 0)) return true
atlasSelection.actions.selectRegion({
region: trueOnhoverSegments[0]
private droppedLayerNames: {
layerName: string
resourceUrl: string
}[] = []
private dismissAllAddedLayers(){
while (this.droppedLayerNames.length) {
const { resourceUrl, layerName } = this.droppedLayerNames.pop()
this.store$.dispatch(
atlasAppearance.actions.removeCustomLayer({
id: layerName
})
)
URL.revokeObjectURL(resourceUrl)
}
}
if (files.length !== 1) {
this.snackbar.open(INVALID_FILE_INPUT, 'Dismiss', {
duration: 5000
})
return
}
const randomUuid = getUuid()
const file = files[0]
/**
* TODO check extension?
*/
this.dismissAllAddedLayers()
let message = `The swc rendering is experimental. Please contact us on any feedbacks. `
const swcText = await file.text()
let src: TVALID_LINEAR_XFORM_SRC
if (/ccf/i.test(swcText)) {
src = "CCF"
message += `CCF detected, applying known transformation.`
}
if (!src) {
message += `no known space detected. Applying default transformation.`
}
const xform = await linearTransform(src, dst)
const url = URL.createObjectURL(file)
this.droppedLayerNames.push({
layerName: randomUuid,
resourceUrl: url
})
this.store$.dispatch(
atlasAppearance.actions.addCustomLayer({
customLayer: {
id: randomUuid,
source: `swc://${url}`,
segments: ["1"],
clType: 'customlayer/nglayer' as const
}
})
)
this.snackbar.open(message, "Dismiss", {
duration: 10000
})
// Get file, try to inflate, if files, use original array buffer
const buf = await file.arrayBuffer()
let outbuf
try {
outbuf = getExportNehuba().pako.inflate(buf).buffer
} catch (e) {
console.log('unpack error', e)
outbuf = buf
}
method: 'PROCESS_NIFTI',
param: {
nifti: outbuf
transfers: [ outbuf ]
})
const { meta, buffer } = result
const url = URL.createObjectURL(new Blob([ buffer ]))
this.droppedLayerNames.push({
layerName: randomUuid,
resourceUrl: url
})
this.store$.dispatch(
atlasAppearance.actions.addCustomLayer({
customLayer: {
id: randomUuid,
source: `nifti://${url}`,
shader: getShader({
colormap: EnumColorMapName.MAGMA,
lowThreshold: meta.min || 0,
highThreshold: meta.max || 1
}),
clType: 'customlayer/nglayer'
}
})
)
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
this.dialog.open(
this.layerCtrlTmpl,
{
data: {
layerName: randomUuid,
filename: file.name,
moreInfoFlag: false,
min: meta.min || 0,
max: meta.max || 1,
warning: meta.warning || []
},
hasBackdrop: false,
disableClose: true,
position: {
top: '0em'
},
autoFocus: false,
panelClass: [
'no-padding-dialog',
'w-100'
]
}
).afterClosed().subscribe(
() => this.dismissAllAddedLayers()
)
} catch (e) {
console.error(e)
this.snackbar.open(`Error loading nifti: ${e.toString()}`, 'Dismiss', {
duration: 5000
})
}