Newer
Older
import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Inject, Optional } from "@angular/core";
import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, race, timer, Subject } from 'rxjs'
import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip, tap } from "rxjs/operators";
import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store";
import { LoggingService } from "src/logging";
import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn";
import { NEHUBA_INSTANCE_INJTKN, scanSliceViewRenderFn } from "../util";
import { deserialiseParcRegionId } from 'common/util'
import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants";
import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
import '!!file-loader?context=third_party&name=main.bundle.js!export-nehuba/dist/min/main.bundle.js'
import '!!file-loader?context=third_party&name=chunk_worker.bundle.js!export-nehuba/dist/min/chunk_worker.bundle.js'
import { INgLayerCtrl, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY, TNgLayerCtrl } from "../layerCtrl.service/layerCtrl.util";
const NG_LANDMARK_LAYER_NAME = 'spatial landmark layer'
const NG_USER_LANDMARK_LAYER_NAME = 'user landmark layer'
/**
* optimized for nehubaConfig.layout.useNehubaPerspective.fixedZoomPerspectiveSlices
* sliceZoom
* sliceViewportWidth
* sliceViewportHeight
*/
const NG_LANDMARK_CONSTANT = 1e-8
export const IMPORT_NEHUBA_INJECT_TOKEN = `IMPORT_NEHUBA_INJECT_TOKEN`
}
labelIndicies: number[]
}
export interface INehubaLifecycleHook{
onInit?: () => void
}
export const scanFn = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
const found = acc.find(layerLabelIndex => {
return layerLabelIndex.layer.name === curr.layer.name
})
if (!found) {
return [ ...acc, curr ]
return acc.map(layerLabelIndex => {
return layerLabelIndex.layer.name === curr.layer.name
? curr
: layerLabelIndex
})
/**
* no selector is needed, as currently, nehubaviewer is created dynamically
*/
@Component({
templateUrl : './nehubaViewer.template.html',
styleUrls : [
private sliceviewLoading$: Observable<boolean>
public overrideShowLayers: string[] = []
public lifecycle: INehubaLifecycleHook
public viewerPosInVoxel$ = new BehaviorSubject(null)
public viewerPosInReal$ = new BehaviorSubject(null)
public mousePosInVoxel$ = new BehaviorSubject(null)
public mousePosInReal$ = new BehaviorSubject(null)
private subscriptions: Subscription[] = []
@Output() public nehubaReady: EventEmitter<null> = new EventEmitter()
@Output() public layersChanged: EventEmitter<null> = new EventEmitter()
@Output() public viewerPositionChange: EventEmitter<any> = new EventEmitter()
@Output() public mouseoverLandmarkEmitter: EventEmitter<number | null> = new EventEmitter()
@Output() public mouseoverUserlandmarkEmitter: EventEmitter<string> = new EventEmitter()
@Output() public regionSelectionEmitter: EventEmitter<{segment: number, layer: {name?: string, url?: string}}> = new EventEmitter()
@Output() public errorEmitter: EventEmitter<any> = new EventEmitter()
public auxilaryMeshIndices: number[] = []
/* only used to set initial navigation state */
public initNav: any
public initRegions: any[]
public initNiftiLayers: any[] = []
private _dim: [number, number, number]
return this._dim
? this._dim
: [1.5e9, 1.5e9, 1.5e9]
}
public _s2$: any = null
public _s3$: any = null
public _s4$: any = null
public _s5$: any = null
public _s6$: any = null
public _s7$: any = null
public _s8$: any = null
Xiao Gui
committed
Xiao Gui
committed
this._s2$,
this._s3$,
this._s4$,
this._s5$,
this._s6$,
Xiao Gui
committed
]
private createNehubaPromiseRs: Function
private createNehubaPromise = new Promise(rs => {
this.createNehubaPromiseRs = rs
})
public nehubaLoaded: boolean = false
public landmarksLoaded: boolean = false
constructor(
public elementRef: ElementRef,
private workerService: AtlasWorkerService,
private log: LoggingService,
@Inject(IMPORT_NEHUBA_INJECT_TOKEN) getImportNehubaPr: () => Promise<any>,
@Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaViewer$: Subject<NehubaViewerUnit>,
@Optional() @Inject(SET_MESHES_TO_LOAD) private injSetMeshesToLoad$: Observable<IMeshesToLoad>,
@Optional() @Inject(SET_COLORMAP_OBS) private setColormap$: Observable<IColorMap>,
@Optional() @Inject(SET_LAYER_VISIBILITY) private layerVis$: Observable<string[]>,
@Optional() @Inject(SET_SEGMENT_VISIBILITY) private segVis$: Observable<string[]>,
@Optional() @Inject(NG_LAYER_CONTROL) private layerCtrl$: Observable<TNgLayerCtrl<keyof INgLayerCtrl>>,
if (this.nehubaViewer$) {
this.nehubaViewer$.next(this)
}
const fixedZoomPerspectiveSlices = this.config && this.config.layout && this.config.layout.useNehubaPerspective && this.config.layout.useNehubaPerspective.fixedZoomPerspectiveSlices
if (fixedZoomPerspectiveSlices) {
const { sliceZoom, sliceViewportWidth, sliceViewportHeight } = fixedZoomPerspectiveSlices
const dim = Math.min(sliceZoom * sliceViewportWidth, sliceZoom * sliceViewportHeight)
this._dim = [dim, dim, dim]
}
this.patchNG()
this.loadNehuba()
this.layersChangedHandler = this.nehubaViewer.ngviewer.layerManager.layersChanged.add(() => this.layersChanged.emit(null))
this.nehubaViewer.ngviewer.registerDisposer(this.layersChangedHandler)
.then(() => {
// all mutation to this.nehubaViewer should await createNehubaPromise
this.createNehubaPromiseRs()
})
.catch(e => this.errorEmitter.emit(e))
/**
* TODO move to layerCtrl.service
*/
this.ondestroySubscriptions.push(
fromEvent(this.workerService.worker, 'message').pipe(
if (!message) {
// this.log.error('worker response message is undefined', message)
return false
}
if (!message.data) {
// this.log.error('worker response message.data is undefined', message.data)
return false
}
/* worker responded with not assembled landmark, no need to act */
return false
}
/* file url needs to be defined */
return false
}
return true
}),
debounceTime(100),
filter(e => this.templateId === e.data.template),
).subscribe(url => {
this.removeSpatialSearch3DLandmarks()
const _ = {}
source : `vtk://${url}`,
}
this.loadLayer(_)
)
/**
* TODO move to layerCtrl.service
*/
this.ondestroySubscriptions.push(
fromEvent(this.workerService.worker, 'message').pipe(
if (!message) {
// this.log.error('worker response message is undefined', message)
if (!message.data) {
// this.log.error('worker response message.data is undefined', message.data)
/* worker responded with not assembled landmark, no need to act */
return false
}
/**
* nb url may be undefined
* if undefined, user have removed all user landmarks, and all that needs to be done
* is remove the user landmark layer
return true
}),
debounceTime(100),
this.removeuserLandmarks()
/**
* url may be null if user removes all landmarks
*/
if (!url) {
/**
* remove transparency from meshes in current layer(s)
*/
_[NG_USER_LANDMARK_LAYER_NAME] = {
type: 'mesh',
source: `vtk://${url}`,
shader: this.userLandmarkShader,
/**
* adding transparency to meshes in current layer(s)
*/
if (this.setColormap$) {
this.ondestroySubscriptions.push(
this.setColormap$.pipe(
switchMap(switchMapWaitFor({
condition: () => !!(this.nehubaViewer?.ngviewer)
})),
debounceTime(160),
).subscribe(v => {
const map = new Map()
for (const key in v) {
const m = new Map()
map.set(key, m)
for (const lblIdx in v[key]) {
m.set(lblIdx, v[key][lblIdx])
}
}
this.setColorMap(map)
})
)
} else {
this.log.error(`SET_COLORMAP_OBS not provided`)
}
if (this.layerVis$) {
this.ondestroySubscriptions.push(
this.layerVis$.pipe(
switchMap(switchMapWaitFor({ condition: () => !!(this.nehubaViewer?.ngviewer) })),
debounceTime(160),
).subscribe((layerNames: string[]) => {
/**
* debounce 160ms to set layer invisible etc
* on switch from freesurfer -> volumetric viewer, race con results in managed layer not necessarily setting layer visible correctly
*/
const managedLayers = this.nehubaViewer.ngviewer.layerManager.managedLayers
managedLayers.forEach(layer => layer.setVisible(false))
for (const layerName of layerNames) {
const layer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(layerName)
if (layer) {
layer.setVisible(true)
} else {
this.log.log('layer unavailable', layerName)
}
}
})
)
} else {
this.log.error(`SET_LAYER_VISIBILITY not provided`)
}
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
if (this.segVis$) {
this.ondestroySubscriptions.push(
this.segVis$.pipe().subscribe(val => {
// null === hide all seg
if (val === null) {
this.hideAllSeg()
return
}
// empty array === show all seg
if (val.length === 0) {
this.showAllSeg()
return
}
// show limited seg
this.showSegs(val)
})
)
} else {
this.log.error(`SET_SEGMENT_VISIBILITY not provided`)
}
if (this.layerCtrl$) {
this.ondestroySubscriptions.push(
this.layerCtrl$.pipe(
bufferUntil(({
condition: () => !!this.nehubaViewer?.ngviewer
}))
).subscribe(messages => {
for (const message of messages) {
if (message.type === 'add') {
const p = message as TNgLayerCtrl<'add'>
this.loadLayer(p.payload)
}
if (message.type === 'remove') {
const p = message as TNgLayerCtrl<'remove'>
for (const name of p.payload.names){
this.removeLayer({ name })
}
}
if (message.type === 'update') {
const p = message as TNgLayerCtrl<'update'>
this.updateLayer(p.payload)
}
}
})
)
} else {
this.log.error(`NG_LAYER_CONTROL not provided`)
}
public numMeshesToBeLoaded: number = 0
public applyPerformanceConfig({ gpuLimit }: Partial<ViewerConfiguration>) {
if (gpuLimit && this.nehubaViewer) {
const limit = this.nehubaViewer.ngviewer.state.children.get('gpuMemoryLimit')
if (limit && limit.restoreState) {
limit.restoreState(gpuLimit)
}
}
}
/* required to check if correct landmarks are loaded */
return this._templateId
}
this._templateId = id
}
public spatialLandmarkSelectionChanged(labels: number[]) {
const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}`
const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }`
if (!this.nehubaViewer) {
if (!PRODUCTION) { this.log.warn('setting special landmark selection changed failed ... nehubaViewer is not yet defined') }
return
}
const landmarkLayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(NG_LANDMARK_LAYER_NAME)
if (!landmarkLayer) {
if (!PRODUCTION) { this.log.warn('landmark layer could not be found ... will not update colour map') }
return
}
if (labels.length === 0) {
landmarkLayer.layer.displayState.fragmentMain.restoreState(FRAGMENT_MAIN_WHITE)
} else {
landmarkLayer.layer.displayState.fragmentMain.restoreState(newShader)
}
}
// TODO move region management to another service
public multiNgIdsLabelIndexMap: Map<string, Map<number, any>> = new Map()
Xiao Gui
committed
public navPosReal: [number, number, number] = [0, 0, 0]
public navPosVoxel: [number, number, number] = [0, 0, 0]
Xiao Gui
committed
public mousePosReal: [number, number, number] = [0, 0, 0]
public mousePosVoxel: [number, number, number] = [0, 0, 0]
Xiao Gui
committed
private _multiNgIdColorMap: Map<string, Map<number, {red: number, green: number, blue: number}>>
get multiNgIdColorMap() {
return this._multiNgIdColorMap
}
set multiNgIdColorMap(val) {
this._multiNgIdColorMap = val
}
private loadMeshes$: ReplaySubject<{labelIndicies: number[], layer: { name: string }}> = new ReplaySubject()
public mouseOverSegment: number | null
public getNgHash: () => string = () => this.exportNehuba
? this.exportNehuba.getNgHash()
public loadNehuba() {
this.nehubaViewer = this.exportNehuba.createNehubaViewer(this.config, (err) => {
/* print in debug mode */
this.log.error(err)
/**
* Hide all layers except the base layer (template)
* Then show the layers referenced in multiNgIdLabelIndexMap
*/
Xiao Gui
committed
/* creation of the layout is done on next frame, hence the settimeout */
setTimeout(() => {
this.nehubaReady.emit(null)
this.newViewerInit()
this.loadNewParcellation()
this.sliceviewLoading$ = fromEvent(this.elementRef.nativeElement, 'sliceRenderEvent').pipe(
scan(scanSliceViewRenderFn, [ null, null, null ]),
map(arrOfFlags => arrOfFlags.some(flag => flag)),
startWith(true),
)
(this.injSetMeshesToLoad$ || this.loadMeshes$).pipe(
switchMap(layerLabelIdx =>
/**
* sometimes (e.g. when all slice views are minimised), sliceviewlaoding will not emit
* so if sliceviewloading does not emit another value (except the initial true value)
* force start loading of mesh
*/
race(
this.sliceviewLoading$.pipe(
skip(1)
),
timer(500).pipe(
mapTo(false)
)
).pipe(
filter(flag => !flag),
take(1),
mapTo(layerLabelIdx),
)
),
).subscribe(layersLabelIndex => {
let totalMeshes = 0
const { layer, labelIndicies } = layerLayerIndex
totalMeshes += labelIndicies.length
this.nehubaViewer.setMeshesToLoad(labelIndicies, layer)
}
// TODO implement total mesh to be loaded and mesh loading UI
const { onInit } = this.lifecycle || {}
onInit && onInit.call(this)
if (this.nehubaViewer$) {
this.nehubaViewer$.next(null)
}
this.subscriptions.pop().unsubscribe()
}
Xiao Gui
committed
})
this.ondestroySubscriptions.forEach(s => s.unsubscribe())
this.onDestroyCb.pop()()
}
this.nehubaViewer && this.nehubaViewer.dispose()
private onDestroyCb: Array<() => void> = []
private patchNG() {
const { LayerManager, UrlHashBinding } = this.exportNehuba.getNgPatchableObj()
// this.log.log('seturl hash')
// this.log.log('setting url hash')
}
UrlHashBinding.prototype.updateFromUrlHash = () => {
/* TODO find a more permanent fix to disable double click */
LayerManager.prototype.invokeAction = (arg) => {
/**
* The emitted value does not affect the region selection
* the region selection is taken care of in nehubaContainer
*/
const map = this.multiNgIdsLabelIndexMap.get(this.mouseOverLayer.name)
const region = map && map.get(this.mouseOverSegment)
if (arg === 'select') {
this.regionSelectionEmitter.emit({ segment: region, layer: this.mouseOverLayer })
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
this.onDestroyCb.push(() => LayerManager.prototype.invokeAction = (_arg) => { /** in default neuroglancer, this function is invoked when selection occurs */ })
/**
* if selector is an empty object, select all layers
*/
return layerObj instanceof Object && Object.keys(layerObj).every(key =>
/**
* the property described by the selector must exist and ...
*/
/**
* if the selector is regex, test layer property
*/
( layerObj[key] instanceof RegExp
? layerObj[key].test(l[key])
/**
* if selector is string, test for strict equality
*/
: typeof layerObj[key] === 'string'
? layerObj[key] === l[key]
/**
* otherwise do not filter
*/
private userLandmarkShader: string = FRAGMENT_MAIN_WHITE
public updateUserLandmarks(landmarks: any[]) {
if (!this.nehubaViewer) {
return
this.workerService.worker.postMessage({
type : 'GET_USERLANDMARKS_VTK',
scale: Math.min(...this.dim.map(v => v * NG_LANDMARK_CONSTANT)),
landmarks : landmarks.map(lm => lm.position.map(coord => coord * 1e6)),
})
const parseLmColor = lm => {
if (!lm) return null
const { color } = lm
if (!color) return null
if (!Array.isArray(color)) return null
if (color.length !== 3) return null
const parseNum = num => (num >= 0 && num <= 255 ? num / 255 : 1).toFixed(3)
return `emitRGB(vec3(${color.map(parseNum).join(',')}));`
}
const appendConditional = (frag, idx) => frag && `if (label > ${idx - 0.01} && label < ${idx + 0.01}) { ${frag} }`
if (landmarks.some(parseLmColor)) {
this.userLandmarkShader = `void main(){ ${landmarks.map(parseLmColor).map(appendConditional).filter(v => !!v).join('else ')} else {${FRAGMENT_EMIT_WHITE}} }`
} else {
this.userLandmarkShader = FRAGMENT_MAIN_WHITE
}
}
this.removeLayer({
})
}
this.removeLayer({
name : NG_USER_LANDMARK_LAYER_NAME,
})
}
// pos in mm
public addSpatialSearch3DLandmarks(geometries: any[], scale?: number, type?: 'icosahedron') {
this.workerService.worker.postMessage({
type : 'GET_LANDMARKS_VTK',
template : this.templateId,
scale: Math.min(...this.dim.map(v => v * NG_LANDMARK_CONSTANT)),
geometry === null
? null
// gemoetry : [number,number,number] | [ [number,number,number][], [number,number,number][] ]
: isNaN(geometry[0])
? [geometry[0].map(triplets => triplets.map(coord => coord * 1e6)), geometry[1]]
public setLayerVisibility(condition: {name: string|RegExp}, visible: boolean) {
if (!this.nehubaViewer) {
return false
const viewer = this.nehubaViewer.ngviewer
viewer.layerManager.managedLayers
.filter(l => this.filterLayers(l, condition))
.map(layer => layer.setVisible(visible))
return false
const removeLayer = (i) => (viewer.layerManager.removeManagedLayer(i), i.name)
return viewer.layerManager.managedLayers
const viewer = this.nehubaViewer.ngviewer
return Object.keys(layerObj)
/* if the layer exists, it will not be loaded */
!viewer.layerManager.getLayerByName(key))
public updateLayer(layerObj: INgLayerCtrl['update']) {
const viewer = this.nehubaViewer.ngviewer
for (const layerName in layerObj) {
const layer = viewer.layerManager.getLayerByName(layerName)
if (!layer) continue
const { visible } = layerObj[layerName]
layer.setVisible(!!visible)
}
}
Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => {
Array.from(this.multiNgIdsLabelIndexMap.get(ngId).keys()).forEach(idx => {
this.nehubaViewer.hideSegment(idx, {
})
})
this.nehubaViewer.showSegment(0, {
Xiao Gui
committed
this.hideAllSeg()
Array.from(this.multiNgIdsLabelIndexMap.keys()).forEach(ngId => {
Xiao Gui
committed
})
Xiao Gui
committed
this.hideAllSeg()
Xiao Gui
committed
/**
* TODO tobe deprecated
*/
if (typeof array[0] === 'number') {
const reduceFn: (acc: Map<string, number[]>, curr: string) => Map<string, number[]> = (acc, curr) => {
if (!exist) {
newMap.set(ngId, [Number(labelIndex)])
} else {
newMap.set(ngId, [...exist, Number(labelIndex)])
}
const newMap: Map<string, number[]> = array.reduce(reduceFn, new Map())
/**
* TODO
* ugh, ugly code. cleanify
*/
* sometimes, ngId still happends to be undefined
*/
newMap.forEach((segs, ngId) => {
this.nehubaViewer.hideSegment(0, {
})
segs.forEach(seg => {
this.nehubaViewer.showSegment(seg, {
private vec3(pos: [number, number, number]) {
return this.exportNehuba.vec3.fromValues(...pos)
this.log.warn('setNavigationState > this.nehubaViewer is not yet defined')
Xiao Gui
committed
const {
orientation,
perspectiveOrientation,
perspectiveZoom,
position,
positionReal,
Xiao Gui
committed
} = newViewerState
Xiao Gui
committed
this.nehubaViewer.ngviewer.perspectiveNavigationState.zoomFactor.restoreState(perspectiveZoom)
Xiao Gui
committed
this.nehubaViewer.ngviewer.navigationState.zoomFactor.restoreState(zoom)
Xiao Gui
committed
this.nehubaViewer.ngviewer.perspectiveNavigationState.pose.orientation.restoreState( perspectiveOrientation )
Xiao Gui
committed
this.nehubaViewer.ngviewer.navigationState.pose.orientation.restoreState( orientation )
this.nehubaViewer.setPosition( this.vec3(position) , positionReal ? true : false )
Xiao Gui
committed
}
this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 1, 0]), -amount / 4.0 * Math.PI / 180.0)
}
this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([1, 0, 0]), amount / 4.0 * Math.PI / 180.0)
}
this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 0, 1]), amount / 4.0 * Math.PI / 180.0)
}
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
public toggleOctantRemoval(flag?: boolean) {
const ctrl = this.nehubaViewer?.ngviewer?.showPerspectiveSliceViews
if (!ctrl) {
this.log.error(`toggleOctantRemoval failed. this.nehubaViewer.ngviewer?.showPerspectiveSliceViews returns falsy`)
return
}
const newVal = typeof flag === 'undefined'
? !ctrl.value
: flag
ctrl.restoreState(newVal)
if (this.landmarksLoaded) {
/**
* showPerspectSliceView -> ! meshTransparency
*/
this.setMeshTransparency(!newVal)
}
}
public setMeshTransparency(flag: boolean){
/**
* 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(flag ? 0.2 : 1.0)
}
}
}
/* isn't this layer specific? */
/* TODO this is layer specific. need a way to distinguish between different segmentation layers */
this._s2$ = this.nehubaViewer.mouseOver.segment
this.mouseOverSegment = segment
this.mouseOverLayer = { ...layer }
})
Xiao Gui
committed
if (this.initRegions && this.initRegions.length > 0) {
this.hideAllSeg()
this.showSegs(this.initRegions)
}
this.initNiftiLayers.forEach(layer => this.loadLayer(layer))
this.hideAllSeg()
}
this._s8$ = this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer }) => {
const map = this.multiNgIdsLabelIndexMap.get(name)
const region = map && map.get(segmentId)
this.mouseoverSegmentEmitter.emit({
layer,
segment: region,
Xiao Gui
committed
// nehubaViewer.navigationState.all emits every time a new layer is added or removed from the viewer
Xiao Gui
committed
this._s3$ = this.nehubaViewer.navigationState.all
.distinctUntilChanged((a, b) => {
const {
orientation: o1,
perspectiveOrientation: po1,
perspectiveZoom: pz1,
position: p1,
} = a
const {
orientation: o2,
perspectiveOrientation: po2,
perspectiveZoom: pz2,
position: p2,
} = b
return [0, 1, 2, 3].every(idx => o1[idx] === o2[idx]) &&
[0, 1, 2, 3].every(idx => po1[idx] === po2[idx]) &&
pz1 === pz2 &&
z1 === z2
})
.subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
Xiao Gui
committed
this.viewerState = {
orientation,
perspectiveOrientation,
perspectiveZoom,
zoom,
position,
Xiao Gui
committed
}
orientation : Array.from(orientation),
perspectiveOrientation : Array.from(perspectiveOrientation),
perspectiveZoom,
zoom,
Xiao Gui
committed
})
// TODO bug: mouseoverlandmarkemitter does not emit empty for VTK layer when user mouse click
this.ondestroySubscriptions.push(
this.nehubaViewer.mouseOver.layer
.filter(obj => obj.layer.name === NG_LANDMARK_LAYER_NAME)
)
this.ondestroySubscriptions.push(
this.nehubaViewer.mouseOver.layer
.filter(obj => obj.layer.name === NG_USER_LANDMARK_LAYER_NAME)
.subscribe(obj => this.mouseoverUserlandmarkEmitter.emit(obj.value)),
Xiao Gui
committed
this._s4$ = this.nehubaViewer.navigationState.position.inRealSpace
.subscribe(v => {
this.navPosReal = Array.from(v) as [number, number, number]
this.viewerPosInReal$.next(Array.from(v))
})
Xiao Gui
committed
this._s5$ = this.nehubaViewer.navigationState.position.inVoxels
.subscribe(v => {
this.navPosVoxel = Array.from(v) as [number, number, number]
this.viewerPosInVoxel$.next(Array.from(v))
})
Xiao Gui
committed
this._s6$ = this.nehubaViewer.mousePosition.inRealSpace
.subscribe(v => {
this.mousePosReal = Array.from(v) as [number, number, number]
this.mousePosInReal$.next(Array.from(v))
})
Xiao Gui
committed
this._s7$ = this.nehubaViewer.mousePosition.inVoxels
.subscribe(v => {
this.mousePosVoxel = Array.from(v) as [number, number, number]
this.mousePosInVoxel$.next(Array.from(v))
})