diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index b9b9c9081..9cdc97fa1 100644 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -36,3 +36,4 @@ random reactive-var reactive-dict lesiontracker +practicalmeteor:loglevel diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 4a556777f..d00252c84 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -16,6 +16,7 @@ caching-compiler@1.0.0 caching-html-compiler@1.0.2 callback-hook@1.0.4 check@1.1.0 +coffeescript@1.0.11 cornerstone@0.0.1 ddp@1.2.2 ddp-client@1.2.1 @@ -67,6 +68,8 @@ npm-bcrypt@0.7.8_2 npm-mongo@1.4.39_1 observe-sequence@1.0.7 ordered-dict@1.0.4 +practicalmeteor:chai@1.9.2_3 +practicalmeteor:loglevel@1.1.0_3 promise@0.5.1 random@1.0.5 rate-limit@1.0.0 diff --git a/LesionTracker/client/components/viewer.html b/LesionTracker/client/components/viewer.html index 8530698dc..0bc88f89a 100644 --- a/LesionTracker/client/components/viewer.html +++ b/LesionTracker/client/components/viewer.html @@ -1,10 +1,10 @@ \ No newline at end of file diff --git a/LesionTracker/client/components/viewer.js b/LesionTracker/client/components/viewer.js index f72198aa7..a79b498ad 100644 --- a/LesionTracker/client/components/viewer.js +++ b/LesionTracker/client/components/viewer.js @@ -1,4 +1,6 @@ function resizeViewports() { + log.info("viewer resizeViewports"); + // Handle resizing of image viewer viewports // For some reason, this seems to need to be on // another delay, or the resizing won't work properly @@ -15,7 +17,8 @@ function resizeViewports() { } Template.viewer.onCreated(function() { - console.log("Image Viewer onCreated"); + log.info("viewer onCreated"); + OHIF = { viewer: {} }; @@ -55,35 +58,35 @@ Template.viewer.onCreated(function() { }; } - if (this.data.activeViewport === undefined) { - this.data.activeViewport = new ReactiveVar(0); - } - if (this.data.viewportRows === undefined) { - this.data.viewportRows = new ReactiveVar(1); - } - if (this.data.viewportColumns === undefined) { - this.data.viewportColumns = new ReactiveVar(2); - } - var contentId = this.data.contentId; - // Update the viewer data object - ViewerData[contentId].viewportColumns = this.data.viewportColumns; - ViewerData[contentId].viewportRows = this.data.viewportRows; - ViewerData[contentId].activeViewport = this.data.activeViewport; - Session.set('ViewerData', ViewerData); + if (ViewerData[contentId].loadedSeriesData) { + log.info('Reloading previous loadedSeriesData'); + + OHIF.viewer.loadedSeriesData = ViewerData[contentId].loadedSeriesData; - if (ViewerData[contentId].viewer) { - OHIF.viewer = ViewerData[contentId].viewer; } else { - OHIF.viewer.imageViewerLoadedSeriesDictionary = {}; - ViewerData[contentId].viewer = OHIF.viewer; + log.info('Setting default ViewerData'); + OHIF.viewer.loadedSeriesData = {}; + + ViewerData[contentId].loadedSeriesData = OHIF.viewer.loadedSeriesData; + + // Update the viewer data object + ViewerData[contentId].viewportColumns = 2; + ViewerData[contentId].viewportRows = 1; + ViewerData[contentId].activeViewport = 0; + Session.set('ViewerData', ViewerData); } + Session.set('activeViewport', ViewerData[contentId].activeViewport || 0); + + Session.set("studies", this.data.studies); + OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.updateImageSynchronizer); }); Template.viewer.onDestroyed(function() { + log.info("onDestroyed"); OHIF.viewer.updateImageSynchronizer.destroy(); }); diff --git a/LesionTracker/client/components/viewerMain/viewerMain.html b/LesionTracker/client/components/viewerMain/viewerMain.html index e7bdf403f..6bf8bf680 100644 --- a/LesionTracker/client/components/viewerMain/viewerMain.html +++ b/LesionTracker/client/components/viewerMain/viewerMain.html @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/LesionTracker/client/components/viewerMain/viewerMain.js b/LesionTracker/client/components/viewerMain/viewerMain.js index 2062952f5..7db405613 100644 --- a/LesionTracker/client/components/viewerMain/viewerMain.js +++ b/LesionTracker/client/components/viewerMain/viewerMain.js @@ -1,7 +1,4 @@ Template.viewerMain.helpers({ - studies : function() { - return this.studies; - }, 'toolbarOptions': function() { var toolbarOptions = {}; diff --git a/LesionTracker/client/log.js b/LesionTracker/client/log.js new file mode 100644 index 000000000..f4e50c3e4 --- /dev/null +++ b/LesionTracker/client/log.js @@ -0,0 +1,3 @@ +// Create application logger using loglevel +// https://atmospherejs.com/spacejamio/loglevel +log = loglevel.createAppLogger('LesionTracker', defaultLevel = 'info'); diff --git a/LesionTracker/client/route.js b/LesionTracker/client/route.js deleted file mode 100644 index 2fa8c2777..000000000 --- a/LesionTracker/client/route.js +++ /dev/null @@ -1,49 +0,0 @@ -Router.configure({ - layoutTemplate: 'layoutLesionTracker', - //loadingTemplate: '', - notFoundTemplate: 'notFound' -}); - -Router.route('/', function () { - this.render('worklist', {}); -}); - -tabs = new Meteor.Collection(null); -Session.setDefault('ViewerData', {}); -Session.setDefault('ViewerDataUpdated', Random.id()); - -Object.keys(ViewerData).forEach(function(contentId) { - var tabData = ViewerData[contentId]; - var data = { - title: tabData.title, - contentid: tabData.contentId, - }; - tabs.insert(data); -}); - - -Router.route('/viewer/:_id', { - layoutTemplate: 'layout', - name: 'viewer', - onBeforeAction: function() { - var self = this; - - Meteor.call('GetStudyMetadata', this.params._id, function(error, study) { - sortStudy(study); - - var title = study.seriesList[0].instances[0].patientName; - var contentid = generateUUID(); - - var data = { - title: title, - contentid: contentid, - }; - tabs.insert(data); - - data.studies = [study]; - - self.render('worklist'); - Session.set('OpenNewTabEvent', data); - }); - } -}); \ No newline at end of file diff --git a/LesionTracker/client/routes.js b/LesionTracker/client/routes.js new file mode 100644 index 000000000..422eeb95c --- /dev/null +++ b/LesionTracker/client/routes.js @@ -0,0 +1,45 @@ +tabs = new Meteor.Collection(null); +Session.setDefault('ViewerData', {}); + +// Re-add any tab data saved in the Session +Object.keys(ViewerData).forEach(function(contentId) { + var tabData = ViewerData[contentId]; + var data = { + title: tabData.title, + contentid: tabData.contentid, + }; + tabs.insert(data); +}); + +Router.configure({ + layoutTemplate: 'layout', + loadingTemplate: 'layout', + notFoundTemplate: 'notFound' +}); + +Router.onBeforeAction('loading'); + +Router.route('/', function () { + this.render('worklist'); +}); + + +Router.route('/viewer/:_id', { + layoutTemplate: 'layout', + name: 'viewer', + onBeforeAction: function() { + log.info('Router GetStudyMetadata'); + + var studyInstanceUid = this.params._id; + + // Check if this study is already loaded in a tab + // If it is, stop here so we don't keep adding tabs on hot-code reloads + var tab = tabs.find({'studyInstanceUid' : studyInstanceUid}).fetch(); + if (tab) { + return; + } + + this.render('worklist'); + openNewTab(studyInstanceUid); + } +}); \ No newline at end of file diff --git a/OHIFViewer/.meteor/packages b/OHIFViewer/.meteor/packages index 2dbded030..78786adc7 100644 --- a/OHIFViewer/.meteor/packages +++ b/OHIFViewer/.meteor/packages @@ -33,3 +33,4 @@ dicomweb viewerbase worklist reactive-var +practicalmeteor:loglevel diff --git a/OHIFViewer/.meteor/versions b/OHIFViewer/.meteor/versions index 62a4a53a7..86aaf5f24 100644 --- a/OHIFViewer/.meteor/versions +++ b/OHIFViewer/.meteor/versions @@ -15,6 +15,7 @@ caching-compiler@1.0.0 caching-html-compiler@1.0.2 callback-hook@1.0.4 check@1.1.0 +coffeescript@1.0.11 cornerstone@0.0.1 ddp@1.2.2 ddp-client@1.2.1 @@ -64,6 +65,8 @@ npm-bcrypt@0.7.8_2 npm-mongo@1.4.39_1 observe-sequence@1.0.7 ordered-dict@1.0.4 +practicalmeteor:chai@1.9.2_3 +practicalmeteor:loglevel@1.1.0_3 promise@0.5.1 random@1.0.5 rate-limit@1.0.0 diff --git a/OHIFViewer/client/components/viewer/viewer.html b/OHIFViewer/client/components/viewer/viewer.html index 173d21833..6e7b46e82 100644 --- a/OHIFViewer/client/components/viewer/viewer.html +++ b/OHIFViewer/client/components/viewer/viewer.html @@ -1,6 +1,9 @@ \ No newline at end of file diff --git a/OHIFViewer/client/components/viewer/viewer.js b/OHIFViewer/client/components/viewer/viewer.js index ae0c0b139..058215c73 100644 --- a/OHIFViewer/client/components/viewer/viewer.js +++ b/OHIFViewer/client/components/viewer/viewer.js @@ -1,4 +1,6 @@ function resizeViewports() { + log.info("viewer resizeViewports"); + // Handle resizing of image viewer viewports // For some reason, this seems to need to be on // another delay, or the resizing won't work properly @@ -15,7 +17,8 @@ function resizeViewports() { } Template.viewer.onCreated(function() { - console.log("Image Viewer onCreated"); + log.info("viewer onCreated"); + OHIF = { viewer: {} }; @@ -55,35 +58,35 @@ Template.viewer.onCreated(function() { }; } - if (this.data.activeViewport === undefined) { - this.data.activeViewport = new ReactiveVar(0); - } - if (this.data.viewportRows === undefined) { - this.data.viewportRows = new ReactiveVar(1); - } - if (this.data.viewportColumns === undefined) { - this.data.viewportColumns = new ReactiveVar(1); - } - var contentId = this.data.contentId; - // Update the viewer data object - ViewerData[contentId].viewportColumns = this.data.viewportColumns; - ViewerData[contentId].viewportRows = this.data.viewportRows; - ViewerData[contentId].activeViewport = this.data.activeViewport; - Session.set('ViewerData', ViewerData); + if (ViewerData[contentId].loadedSeriesData) { + log.info('Reloading previous loadedSeriesData'); + + OHIF.viewer.loadedSeriesData = ViewerData[contentId].loadedSeriesData; - if (ViewerData[contentId].viewer) { - OHIF.viewer = ViewerData[contentId].viewer; } else { - OHIF.viewer.imageViewerLoadedSeriesDictionary = {}; - ViewerData[contentId].viewer = OHIF.viewer; + log.info('Setting default ViewerData'); + OHIF.viewer.loadedSeriesData = {}; + + ViewerData[contentId].loadedSeriesData = OHIF.viewer.loadedSeriesData; + + // Update the viewer data object + ViewerData[contentId].viewportColumns = 1; + ViewerData[contentId].viewportRows = 1; + ViewerData[contentId].activeViewport = 0; + Session.set('ViewerData', ViewerData); } + Session.set('activeViewport', ViewerData[contentId].activeViewport || 0); + + Session.set("studies", this.data.studies); + OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.updateImageSynchronizer); }); Template.viewer.onDestroyed(function() { + log.info("onDestroyed"); OHIF.viewer.updateImageSynchronizer.destroy(); }); diff --git a/OHIFViewer/client/components/viewer/viewer.styl b/OHIFViewer/client/components/viewer/viewer.styl index 854b359f5..4a1c1f8df 100644 --- a/OHIFViewer/client/components/viewer/viewer.styl +++ b/OHIFViewer/client/components/viewer/viewer.styl @@ -12,4 +12,10 @@ user-select: none .studyBrowser - width: auto \ No newline at end of file + width: auto + +.viewerMain + float: left + background-color: gray + height: 100% + width: calc(100% - 120px) \ No newline at end of file diff --git a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.html b/OHIFViewer/client/components/viewer/viewerMain/viewerMain.html deleted file mode 100644 index eb39a71c8..000000000 --- a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.js b/OHIFViewer/client/components/viewer/viewerMain/viewerMain.js deleted file mode 100644 index 260bf1cc9..000000000 --- a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.js +++ /dev/null @@ -1,5 +0,0 @@ -Template.viewerMain.helpers({ - studies : function() { - return this.studies; - } -}); \ No newline at end of file diff --git a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.styl b/OHIFViewer/client/components/viewer/viewerMain/viewerMain.styl deleted file mode 100644 index 7b269b4cd..000000000 --- a/OHIFViewer/client/components/viewer/viewerMain/viewerMain.styl +++ /dev/null @@ -1,5 +0,0 @@ -.viewerMain - float: left - background-color: gray - height: 100% - width: calc(100% - 120px) \ No newline at end of file diff --git a/OHIFViewer/client/log.js b/OHIFViewer/client/log.js new file mode 100644 index 000000000..1f567f0c5 --- /dev/null +++ b/OHIFViewer/client/log.js @@ -0,0 +1,3 @@ +// Create application logger using loglevel +// https://atmospherejs.com/spacejamio/loglevel +log = loglevel.createAppLogger('OHIFViewer', defaultLevel = 'info'); diff --git a/OHIFViewer/client/routes.js b/OHIFViewer/client/routes.js index 84ae25bfe..422eeb95c 100644 --- a/OHIFViewer/client/routes.js +++ b/OHIFViewer/client/routes.js @@ -1,49 +1,45 @@ -Router.configure({ - layoutTemplate: 'layout', - //loadingTemplate: '', - notFoundTemplate: 'notFound' -}); - -Router.route('/', function () { - this.render('worklist', {}); -}); - tabs = new Meteor.Collection(null); Session.setDefault('ViewerData', {}); -Session.setDefault('ViewerDataUpdated', Random.id()); +// Re-add any tab data saved in the Session Object.keys(ViewerData).forEach(function(contentId) { var tabData = ViewerData[contentId]; var data = { title: tabData.title, - contentid: tabData.contentId, + contentid: tabData.contentid, }; tabs.insert(data); }); +Router.configure({ + layoutTemplate: 'layout', + loadingTemplate: 'layout', + notFoundTemplate: 'notFound' +}); + +Router.onBeforeAction('loading'); + +Router.route('/', function () { + this.render('worklist'); +}); + Router.route('/viewer/:_id', { layoutTemplate: 'layout', name: 'viewer', onBeforeAction: function() { - var self = this; + log.info('Router GetStudyMetadata'); - Meteor.call('GetStudyMetadata', this.params._id, function(error, study) { - sortStudy(study); + var studyInstanceUid = this.params._id; + + // Check if this study is already loaded in a tab + // If it is, stop here so we don't keep adding tabs on hot-code reloads + var tab = tabs.find({'studyInstanceUid' : studyInstanceUid}).fetch(); + if (tab) { + return; + } - var title = study.seriesList[0].instances[0].patientName; - var contentid = generateUUID(); - - var data = { - title: title, - contentid: contentid, - }; - tabs.insert(data); - - data.studies = [study]; - - self.render('worklist'); - Session.set('OpenNewTabEvent', data); - }); + this.render('worklist'); + openNewTab(studyInstanceUid); } }); \ No newline at end of file diff --git a/Packages/cornerstone/client/cornerstone.js b/Packages/cornerstone/client/cornerstone.js index b68821d71..f2cca6c2d 100644 --- a/Packages/cornerstone/client/cornerstone.js +++ b/Packages/cornerstone/client/cornerstone.js @@ -2148,7 +2148,7 @@ if(typeof cornerstone === 'undefined'){ function initShaders() { for (var id in cornerstone.webGL.shaders) { - console.log("WEBGL: Loading shader", id); + //console.log("WEBGL: Loading shader", id); var shader = cornerstone.webGL.shaders[ id ]; shader.attributes = {}; shader.uniforms = {}; @@ -2168,13 +2168,13 @@ if(typeof cornerstone === 'undefined'){ function initRenderer() { if (cornerstone.webGL.isWebGLInitialized === true) { - console.log("WEBGL Renderer already initialized"); + //console.log("WEBGL Renderer already initialized"); return; } if ( initWebGL( renderCanvas ) ) { initBuffers(); initShaders(); - console.log("WEBGL Renderer initialized!"); + //console.log("WEBGL Renderer initialized!"); cornerstone.webGL.isWebGLInitialized = true; } } diff --git a/Packages/lesiontracker/components/lesionTable/lesionTable.js b/Packages/lesiontracker/components/lesionTable/lesionTable.js index a219fdcb4..5a1d72bce 100644 --- a/Packages/lesiontracker/components/lesionTable/lesionTable.js +++ b/Packages/lesiontracker/components/lesionTable/lesionTable.js @@ -3,12 +3,22 @@ TabsTimepoints = new Meteor.Collection(null); Template.lesionTable.helpers({ 'measurement': function() { +<<<<<<< HEAD var contentId = Session.get("activeContentId"); return Measurements.find({contentId: contentId}); }, 'tabTimepoints': function() { var contentId = Session.get("activeContentId"); return TabsTimepoints.find({contentId: contentId}); +======= + var contentId = this.contentId; + console.log(Measurements.find({contentId: contentId})); + return Measurements.find({contentId: contentId}); + }, + 'timepointNames': function() { + var contentId = this.contentId; + return Template.instance().timepointNamesDictionary.get(contentId); +>>>>>>> Updates to improve reactivity, session storage, logging }, 'lesionData': function() { var array = []; @@ -21,19 +31,16 @@ Template.lesionTable.helpers({ }); Template.lesionTable.onRendered(function() { + var contentId = this.data.contentId; + var viewportColumns = ViewerData[contentId].viewportColumns; + var viewportRows = ViewerData[contentId].viewportRows; - var cols = Template.instance().data.viewportColumns.curValue; - var rows = Template.instance().data.viewportRows.curValue; - - var totalViewports = cols * rows; - - var contentId = Session.get('activeContentId'); - var timepointsArray = []; + var totalViewports = viewportColumns * viewportRows; + for(var i=0; i< totalViewports; i++) { - - var timepointID = contentId.toString()+ i.toString(); + var timepointID = contentId.toString() + i.toString(); var timepointName = "Baseline"; - if(i > 0) { + if (i > 0) { timepointName = "Follow Up "+i; } var timepointObject = {timepointID: timepointID, timepointName: timepointName}; @@ -43,7 +50,7 @@ Template.lesionTable.onRendered(function() { // Prevent duplicate data when onRendered is called var tabTimepoint = TabsTimepoints.find({contentId: contentId}).fetch(); - if (tabTimepoint != undefined && tabTimepoint.length > 0) { + if (tabTimepoint !== undefined && tabTimepoint.length > 0) { // Update timepoints TabsTimepoints.update( { contentId: contentId}, diff --git a/Packages/viewerbase/components/studyBrowser/studyBrowser/studyBrowser.js b/Packages/viewerbase/components/studyBrowser/studyBrowser/studyBrowser.js index 512dd6076..2fcac4d84 100644 --- a/Packages/viewerbase/components/studyBrowser/studyBrowser/studyBrowser.js +++ b/Packages/viewerbase/components/studyBrowser/studyBrowser/studyBrowser.js @@ -1,5 +1,5 @@ Template.studyBrowser.helpers({ studies : function() { - return this.studies; + return Session.get('studies'); } }); \ No newline at end of file diff --git a/Packages/viewerbase/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/viewerbase/components/viewer/imageViewerViewport/imageViewerViewport.js index cc8653e6d..f612b6cf2 100644 --- a/Packages/viewerbase/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/viewerbase/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -6,8 +6,9 @@ * @param element */ function enablePrefetchOnElement(viewportIndex) { + log.info("imageViewerViewport enablePrefetchOnElement"); + var element = $('.imageViewerViewport').get(viewportIndex); - //console.log('Enabling prefetch on new element'); // Loop through all viewports and disable stackPrefetch $('.imageViewerViewport').each(function() { @@ -25,6 +26,8 @@ function enablePrefetchOnElement(viewportIndex) { } function displayReferenceLines(viewportIndex) { + log.info("imageViewerViewport displayReferenceLines"); + var element = $('.imageViewerViewport').get(viewportIndex); $('.imageViewerViewport').each(function(index, element) { @@ -49,17 +52,22 @@ function displayReferenceLines(viewportIndex) { } function loadSeriesIntoViewport(data) { + log.info("imageViewerViewport loadSeriesIntoViewport"); + if (!data.series || !data.element) { return; } - var contentId = $("#viewer").parents('.tab-pane.active').attr('id'); + var contentId = Session.get('activeContentId'); + var study = data.study; var series = data.series; var element = data.element; var viewportIndex = $(".imageViewerViewport").index(element); + ViewerData[contentId].loadedSeriesData[viewportIndex] = {}; + var allEvents = 'CornerstoneToolsMouseDown CornerstoneToolsMouseDownActivate ' + 'CornerstoneToolsMouseClick CornerstoneToolsMouseDrag CornerstoneToolsMouseUp ' + 'CornerstoneToolsMouseWheel CornerstoneToolsTap CornerstoneToolsTouchPress ' + @@ -115,6 +123,8 @@ function loadSeriesIntoViewport(data) { endLoadingHandler(element); } + cornerstone.resize(element, true); + element.classList.remove('empty'); $(element).siblings('.viewportInstructions').hide(); $(element).siblings('.imageViewerViewportOverlay').show(); @@ -142,9 +152,11 @@ function loadSeriesIntoViewport(data) { cornerstoneTools.magnify.enable(element); function onImageRendered(e, eventData) { + log.info('imageViewerViewport onImageRendered'); + Session.set('CornerstoneImageRendered' + viewportIndex, Random.id()); var viewport = cornerstone.getViewport(element); - ViewerData[contentId].viewer.imageViewerLoadedSeriesDictionary[viewportIndex].viewport = viewport; + ViewerData[contentId].loadedSeriesData[viewportIndex].viewport = viewport; Session.set('ViewerData', ViewerData); } @@ -153,6 +165,8 @@ function loadSeriesIntoViewport(data) { Session.set('CornerstoneImageRendered' + viewportIndex, Random.id()); function onNewImage(e, eventData) { + log.info('imageViewerViewport onNewImage'); + // Update the templateData with the new imageId // This allows the template helpers to update reactively templateData.imageId = eventData.enabledElement.image.imageId; @@ -161,7 +175,7 @@ function loadSeriesIntoViewport(data) { var stack = cornerstoneTools.getToolState(element, 'stack'); if (stack && stack.data.length && stack.data[0].imageIds.length > 1) { var imageIdIndex = stack.data[0].imageIds.indexOf(templateData.imageId); - ViewerData[contentId].viewer.imageViewerLoadedSeriesDictionary[viewportIndex].currentImageIdIndex = imageIdIndex; + ViewerData[contentId].loadedSeriesData[viewportIndex].currentImageIdIndex = imageIdIndex; Session.set('ViewerData', ViewerData); } } @@ -170,14 +184,17 @@ function loadSeriesIntoViewport(data) { $(element).on('CornerstoneNewImage', onNewImage); function sendActivationTrigger(e, eventData) { - var activeViewportIndex = data.activeViewport.curValue; + log.info('imageViewerViewport sendActivationTrigger'); + var activeViewportIndex = Session.get('activeViewport'); var viewportIndex = $(".imageViewerViewport").index(eventData.element); if (viewportIndex === activeViewportIndex) { return; } eventData.viewportIndex = viewportIndex; - var customEvent = jQuery.Event('ActivateViewport', eventData); - customEvent.type = 'ActivateViewport'; // Need to overwrite the type set in the touch tools + var customEvent = $.Event('ActivateViewport', eventData); + + // Need to overwrite the type set in the original event + customEvent.type = 'ActivateViewport'; $(e.target).trigger(customEvent, eventData); } @@ -186,7 +203,7 @@ function loadSeriesIntoViewport(data) { Session.set('CornerstoneNewImage' + viewportIndex, Random.id()); - OHIF.viewer.imageViewerLoadedSeriesDictionary[viewportIndex] = { + OHIF.viewer.loadedSeriesData[viewportIndex] = { studyInstanceUid: data.studyInstanceUid, seriesInstanceUid: data.seriesInstanceUid, currentImageIdIndex: data.currentImageIdIndex, @@ -206,16 +223,19 @@ function loadSeriesIntoViewport(data) { } Template.imageViewerViewport.onRendered(function() { - var studies = this.data.studies; + log.info("imageViewerViewport onRendered"); + + var studies = Session.get('studies'); + var activeViewport = Session.get('activeViewport'); var element = this.find(".imageViewerViewport"); var data = { element: element, viewport: this.data.viewport, currentImageIdIndex: this.data.currentImageIdIndex, - activeViewport: this.data.activeViewport, studyInstanceUid: this.data.studyInstanceUid, - seriesInstanceUid: this.data.seriesInstanceUid + seriesInstanceUid: this.data.seriesInstanceUid, + activeViewport: activeViewport }; if (this.data.seriesInstanceUid === undefined || this.data.studyInstanceUid === undefined) { @@ -246,18 +266,23 @@ Template.imageViewerViewport.onRendered(function() { }); Template.imageViewerViewport.onDestroyed(function() { + log.info("imageViewerViewport onDestroyed"); + var element = this.find(".imageViewerViewport"); + + try { + cornerstoneTools.stopClip(element); + } catch(error) { + } + cornerstone.disable(element); }); Template.imageViewerViewport.events({ 'ActivateViewport .imageViewerViewport': function(e) { - if (this.viewportIndex === this.activeViewport.curValue) { - return; - } - Session.set("ActivateViewportIndex", this.viewportIndex); - console.log('ActivateViewport index: ' + this.viewportIndex); - this.activeViewport.curValue = this.viewportIndex; + log.info("imageViewerViewport ActivateViewport"); + + Session.set("activeViewport", this.viewportIndex); enablePrefetchOnElement(this.viewportIndex); displayReferenceLines(this.viewportIndex); }, diff --git a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.html b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.html index aced504b7..3a22cc13d 100644 --- a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.html +++ b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.html @@ -3,7 +3,7 @@ {{ #each viewportArray }}
- {{ >imageViewerViewport viewportIndex=viewportIndex studies=studies activeViewport=activeViewport seriesInstanceUid=seriesInstanceUid studyInstanceUid=studyInstanceUid currentImageIdIndex=currentImageIdIndex viewport=viewport}} + {{ >imageViewerViewport viewportIndex=viewportIndex seriesInstanceUid=seriesInstanceUid studyInstanceUid=studyInstanceUid currentImageIdIndex=currentImageIdIndex viewport=viewport}}
{{ /each }} diff --git a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js index 1cf4ec99b..9b52aee3e 100644 --- a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js +++ b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js @@ -1,49 +1,44 @@ Template.imageViewerViewports.helpers({ height: function() { - var viewportRows; - if (!Template.parentData(1).viewportRows) { - viewportRows = 1; - } else { - viewportRows = Template.parentData(1).viewportRows.curValue; //Having issues with .get(), not sure why? - } + var viewportRows = this.viewportRows || 1; return 100 / viewportRows; }, width: function() { - var viewportColumns; - if (!Template.parentData(1).viewportColumns) { - viewportColumns = 1; - } else { - viewportColumns = Template.parentData(1).viewportColumns.curValue; //Having issues with .get(), not sure why? - } + var viewportColumns = this.viewportColumns || 1; return 100 / viewportColumns; }, viewportArray: function() { - // This is a really annoying thing to have to do, but Meteor - // doesn't want to let me use another type of helper. - var viewportRows; - if (!this.viewportRows) { - viewportRows = 1; - } else { - viewportRows = this.viewportRows.curValue; //Having issues with .get(), not sure why? + log.info("imageViewerViewports viewportArray"); + + var studies = Session.get('studies'); + + var viewportRows = this.viewportRows || 1; + var viewportColumns = this.viewportColumns || 1; + + var contentId = this.contentId || $("#viewer").parents(".tab-pane.active").attr('id'); + if (this.viewportRows && this.viewportColumns) { + viewportRows = this.viewportRows || 1; + viewportColumns = this.viewportColumns || 1; + } else if (ViewerData[contentId].viewportRows && ViewerData[contentId].viewportColumns) { + viewportRows = ViewerData[contentId].viewportRows; + viewportColumns = ViewerData[contentId].viewportColumns; } - var viewportColumns; - if (!this.viewportColumns) { - viewportColumns = 1; - } else { - viewportColumns = this.viewportColumns.curValue; //Having issues with .get(), not sure why? - } - + // Update viewerData + ViewerData[contentId].viewportRows = viewportRows; + ViewerData[contentId].viewportColumns = viewportColumns; + Session.set("ViewerData", ViewerData); + var viewportData; - if (OHIF && OHIF.viewer && !$.isEmptyObject(OHIF.viewer.imageViewerLoadedSeriesDictionary)) { - viewportData = OHIF.viewer.imageViewerLoadedSeriesDictionary; + if (!$.isEmptyObject(ViewerData[contentId].loadedSeriesData)) { + viewportData = ViewerData[contentId].loadedSeriesData; } var hangingProtocol = getHangingProtocol(); var inputData = { viewportColumns: viewportColumns, viewportRows: viewportRows, - studies: this.studies + studies: studies }; var hangingProtocolViewportData = hangingProtocol(inputData); @@ -52,8 +47,10 @@ Template.imageViewerViewports.helpers({ for (var i=0; i < numViewports; ++i) { var data = { viewportIndex: i, - studies: this.studies, - activeViewport: this.activeViewport + // These two are necessary because otherwise the width and height helpers + // don't get the right data context. Seems to be related to the "each" loop. + viewportColumns: viewportColumns, + viewportRows: viewportRows }; if (viewportData && viewportData[i]) { data.seriesInstanceUid = viewportData[i].seriesInstanceUid; diff --git a/Packages/viewerbase/components/viewer/layoutChooser/layoutChooser.js b/Packages/viewerbase/components/viewer/layoutChooser/layoutChooser.js index 4d0cfac35..84e3ee81d 100644 --- a/Packages/viewerbase/components/viewer/layoutChooser/layoutChooser.js +++ b/Packages/viewerbase/components/viewer/layoutChooser/layoutChooser.js @@ -27,31 +27,18 @@ Template.layoutChooser.events({ }, 'click .layoutChooser table td': function(evt) { $('#imageViewerViewports').remove(); - var container = $(".viewerMain").get(0); var currentCell = $(evt.currentTarget); var rowIndex = currentCell.closest('tr').index(); var columnIndex = currentCell.index(); - var data = {}; - // Add 1 because the indices start from zero - if (this.viewportRows) { - this.viewportRows.curValue = rowIndex + 1; - data.viewportRows = this.viewportRows; - } else { - data.viewportRows = 1; - } - - if (this.viewportColumns) { - this.viewportColumns.curValue = columnIndex + 1; - data.viewportColumns = this.viewportColumns; - } else { - data.viewportColumns = 1; - } - - data.studies = Template.parentData(2).studies; - data.activeViewport = Template.parentData(2).activeViewport; + var data = { + viewportRows: rowIndex + 1, + viewportColumns: columnIndex + 1, + }; + + var container = $(".viewerMain").get(0); UI.renderWithData(Template.imageViewerViewports, data, container); } }); \ No newline at end of file diff --git a/Packages/viewerbase/components/viewer/playClipButton/playClipButton.js b/Packages/viewerbase/components/viewer/playClipButton/playClipButton.js index 75d8e7947..ec35a6d47 100644 --- a/Packages/viewerbase/components/viewer/playClipButton/playClipButton.js +++ b/Packages/viewerbase/components/viewer/playClipButton/playClipButton.js @@ -1,7 +1,7 @@ Template.playClipButton.helpers({ 'isPlaying': function() { Session.get('UpdateCINE'); - var viewportIndex = this.activeViewport.curValue; - return !!OHIF.viewer.isPlaying[viewportIndex]; + var activeViewport = Session.get('activeViewport'); + return !!OHIF.viewer.isPlaying[activeViewport]; } }); \ No newline at end of file diff --git a/Packages/viewerbase/components/viewer/toolbar/toolbar.html b/Packages/viewerbase/components/viewer/toolbar/toolbar.html index 12c4ac125..b4616916a 100644 --- a/Packages/viewerbase/components/viewer/toolbar/toolbar.html +++ b/Packages/viewerbase/components/viewer/toolbar/toolbar.html @@ -6,11 +6,11 @@ {{ /each }} {{ #if includePlayClipButton }} - {{ >playClipButton activeViewport=activeViewport}} + {{ >playClipButton }} {{ /if }} {{ #if includeLayoutButton }} - {{ >layoutButton viewportRows=viewportRows viewportColumns=viewportColumns}} + {{ >layoutButton }} {{ /if }} diff --git a/Packages/viewerbase/components/viewer/toolbar/toolbar.js b/Packages/viewerbase/components/viewer/toolbar/toolbar.js index 77799e35e..b43711c81 100644 --- a/Packages/viewerbase/components/viewer/toolbar/toolbar.js +++ b/Packages/viewerbase/components/viewer/toolbar/toolbar.js @@ -84,8 +84,8 @@ Template.toolbar.events({ if (!OHIF.viewer.functionList.hasOwnProperty(command)) { return; } - var viewportIndex = Template.parentData(0).activeViewport.get(); - var element = $('.imageViewerViewport').get(viewportIndex); + var activeViewport = Session.get('activeViewport'); + var element = $('.imageViewerViewport').get(activeViewport); OHIF.viewer.functionList[command](element); } }); diff --git a/Packages/viewerbase/lib/rerenderViewportWithNewSeries.js b/Packages/viewerbase/lib/rerenderViewportWithNewSeries.js index a8dae13e9..35c6f3f16 100644 --- a/Packages/viewerbase/lib/rerenderViewportWithNewSeries.js +++ b/Packages/viewerbase/lib/rerenderViewportWithNewSeries.js @@ -12,7 +12,7 @@ rerenderViewportWithNewSeries = function(element, data) { data.viewportIndex = viewportIndex; // Update the dictionary of loaded series for the specified viewport - OHIF.viewer.imageViewerLoadedSeriesDictionary[viewportIndex] = { + OHIF.viewer.loadedSeriesData[viewportIndex] = { seriesInstanceUid: data.seriesInstanceUid, studyInstanceUid: data.studyInstanceUid, currentImageIdIndex: 0 diff --git a/Packages/viewerbase/log.js b/Packages/viewerbase/log.js new file mode 100644 index 000000000..e188235f2 --- /dev/null +++ b/Packages/viewerbase/log.js @@ -0,0 +1,3 @@ +// Create package logger using loglevel +// https://atmospherejs.com/spacejamio/loglevel +log = loglevel.createPackageLogger('viewerbase', defaultLevel = 'warn'); \ No newline at end of file diff --git a/Packages/viewerbase/package.js b/Packages/viewerbase/package.js index 15f3418a4..52cdca459 100644 --- a/Packages/viewerbase/package.js +++ b/Packages/viewerbase/package.js @@ -11,9 +11,13 @@ Package.onUse(function (api) { api.use('jquery'); api.use('stylus'); + api.use('practicalmeteor:loglevel'); + // Our custom package api.use('cornerstone'); + api.addFiles('log.js', 'client'); + // ---------- Components ---------- // Basic components diff --git a/Packages/worklist/components/worklist.js b/Packages/worklist/components/worklist.js index 086f7bd72..acee1a9e7 100644 --- a/Packages/worklist/components/worklist.js +++ b/Packages/worklist/components/worklist.js @@ -1,43 +1,59 @@ ViewerData = Session.get('ViewerData') || {}; +StudyMetaData = {}; -Template.worklist.helpers({ - 'tabs': function() { - console.log('Updating tabs'); - return tabs.find(); - } -}); +getStudyMetadata = function(studyInstanceUid, doneCallback) { + log.info('worklistStudy getStudyMetadata'); -Template.worklist.events({ - 'click a[data-toggle="tab"]': function(e) { - var contentId = $(e.currentTarget).data('target').replace("#", ""); - switchToTab(contentId); + if (StudyMetaData.hasOwnProperty(studyInstanceUid) && StudyMetaData[studyInstanceUid]) { + var study = StudyMetaData[studyInstanceUid]; + doneCallback(study); + return; } -}); + + Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) { + sortStudy(study); + + StudyMetaData[studyInstanceUid] = study; + + doneCallback(study); + }); +}; switchToTab = function(contentId) { - var data = ViewerData[contentId]; - - console.log("Switching to tab: " + contentId); - - // Set active tab - Session.set("activeContentId", contentId); + log.info("Switching to tab: " + contentId); $('.tabTitle a[data-target="#' + contentId + '"]').tab('show'); $("#viewer").remove(); - var container = $('.tab-content').find("#" + contentId).get(0); - if (!container) { - return; - } - if (contentId === 'worklistTab') { - console.log('Switching to worklist'); document.body.style.overflow = null; document.body.style.height = null; document.body.style.minWidth = null; document.body.style.position = null; - } else { + return; + } + + // Set active tab + Session.set("activeContentId", contentId); + + // Get tab content container + var container = $('.tab-content').find("#" + contentId).get(0); + if (!container) { + return; + } + + var studyInstanceUid = ViewerData[contentId].studyInstanceUid; + getStudyMetadata(studyInstanceUid, function(study) { + var data = { + viewportRows: ViewerData[contentId].viewportRows, + viewportColumns: ViewerData[contentId].viewportColumns, + contentId: contentId, + studies: [study], + }; + + Session.set('studies', data.studies); + UI.renderWithData(Template.viewer, data, container); var imageViewer = $("#viewer"); if (imageViewer) { @@ -47,34 +63,50 @@ switchToTab = function(contentId) { document.body.style.minWidth = 0; document.body.style.position = 'fixed'; // Prevent overscroll on mobile devices } - } + }); +}; + + +openNewTab = function(studyInstanceUid) { + getStudyMetadata(studyInstanceUid, function(study) { + var title = study.seriesList[0].instances[0].patientName; + var contentid = generateUUID(); + + var data = { + title: title, + contentid: contentid, + }; + tabs.insert(data); + + ViewerData[contentid] = { + title: title, + contentid: contentid, + studyInstanceUid: studyInstanceUid + }; + switchToTab(contentid); + }); }; Template.worklist.onRendered(function() { - this.autorun(function() { - var data = Session.get('OpenNewTabEvent'); - - // If we have no new tab data, stop here - // (e.g. if we are rendering the worklist) - if (!data) { - return; - } - - var contentId = data.contentid; - if (ViewerData.hasOwnProperty(contentId)) { - console.warn('Contentid already exists?'); - return; - } - - // Update the viewer data object - ViewerData[contentId] = { - contentId: contentId, - studies: data.studies, - title: data.title - }; - Session.set('ViewerData', ViewerData); - - + // If there is a tab set as active in the Session, + // switch to that now. + var contentId = Session.get("activeContentId"); + if (contentId) { switchToTab(contentId); - }); + } +}); + + +Template.worklist.helpers({ + 'tabs': function() { + log.info('Updating tabs'); + return tabs.find(); + } +}); + +Template.worklist.events({ + 'click a[data-toggle="tab"]': function(e) { + var contentId = $(e.currentTarget).data('target').replace("#", ""); + switchToTab(contentId); + } }); \ No newline at end of file diff --git a/Packages/worklist/components/worklistStudy/worklistStudy.js b/Packages/worklist/components/worklistStudy/worklistStudy.js index 389460128..f02950b9b 100644 --- a/Packages/worklist/components/worklistStudy/worklistStudy.js +++ b/Packages/worklist/components/worklistStudy/worklistStudy.js @@ -1,5 +1,5 @@ Template.worklistStudy.events({ - 'click': function (){ - Router.go('viewer', {_id: this.studyInstanceUid}); + 'click': function () { + openNewTab(this.studyInstanceUid); } }); \ No newline at end of file diff --git a/Packages/worklist/log.js b/Packages/worklist/log.js new file mode 100644 index 000000000..3496daf76 --- /dev/null +++ b/Packages/worklist/log.js @@ -0,0 +1,3 @@ +// Create package logger using loglevel +// https://atmospherejs.com/spacejamio/loglevel +log = loglevel.createPackageLogger('worklist', defaultLevel = 'info'); \ No newline at end of file diff --git a/Packages/worklist/package.js b/Packages/worklist/package.js index 6414183dd..eb3c02907 100644 --- a/Packages/worklist/package.js +++ b/Packages/worklist/package.js @@ -11,6 +11,13 @@ Package.onUse(function (api) { api.use('jquery'); api.use('stylus'); + api.use('practicalmeteor:loglevel'); + + // Our custom package + api.use('dicomweb'); + + api.addFiles('log.js', 'client'); + // Components api.addFiles('components/worklist.html', 'client'); api.addFiles('components/worklist.js', 'client'); @@ -37,6 +44,7 @@ Package.onUse(function (api) { api.addFiles('lib/generateUUID.js', 'client'); api.export('generateUUID', 'client'); + api.export('openNewTab', 'client'); api.export('switchToTab', 'client'); api.export('ViewerData', 'client'); diff --git a/README.md b/README.md index 3dfadfb50..011864000 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # Viewers This repo contains the OHIF DICOM Viewer and Lesion Trackers, and various shared meteor packages + + +### Docker usage +#### Temporary data storage +docker run --rm -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins + +#### Persistent data storage +1. Create a persistant data volume for Orthanc to use +docker create --name sampledata -v /sampledata jodogne/orthanc-plugins + +2. Run Orthanc from Docker with the data volume attached +docker run --volumes-from sampledata -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins + +3. Upload your data and it will be persisted \ No newline at end of file