From 72c08147d45addd38f4d61cb28d147f425cdbc01 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 3 May 2017 00:27:28 -0300 Subject: [PATCH] OHIF-27: PlayClipController --- .../client/lib/classes/MetadataProvider.js | 10 +- .../imageThumbnail/imageThumbnail.js | 2 +- .../viewer/cineDialog/cineDialog.js | 1 + .../imageViewerViewport.js | 9 +- .../viewer/viewerMain/viewerMain.js | 10 +- Packages/ohif-viewerbase/client/index.js | 12 +- .../client/lib/classes/PlayClipManager.js | 306 ++++++++++++++++++ .../StudyLoadingListener.js} | 170 ++++++++-- .../client/lib/viewportUtils.js | 37 +-- 9 files changed, 489 insertions(+), 68 deletions(-) create mode 100644 Packages/ohif-viewerbase/client/lib/classes/PlayClipManager.js rename Packages/ohif-viewerbase/client/lib/{studyLoadingListener.js => classes/StudyLoadingListener.js} (57%) diff --git a/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js b/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js index 9f468560e..8e29b8597 100644 --- a/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js +++ b/Packages/ohif-cornerstone/client/lib/classes/MetadataProvider.js @@ -76,6 +76,10 @@ export class MetadataProvider { // the imagePlane object for easier use in the Viewer metadata.imagePlane = this.getImagePlane(instanceMetadata); + if (metadata.instance && !metadata.instance.multiframeMetadata) { + metadata.instance.multiframeMetadata = this.getMultiframeModuleMetadata(data); + } + // Add the metadata to the imageId lookup object this.metadataLookup.set(imageId, metadata); } @@ -110,8 +114,10 @@ export class MetadataProvider { if(image.data) { value = this.getFromDataSet(image.data, type, tag); - } else { + } else if(image.instance) { value = image.instance[attrName]; + } else { + value = image[attrName]; } return value == null ? defaultValue : value; @@ -208,7 +214,7 @@ export class MetadataProvider { imageMetadata.instance.frameTime = imageMetadata.instance.frameTime || this.getFromDataSet(image.data, 'string', 'x00181063'); imageMetadata.instance.frameTimeVector = imageMetadata.instance.frameTimeVector || this.getFromDataSet(image.data, 'string', 'x00181065'); - if ((image.data || image.instance) && !imageMetadata.instance.multiframeMetadata) { + if (!imageMetadata.instance.multiframeMetadata) { imageMetadata.instance.multiframeMetadata = this.getMultiframeModuleMetadata(image); } diff --git a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js index 5a857c915..e731e5dac 100644 --- a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js +++ b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js @@ -118,7 +118,7 @@ Template.imageThumbnail.helpers({ const instance = Template.instance(); const stack = instance.data.thumbnail.stack; const displaySetInstanceUid = stack.displaySetInstanceUid; - const progress = Session.get('DisplaySetProgress:' + displaySetInstanceUid); + const progress = Session.get('StackProgress:' + displaySetInstanceUid); const percentComplete = progress && progress.percentComplete; return percentComplete; diff --git a/Packages/ohif-viewerbase/client/components/viewer/cineDialog/cineDialog.js b/Packages/ohif-viewerbase/client/components/viewer/cineDialog/cineDialog.js index 373fc51bc..4d097a29f 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/cineDialog/cineDialog.js +++ b/Packages/ohif-viewerbase/client/components/viewer/cineDialog/cineDialog.js @@ -274,6 +274,7 @@ Template.cineDialog.events({ Template.cineDialog.helpers({ isPlaying() { + Session.get('UpdateCINE'); return viewportUtils.isPlaying(); }, diff --git a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index aa40ff897..683db6964 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -14,6 +14,7 @@ import { updateCrosshairsSynchronizer } from '../../../lib/updateCrosshairsSynch import { toolManager } from '../../../lib/toolManager'; import { updateOrientationMarkers } from '../../../lib/updateOrientationMarkers'; import { getInstanceClassDefaultViewport } from '../../../lib/instanceClassSpecificViewport'; +import { PlayClipManager } from '../../../lib/classes/PlayClipManager'; import { OHIFError } from '../../../lib/classes/OHIFError'; const allCornerstoneEvents = 'CornerstoneToolsMouseDown CornerstoneToolsMouseDownActivate ' + @@ -259,9 +260,12 @@ const loadDisplaySetIntoViewport = (data, templateData) => { cornerstoneTools.addToolState(element, 'playClip', cineToolData); + const playClipManager = PlayClipManager.getInstance(); + const playClipController = playClipManager.add(element, displaySet); + // Autoplay datasets that have framerates set if (multiframeMetadata && multiframeMetadata.isMultiframeImage && multiframeMetadata.averageFrameRate > 0) { - cornerstoneTools.playClip(element); + playClipController.play(); } // Enable mouse, mouseWheel, touch, and keyboard input on the element @@ -587,6 +591,9 @@ Template.imageViewerViewport.onDestroyed(function() { // for compatibility with other systems $element.trigger('OHIFDestroyedViewport'); + // Disable the PlayClipController for the current element + PlayClipManager.getInstance().remove(element); + // Disable the viewport element with Cornerstone // This also triggers the removal of the element from all available // synchronizers, such as the one used for reference lines. diff --git a/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js b/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js index 06cfd17e2..e28d3edf4 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js +++ b/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js @@ -9,6 +9,8 @@ import { hotkeyUtils } from '../../../lib/hotkeyUtils'; import { ResizeViewportManager } from '../../../lib/classes/ResizeViewportManager'; import { LayoutManager } from '../../../lib/classes/LayoutManager'; import { StudyPrefetcher } from '../../../lib/classes/StudyPrefetcher'; +import { StudyLoadingListener } from '../../../lib/classes/StudyLoadingListener'; +import { PlayClipManager } from '../../../lib/classes/PlayClipManager'; Meteor.startup(() => { window.ResizeViewportManager = window.ResizeViewportManager || new ResizeViewportManager(); @@ -36,9 +38,13 @@ Template.viewerMain.onRendered(() => { const { studies } = instance.data; const parentElement = instance.$('#layoutManagerTarget').get(0); const studyPrefetcher = StudyPrefetcher.getInstance(); + const studyLoadingListener = StudyLoadingListener.getInstance(); - OHIF.viewerbase.studyLoadingListener.clear(); - OHIF.viewerbase.studyLoadingListener.addStudies(studies); + studyLoadingListener.clear(); + studyLoadingListener.addStudies(studies); + + // Remove all PlayClipControllers from PlayClipManager + PlayClipManager.getInstance().clear(); OHIF.viewerbase.layoutManager = new LayoutManager(parentElement, studies); studyPrefetcher.setStudies(studies); diff --git a/Packages/ohif-viewerbase/client/index.js b/Packages/ohif-viewerbase/client/index.js index fdfaf0bd7..9da797bd2 100644 --- a/Packages/ohif-viewerbase/client/index.js +++ b/Packages/ohif-viewerbase/client/index.js @@ -157,10 +157,6 @@ Viewerbase.createStacks = createStacks; import { StackManager } from './lib/StackManager'; Viewerbase.stackManager = StackManager; -// studyLoadingListener -import { studyLoadingListener } from './lib/studyLoadingListener'; -Viewerbase.studyLoadingListener = studyLoadingListener; - // toolManager import { toolManager } from './lib/toolManager'; Viewerbase.toolManager = toolManager; @@ -204,6 +200,14 @@ Viewerbase.StudyPrefetcher = StudyPrefetcher; import { ResizeViewportManager } from './lib/classes/ResizeViewportManager'; Viewerbase.ResizeViewportManager = ResizeViewportManager; +// StudyLoadingListener +import { StudyLoadingListener } from './lib/classes/StudyLoadingListener'; +Viewerbase.StudyLoadingListener = StudyLoadingListener; + +// PlayClipManager +import { PlayClipManager } from './lib/classes/PlayClipManager'; +Viewerbase.PlayClipManager = PlayClipManager; + // StudyMetadata, SeriesMetadata, InstanceMetadata import { StudyMetadata } from './lib/classes/metadata/StudyMetadata'; import { SeriesMetadata } from './lib/classes/metadata/SeriesMetadata'; diff --git a/Packages/ohif-viewerbase/client/lib/classes/PlayClipManager.js b/Packages/ohif-viewerbase/client/lib/classes/PlayClipManager.js new file mode 100644 index 000000000..4530b35e3 --- /dev/null +++ b/Packages/ohif-viewerbase/client/lib/classes/PlayClipManager.js @@ -0,0 +1,306 @@ +import { $ } from 'meteor/jquery'; +import { Tracker } from 'meteor/tracker'; +import { Session } from 'meteor/session'; +import { OHIF } from 'meteor/ohif:core'; + +class PlayClipController { + constructor(element, displaySet) { + this._isPlaying = false; + this.element = element; + this.displaySet = displaySet; + this._playComputation = null; + } + + _getStackProgress() { + return Session.get('StackProgress:' + this.displaySet.displaySetInstanceUid); + } + + _getToolState(toolType) { + const toolState = cornerstoneTools.getToolState(this.element, toolType); + if (toolState && toolState.data) { + return toolState.data[0]; + } + } + + _countFramesByStatus(stackProgress, startIndex, endIndex, status) { + const framesStatus = stackProgress.framesStatus; + const framesCount = framesStatus.length; + let currentIndex = startIndex; + let count = 0; + + do { + if (framesStatus[currentIndex] === status) { + count++; + } + + if(currentIndex === endIndex) { + break; + } + + currentIndex = (currentIndex + 1) % framesCount; + } while (true) + + return count; + } + + _isLoopEnabled() { + const playClipToolState = this._getToolState('playClip'); + return playClipToolState && playClipToolState.loop; + } + + _getCurrentIndex() { + const stackToolState = this._getToolState('stack'); + return stackToolState.currentImageIdIndex || 0; + } + + _isLastFrame() { + const stackToolState = this._getToolState('stack'); + const currentIndex = (stackToolState && stackToolState.currentImageIdIndex) || 0; + const imagesCount = stackToolState ? stackToolState.imageIds.length : 0; + + return (imagesCount === 0) || (currentIndex === imagesCount - 1); + } + + _getFrameDistance(startIndex, endIndex) { + const framesCount = this.displaySet.numImageFrames; + return startIndex <= endIndex ? endIndex - startIndex : framesCount - (startIndex - endIndex); + } + + _getLastRelativeFrame(startFrame) { + const numImageFrames = this.displaySet.numImageFrames; + + if (this._isLoopEnabled()) { + // Easiest way to get the previous index without having to check if it's negative + return (startFrame + numImageFrames - 1) % numImageFrames; + } + + // Return the last index of the array + return numImageFrames - 1; + } + + isPlaying() { + return this._isPlaying; + } + + play() { + if (this._isPlaying) { + return; + } + + this._isPlaying = true; + + const playClipToolState = this._getToolState('playClip'); + const startIndex = this._getCurrentIndex(); + const endIndex = this._getLastRelativeFrame(startIndex); + const framesToPlay = this._getFrameDistance(startIndex, endIndex); + const playTimeRemaining = framesToPlay / playClipToolState.framesPerSecond; + + const play = playComputation => { + playComputation.stop(); + + try { + cornerstoneTools.playClip(this.element); + } catch(e) { + console.log(e); + } + } + + this._playComputation = Tracker.autorun(playComputation => { + const stackProgress = this._getStackProgress(); + + if (!stackProgress) { + return; + } else if (!stackProgress.multiFrame) { + play(playComputation); + return; + } + + const framesToLoad = this._countFramesByStatus(stackProgress, startIndex, endIndex, false); + const loadTimeRemaining = framesToLoad / stackProgress.framesPerSecond; + + if ((stackProgress.percentComplete === 100) || + (loadTimeRemaining < playTimeRemaining)) { + play(playComputation); + this._attachNewImageListener(); + } + }); + + // Update the UpdateCINE session property + Session.set('UpdateCINE', Random.id()); + } + + stop() { + if (!this._isPlaying) { + return; + } + + if (this._playComputation) { + this._playComputation.stop(); + this._playComputation = null; + } + + try { + cornerstoneTools.stopClip(this.element); + } catch(e) { + console.log(e); + } + + this._isPlaying = false; + this._detachNewImageListener(); + + // Update the UpdateCINE session property + Session.set('UpdateCINE', Random.id()); + } + + togglePlay() { + if(this._isPlaying) { + this.stop(); + } else { + this.play(); + } + } + + _isNextFrameLoaded() { + const stackProgress = this._getStackProgress(); + + if (!stackProgress || !stackProgress.framesStatus) { + return; + } + + const stackToolState = this._getToolState('stack'); + const currentImageIdIndex = stackToolState.currentImageIdIndex || 0; + const isLoopEnabled = this._isLoopEnabled(); + + let nextImageIndex = currentImageIdIndex + 1; + + if ((nextImageIndex >= stackToolState.imageIds.length) && isLoopEnabled) { + nextImageIndex = 0; + } + + return stackProgress.framesStatus && stackProgress.framesStatus[nextImageIndex]; + } + + _newImageEventHandler(e, eventData) { + if (this._isLastFrame() && !this._isLoopEnabled()) { + return this.stop(); + } else if (!this._isNextFrameLoaded()) { + // Stop the clip and play it again in order to wait for the next frames (buffer) + this.stop(); + this.play(); + } + } + + _attachNewImageListener() { + this._detachNewImageListener(); + $(this.element).on('CornerstoneNewImage.PlayClipManager', this._newImageEventHandler.bind(this)); + } + + _detachNewImageListener() { + $(this.element).off('CornerstoneNewImage.PlayClipManager'); + } + + destroy() { + this.stop(); + } +} + +class PlayClipManager { + constructor() { + this.elementControllers = []; + } + + add(element, displaySet) { + const playClipController = new PlayClipController(element, displaySet); + + // Remove any other PlayClipController for this `element` + this.remove(element); + + // Add the new PlayClipController for this `element` + this.elementControllers.push({ + element, + playClipController + }); + + return playClipController; + } + + remove (element) { + const index = this._indexOf(element); + + if (index === -1) { + return; + } + + const elementController = this.elementControllers[index]; + const playClipController = elementController.playClipController; + + playClipController.destroy(); + this.elementControllers.splice(index, 1); + } + + get (element) { + const index = this._indexOf(element); + + if (index >= 0) { + const elementController = this.elementControllers[index]; + return elementController.playClipController; + } + } + + _indexOf(element) { + const elementControllers = this.elementControllers; + const length = elementControllers.length; + + for(let i = 0; i < length; i++) { + if (elementControllers[i].element === element) { + return i; + } + } + + return -1; + } + + _forEach(callback) { + const elementControllers = this.elementControllers; + const length = elementControllers.length; + + for (let i = 0; i < length; i++) { + const elementControllers = this.elementControllers[i]; + const element = elementControllers.element; + const playClipController = elementControllers.playClipController; + + callback(element, playClipController); + } + } + + playAll() { + this._forEach((element, playClipController) => { + playClipController.play(); + }); + } + + stopAll() { + this._forEach((element, playClipController) => { + playClipController.stop(); + }); + } + + clear() { + this._forEach((element, playClipController) => { + playClipController.destroy(); + }); + + this.elementControllers = []; + } + + // Singleton + static getInstance() { + if (!PlayClipManager._instance) { + PlayClipManager._instance = new PlayClipManager(); + } + + return PlayClipManager._instance; + } +} + +export { PlayClipManager }; diff --git a/Packages/ohif-viewerbase/client/lib/studyLoadingListener.js b/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js similarity index 57% rename from Packages/ohif-viewerbase/client/lib/studyLoadingListener.js rename to Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js index 15d286187..9630580d6 100644 --- a/Packages/ohif-viewerbase/client/lib/studyLoadingListener.js +++ b/Packages/ohif-viewerbase/client/lib/classes/StudyLoadingListener.js @@ -2,15 +2,59 @@ import { $ } from 'meteor/jquery'; import { Session } from 'meteor/session'; class BaseLoadingListener { - constructor(stack) { + constructor(stack, options) { + options = options || {}; + this.id = BaseLoadingListener.getNewId(); this.stack = stack; this.startListening(); + this.statsItemsLimit = options.statsItemsLimit || 2; + this.stats = { + items: [], + total: 0, + elapsedTime: 0, + speed: 0 + }; + + // Register the start point to make it possible to calculate + // bytes/s or frames/s when the first byte or frame is received + this._addStatsData(0); + + // Update the progress before starting the download + // to make it possible to update the UI + this._updateProgress(); + } + + _addStatsData(value) { + const date = new Date(); + const stats = this.stats; + const items = stats.items; + const newItem = { + value, + date + }; + + items.push(newItem); + stats.total += newItem.value; + + // Remove items until it gets below the limit + while (items.length > this.statsItemsLimit) { + const item = items.shift(); + stats.total -= item.value; + } + + // Update the elapsedTime (seconds) based on first and last + // elements and recalculate the speed (bytes/s or frames/s) + if (items.length > 1) { + const oldestItem = items[0]; + stats.elapsedTime = (newItem.date.getTime() - oldestItem.date.getTime()) / 1000; + stats.speed = (stats.total - oldestItem.value) / stats.elapsedTime; + } } _getProgressSessionId() { const displaySetInstanceUid = this.stack.displaySetInstanceUid; - return 'DisplaySetProgress:' + displaySetInstanceUid; + return 'StackProgress:' + displaySetInstanceUid; } _clearSession() { @@ -44,6 +88,7 @@ class DICOMFileLoadingListener extends BaseLoadingListener { constructor(stack) { super(stack); this.dataSetUrls = this._getDataSetUrls(stack); + this._lastLoaded = 0; } _getImageLoadProgressEventName() { @@ -66,21 +111,32 @@ class DICOMFileLoadingListener extends BaseLoadingListener { _imageLoadProgressEventHandle(e, eventData) { const dataSetUrl = this._getDataSetUrl(eventData.imageId); + const bytesDiff = eventData.loaded - this._lastLoaded; if (!this.dataSetUrls.has(dataSetUrl)) { return; } - this._triggerProgress(eventData); + // Add the bytes downloaded to the stats + this._addStatsData(bytesDiff); + + // Update the download progress + this._updateProgress(eventData); + + // Cache the last eventData.loaded value + this._lastLoaded = eventData.loaded; } - _triggerProgress(eventData) { + _updateProgress(eventData) { const progressSessionId = this._getProgressSessionId(); + eventData = eventData || {}; Session.set(progressSessionId, { + multiFrame: false, percentComplete: eventData.percentComplete, bytesLoaded: eventData.loaded, - bytesTotal: eventData.total + bytesTotal: eventData.total, + bytesPerSecond: this.stats.speed }); } @@ -110,9 +166,35 @@ class DICOMFileLoadingListener extends BaseLoadingListener { class StackLoadingListener extends BaseLoadingListener { constructor(stack) { - super(stack); - this.remainingImageIds = stack.imageIds.slice(); - this.loadedImageIds = []; + super(stack, { statsItemsLimit: 20 }); + this.imageDataMap = this._convertImageIdsArrayToMap(stack.imageIds); + this.framesStatus = this._createArray(stack.imageIds.length, false); + this.loadedCount = 0; + } + + _convertImageIdsArrayToMap(imageIds) { + const imageIdsMap = new Map(); + + for (let i = 0; i < imageIds.length; i++) { + imageIdsMap.set(imageIds[i], { + index: i, + loaded: false + }); + } + + return imageIdsMap; + } + + _createArray(length, defaultValue) { + // `new Array(length)` is an anti-pattern in javascript because its + // funny API. Otherwise I would go for `new Array(length).fill(false)` + const array = new Array(); + + for(let i = 0; i < length; i++) { + array[i] = defaultValue; + } + + return array; } _getImageLoadedEventName() { @@ -143,42 +225,65 @@ class StackLoadingListener extends BaseLoadingListener { $(cornerstone).off(imageCachePromiseRemovedEventName); } - _moveImageId(imageId, srcImageIds, dstImageIds) { - const imageIdIndex = srcImageIds.indexOf(imageId); + _updateImageStatus(imageId, loaded) { + const imageData = this.imageDataMap.get(imageId); - if (imageIdIndex === -1) { + if (!imageData || (imageData.loaded === loaded)) { return; } - dstImageIds.push(imageId); - srcImageIds.splice(imageIdIndex, 1); - this._triggerProgress(); + // Add one more frame to the stats + if(loaded) { + this._addStatsData(1); + } + + imageData.loaded = loaded; + this.framesStatus[imageData.index] = loaded; + this.loadedCount += loaded ? 1 : -1; + this._updateProgress(); } _imageLoadedEventHandle(e, eventData) { const imageId = eventData.image.imageId; - this._moveImageId(imageId, this.remainingImageIds, this.loadedImageIds); + this._updateImageStatus(imageId, true); } _imageCachePromiseRemovedEventHandle(e, eventData) { const imageId = eventData.imageId; - this._moveImageId(imageId, this.loadedImageIds, this.remainingImageIds); + this._updateImageStatus(imageId, false); } - _triggerProgress() { - const loadedImagesCount = this.loadedImageIds.length; - const remainingImagesCount = this.remainingImageIds.length; - const totalImagesCount = loadedImagesCount + remainingImagesCount; - const percentComplete = Math.round(loadedImagesCount / totalImagesCount * 100); + _updateProgress() { + const totalFramesCount = this.stack.imageIds.length; + const loadedFramesCount = this.loadedCount; + const loadingFramesCount = totalFramesCount - loadedFramesCount; + const percentComplete = Math.round(loadedFramesCount / totalFramesCount * 100); const progressSessionId = this._getProgressSessionId(); Session.set(progressSessionId, { - totalImagesCount, - loadedImagesCount, - remainingImagesCount, - percentComplete + multiFrame: true, + totalFramesCount, + loadedFramesCount, + loadingFramesCount, + percentComplete, + framesPerSecond: this.stats.speed, + framesStatus: this.framesStatus }); } + + _logProgress() { + const totalFramesCount = this.stack.imageIds.length; + const displaySetInstanceUid = this.stack.displaySetInstanceUid; + let progressBar = '['; + + for(let i = 0; i < totalFramesCount; i++) { + const ch = this.framesStatus[i] ? '|' : '.'; + progressBar += `${ch}`; + } + + progressBar += ']'; + console.log(`${displaySetInstanceUid}: ${progressBar}`); + } } class StudyLoadingListener { @@ -223,6 +328,7 @@ class StudyLoadingListener { for (let i = 0; i < length; i++) { const displaySetInstanceUid = displaySetInstanceUids[i]; const displaySet = this.listeners[displaySetInstanceUid]; + displaySet.destroy(); } @@ -244,9 +350,15 @@ class StudyLoadingListener { const colonIndex = imageId.indexOf(':'); return imageId.substring(0, colonIndex); } + + // Singleton + static getInstance() { + if(!StudyLoadingListener._instance) { + StudyLoadingListener._instance = new StudyLoadingListener(); + } + + return StudyLoadingListener._instance; + } } -// Singleton -const studyLoadingListener = new StudyLoadingListener(); - -export { studyLoadingListener }; +export { StudyLoadingListener }; diff --git a/Packages/ohif-viewerbase/client/lib/viewportUtils.js b/Packages/ohif-viewerbase/client/lib/viewportUtils.js index e9edc97c4..2ee7382fd 100644 --- a/Packages/ohif-viewerbase/client/lib/viewportUtils.js +++ b/Packages/ohif-viewerbase/client/lib/viewportUtils.js @@ -6,6 +6,7 @@ import { _ } from 'meteor/underscore'; import { OHIF } from 'meteor/ohif:core'; import { updateOrientationMarkers } from './updateOrientationMarkers'; import { getInstanceClassDefaultViewport } from './instanceClassSpecificViewport'; +import { PlayClipManager } from './classes/PlayClipManager'; /** * Get a cornerstone enabledElement for a DOM Element @@ -195,16 +196,12 @@ const toggleDialog = element => { const toggleCinePlay = () => { // Get the active viewport element const element = getActiveViewportElement(); + const playClipManager = PlayClipManager.getInstance(); + const playClipController = playClipManager.get(element); - // Check if it's playing the clip to toggle it - if (isPlaying()) { - cornerstoneTools.stopClip(element); - } else { - cornerstoneTools.playClip(element); + if (playClipController) { + playClipController.togglePlay(); } - - // Update the UpdateCINE session property - Session.set('UpdateCINE', Random.id()); }; // Show/hide the CINE dialog @@ -219,29 +216,11 @@ const isPlaying = () => { Session.get('UpdateCINE'); Session.get('LayoutManagerUpdated'); - // Get the viewport element and its current playClip tool state const element = getActiveViewportElement(); - // Empty Elements throws cornerstore exception - if (!element || !$(element).find('canvas').length) { - return; - } + const playClipManager = PlayClipManager.getInstance(); + const playClipController = playClipManager.get(element); - const toolState = cornerstoneTools.getToolState(element, 'playClip'); - - // Stop here if the tool state is not defined yet - if (!toolState) { - return false; - } - - // Get the clip state - const clipState = toolState.data[0]; - - if(clipState) { - // Return true if the clip is playing - return !_.isUndefined(clipState.intervalId); - } - - return false; + return playClipController && playClipController.isPlaying(); }; // Check if a study has multiple frames