From adb174540e4fa3734740bca4794c61e7b606bf5e Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Thu, 26 Sep 2019 16:42:40 +0200 Subject: [PATCH] bugfix: fix loading mesh unresponsiveness --- .../atlasViewer.workerService.service.ts | 7 +- .../nehubaViewer/nehubaViewer.component.ts | 157 +++++++----------- src/util/worker.js | 40 ----- 3 files changed, 67 insertions(+), 137 deletions(-) diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts index aefcfa162..6f6d35286 100644 --- a/src/atlasViewer/atlasViewer.workerService.service.ts +++ b/src/atlasViewer/atlasViewer.workerService.service.ts @@ -1,5 +1,8 @@ import { Injectable } from "@angular/core"; +/* telling webpack to pack the worker file */ +import '../util/worker.js' + /** * export the worker, so that services that does not require dependency injection can import the worker */ @@ -11,8 +14,4 @@ export const worker = new Worker('worker.js') export class AtlasWorkerService{ public worker = worker - public safeMeshSet : Map<string, Set<number>> = new Map() } - -/* telling webpack to pack the worker file */ -require('../util/worker.js') \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 7a5ea161b..1e0a4501c 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -1,7 +1,7 @@ -import { Component, OnDestroy, Output, EventEmitter, ElementRef, NgZone, Renderer2 } from "@angular/core"; -import { fromEvent, interval, Subscription } from 'rxjs' +import { Component, OnDestroy, Output, EventEmitter, ElementRef, NgZone, Renderer2, OnInit } from "@angular/core"; +import { fromEvent, Subscription, Subject } from 'rxjs' import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; -import { buffer, map, filter, debounceTime } from "rxjs/operators"; +import { map, filter, debounceTime, scan } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { takeOnePipe } from "../nehubaContainer.component"; import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; @@ -11,6 +11,28 @@ import { getNgIdLabelIndexFromId } from "src/services/stateStore.service"; import 'third_party/export_nehuba/main.bundle.js' import 'third_party/export_nehuba/chunk_worker.bundle.js' +interface LayerLabelIndex { + layer: { + name: string + } + + labelIndicies: number[] +} + +const scanFn : (acc: LayerLabelIndex[], curr: LayerLabelIndex) => LayerLabelIndex[] = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => { + const { layer } = curr + const { name } = layer + const foundIndex = acc.findIndex(({ layer }) => layer.name === name) + debugger + if (foundIndex < 0) return acc.concat(curr) + else return acc.map((item, idx) => idx === foundIndex + ? { + ...item, + labelIndicies: [...new Set([...item.labelIndicies, ...curr.labelIndicies])] + } + : item) +} + /** * no selector is needed, as currently, nehubaviewer is created dynamically */ @@ -21,7 +43,9 @@ import 'third_party/export_nehuba/chunk_worker.bundle.js' ] }) -export class NehubaViewerUnit implements OnDestroy{ +export class NehubaViewerUnit implements OnInit, OnDestroy{ + + private subscriptions: Subscription[] = [] @Output() nehubaReady: EventEmitter<null> = new EventEmitter() @Output() layersChanged: EventEmitter<null> = new EventEmitter() @@ -193,78 +217,12 @@ export class NehubaViewerUnit implements OnDestroy{ this.loadLayer(_) }) ) - - this.ondestroySubscriptions.push( - - fromEvent(this.workerService.worker,'message').pipe( - filter((message:any) => { - - if(!message){ - // console.error('worker response message is undefined', message) - return false - } - if(!message.data){ - // console.error('worker response message.data is undefined', message.data) - return false - } - if(message.data.type !== 'CHECKED_MESH'){ - /* worker responded with not checked mesh, no need to act */ - return false - } - return true - }), - map(e => e.data), - buffer(interval(1000)), - map(arr => arr.reduce((acc:Map<string,Set<number>>,curr)=> { - - const newMap = new Map(acc) - const set = newMap.get(curr.baseUrl) - if(set){ - set.add(curr.checkedIndex) - }else{ - newMap.set(curr.baseUrl,new Set([curr.checkedIndex])) - } - return newMap - }, new Map())) - ).subscribe(map => { - - Array.from(map).forEach(item => { - const baseUrl : string = item[0] - const set : Set<number> = item[1] - - /* validation passed, add to safeMeshSet */ - const oldset = this.workerService.safeMeshSet.get(baseUrl) - if(oldset){ - this.workerService.safeMeshSet.set(baseUrl, new Set([...oldset, ...set])) - }else{ - this.workerService.safeMeshSet.set(baseUrl, new Set([...set])) - } - - /* if the active parcellation is the current parcellation, load the said mesh */ - const baseUrlIsInLoadedBaseUrlList = new Set([...this._baseUrls]).has(baseUrl) - const baseUrlParcellationId = this._baseUrlToParcellationIdMap.get(baseUrl) - if( baseUrlIsInLoadedBaseUrlList && baseUrlParcellationId){ - this.nehubaViewer.setMeshesToLoad([...this.workerService.safeMeshSet.get(baseUrl)], { - name : baseUrlParcellationId - }) - } - }) - }) - ) } private _baseUrlToParcellationIdMap:Map<string, string> = new Map() private _baseUrls: string[] = [] - get numMeshesToBeLoaded():number{ - if(!this._baseUrls || this._baseUrls.length === 0) - return 0 - - return this._baseUrls.reduce((acc, curr) => { - const mappedSet = this.workerService.safeMeshSet.get(curr) - return acc + ((mappedSet && mappedSet.size) || 0) - } ,0) - } + public numMeshesToBeLoaded: number = 0 public applyPerformanceConfig ({ gpuLimit }: Partial<ViewerConfiguration>) { if (gpuLimit && this.nehubaViewer) { @@ -348,6 +306,14 @@ export class NehubaViewerUnit implements OnDestroy{ this._multiNgIdColorMap = val } + private loadMeshes$: Subject<{labelIndicies: number[], layer: { name: string }}> = new Subject() + private loadMeshes(labelIndicies: number[], { name }){ + this.loadMeshes$.next({ + labelIndicies, + layer: { name } + }) + } + public mouseOverSegment: number | null public mouseOverLayer: {name:string,url:string}| null @@ -411,7 +377,28 @@ export class NehubaViewerUnit implements OnDestroy{ ) } + ngOnInit(){ + this.subscriptions.push( + this.loadMeshes$.pipe( + scan(scanFn, []) + ).subscribe(layersLabelIndex => { + let totalMeshes = 0 + for (const layerLayerIndex of layersLabelIndex){ + const { layer, labelIndicies } = layerLayerIndex + totalMeshes += labelIndicies.length + this.nehubaViewer.setMeshesToLoad(labelIndicies, layer) + } + // TODO implement total mesh to be loaded and mesh loading UI + // this.numMeshesToBeLoaded = totalMeshes + }) + ) + } + ngOnDestroy(){ + while(this.subscriptions.length > 0) { + this.subscriptions.pop().unsubscribe() + } + this._s$.forEach(_s$=>{ if(_s$) _s$.unsubscribe() }) @@ -825,28 +812,12 @@ export class NehubaViewerUnit implements OnDestroy{ this._baseUrls.push(baseUrl) this._baseUrlToParcellationIdMap.set(baseUrl, id) - /* load meshes */ - /* TODO could be a bug where user loads new parcellation, but before the worker could completely populate the list */ - const set = this.workerService.safeMeshSet.get(baseUrl) - if(set){ - this.nehubaViewer.setMeshesToLoad([...set],{ - name : id - }) - }else{ - if(newlayer){ - this.zone.runOutsideAngular(() => - this.workerService.worker.postMessage({ - type : 'CHECK_MESHES', - parcellationId : id, - baseUrl, - indices : [ - ...Array.from(this.multiNgIdsLabelIndexMap.get(id).keys()), - ...getAuxilliaryLabelIndices() - ] - }) - ) - } - } + const indicies = [ + ...Array.from(this.multiNgIdsLabelIndexMap.get(id).keys()), + ...getAuxilliaryLabelIndices() + ] + + this.loadMeshes(indicies, { name: id }) }) const obj = Array.from(this.multiNgIdsLabelIndexMap.keys()).map(ngId => { diff --git a/src/util/worker.js b/src/util/worker.js index 44eeeeb51..f5313880e 100644 --- a/src/util/worker.js +++ b/src/util/worker.js @@ -1,5 +1,4 @@ const validTypes = [ - 'CHECK_MESHES', 'GET_LANDMARKS_VTK', 'GET_USERLANDMARKS_VTK', 'BUILD_REGION_SELECTION_TREE', @@ -7,48 +6,12 @@ const validTypes = [ ] const validOutType = [ - 'CHECKED_MESH', 'ASSEMBLED_LANDMARKS_VTK', 'ASSEMBLED_USERLANDMARKS_VTK', 'RETURN_REBUILT_REGION_SELECTION_TREE', 'UPDATE_PARCELLATION_REGIONS' ] -const checkMeshes = (action) => { - - /* filtering now done on the angular level */ - const baseUrl = action.baseUrl - fetch(`${baseUrl}/info`) - .then(res => res.json()) - .then(({mesh}) => { - if (mesh) - return mesh - else - throw new Error('mesh does not exist') - }) - .then(meshPath => action.indices.forEach(index => { - fetch(`${baseUrl}/${meshPath}/${index}:0`) - .then(res => res.json()) - .then(json => { - /* the perspectiveEvent only counts json that has fragments as a part of meshLoaded */ - if(json.fragments && json.fragments.constructor === Array && json.fragments.length > 0){ - postMessage({ - type: 'CHECKED_MESH', - parcellationId: action.parcellationId, - checkedIndex: index, - baseUrl - }) - } - }) - .catch(error => { - /* no cors error is also caught here, but also printed to the console */ - }) - })) - .catch(error => { - // console.error('parsing info json error', error) - }) -} - const vtkHeader = `# vtk DataFile Version 2.0 Created by worker thread at https://github.com/HumanBrainProject/interactive-viewer ASCII @@ -321,9 +284,6 @@ onmessage = (message) => { if(validTypes.findIndex(type => type === message.data.type) >= 0){ switch(message.data.type){ - case 'CHECK_MESHES': - checkMeshes(message.data) - return case 'GET_LANDMARKS_VTK': getLandmarksVtk(message.data) return -- GitLab