From ec9b77c4d165b8725f80837af0d13b714d1a9912 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Tue, 24 Nov 2015 18:08:00 +0100 Subject: [PATCH] Update cornerstoneTools to allow some handles off-screen. Add hardcoded MG hanging protocol to window manager. Fix tooltips in toolbar buttons --- OHIFViewer/client/routes.js | 4 +- .../cornerstone/client/cornerstoneTools.js | 6 +- .../imageViewerViewports.js | 29 +- .../components/viewer/toolbar/toolbar.js | 4 + Packages/viewerbase/lib/windowManager.js | 300 +++++++++++++++++- 5 files changed, 318 insertions(+), 25 deletions(-) diff --git a/OHIFViewer/client/routes.js b/OHIFViewer/client/routes.js index c05cebf2c..3431d9323 100644 --- a/OHIFViewer/client/routes.js +++ b/OHIFViewer/client/routes.js @@ -5,7 +5,7 @@ Object.keys(ViewerData).forEach(function(contentId) { var tabData = ViewerData[contentId]; var data = { title: tabData.title, - contentid: tabData.contentid, + contentid: tabData.contentid }; WorklistTabs.insert(data); }); @@ -27,8 +27,6 @@ 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 diff --git a/Packages/cornerstone/client/cornerstoneTools.js b/Packages/cornerstone/client/cornerstoneTools.js index 38ec70036..bf8382b88 100644 --- a/Packages/cornerstone/client/cornerstoneTools.js +++ b/Packages/cornerstone/client/cornerstoneTools.js @@ -1,4 +1,4 @@ -/*! cornerstoneTools - v0.7.7 - 2015-11-22 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */ +/*! cornerstoneTools - v0.7.7 - 2015-11-23 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */ // Begin Source: src/header.js if (typeof cornerstone === 'undefined') { cornerstone = {}; @@ -6078,6 +6078,10 @@ if (typeof cornerstoneTools === 'undefined') { Object.keys(handles).forEach(function(name) { var handle = handles[name]; + if (handle.allowedOutsideImage === true) { + return; + } + if (cornerstoneMath.point.insideRect(handle, imageRect) === false) { handleOutsideImage = true; } diff --git a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js index 10b556f43..41c333fde 100644 --- a/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js +++ b/Packages/viewerbase/components/viewer/imageViewerViewports/imageViewerViewports.js @@ -26,11 +26,6 @@ Template.imageViewerViewports.helpers({ viewportColumns = ViewerData[contentId].viewportColumns; } - // Update viewerData - ViewerData[contentId].viewportRows = viewportRows; - ViewerData[contentId].viewportColumns = viewportColumns; - Session.set("ViewerData", ViewerData); - var viewportData; if (!$.isEmptyObject(ViewerData[contentId].loadedSeriesData)) { viewportData = ViewerData[contentId].loadedSeriesData; @@ -44,9 +39,19 @@ Template.imageViewerViewports.helpers({ inputData.DisplaySetPresentationGroup = Session.get('WindowManagerPresentationGroup'); var hangingProtocolViewportData = WindowManager.getHangingProtocol(inputData); if (Session.get('UseHangingProtocol')) { - viewportData = hangingProtocolViewportData; + viewportData = hangingProtocolViewportData.viewports; + viewportRows = hangingProtocolViewportData.viewportRows || viewportRows; + viewportColumns = hangingProtocolViewportData.viewportColumns || viewportColumns; } + // Update viewerData + ViewerData[contentId].viewportRows = viewportRows; + ViewerData[contentId].viewportColumns = viewportColumns; + Session.set("ViewerData", ViewerData); + + this.viewportRows = viewportRows; + this.viewportColumns = viewportColumns; + var numViewports = viewportRows * viewportColumns; for (var i=0; i < numViewports; ++i) { var data = { @@ -57,16 +62,16 @@ Template.imageViewerViewports.helpers({ viewportRows: viewportRows }; - if (viewportData && viewportData[i]) { + if (viewportData && !$.isEmptyObject(viewportData[i])) { data.seriesInstanceUid = viewportData[i].seriesInstanceUid; data.studyInstanceUid = viewportData[i].studyInstanceUid; data.currentImageIdIndex = viewportData[i].currentImageIdIndex; data.viewport = viewportData[i].viewport; - } else if (hangingProtocolViewportData && hangingProtocolViewportData[i]) { - data.seriesInstanceUid = hangingProtocolViewportData[i].seriesInstanceUid; - data.studyInstanceUid = hangingProtocolViewportData[i].studyInstanceUid; - data.currentImageIdIndex = hangingProtocolViewportData[i].currentImageIdIndex; - data.viewport = hangingProtocolViewportData[i].viewport; + } else if (hangingProtocolViewportData && !$.isEmptyObject(hangingProtocolViewportData.viewports[i])) { + data.seriesInstanceUid = hangingProtocolViewportData.viewports[i].seriesInstanceUid; + data.studyInstanceUid = hangingProtocolViewportData.viewports[i].studyInstanceUid; + data.currentImageIdIndex = hangingProtocolViewportData.viewports[i].currentImageIdIndex; + data.viewport = hangingProtocolViewportData.viewports[i].viewport; } ViewerWindows.insert(data); diff --git a/Packages/viewerbase/components/viewer/toolbar/toolbar.js b/Packages/viewerbase/components/viewer/toolbar/toolbar.js index af43eb145..3ecf7c58c 100644 --- a/Packages/viewerbase/components/viewer/toolbar/toolbar.js +++ b/Packages/viewerbase/components/viewer/toolbar/toolbar.js @@ -110,6 +110,8 @@ function getDefaultButtonData() { Template.toolbar.events({ 'click .imageViewerTool': function(e) { + $(e.currentTarget).tooltip('hide'); + var tool = e.currentTarget.id; console.log('Setting active tool to: ' + tool); @@ -117,6 +119,8 @@ Template.toolbar.events({ toolManager.setActiveTool(tool, elements); }, 'click .imageViewerCommand': function(e) { + $(e.currentTarget).tooltip('hide'); + var command = e.currentTarget.id; if (!OHIF.viewer.functionList.hasOwnProperty(command)) { return; diff --git a/Packages/viewerbase/lib/windowManager.js b/Packages/viewerbase/lib/windowManager.js index d8126e2fc..785a819fd 100644 --- a/Packages/viewerbase/lib/windowManager.js +++ b/Packages/viewerbase/lib/windowManager.js @@ -1,3 +1,243 @@ +/** + * This is a temporary function which will return a hardcoded hanging protocol as a JavaScript object + */ +function getMammoHangingProtocolObject() { + + var protocol = [{ + stage: 1, + rows: 2, + columns: 4, + viewports: [{ + seriesDescription: 'RCC', + study: 'prior' + }, { + seriesDescription: 'LCC', + study: 'prior' + }, { + seriesDescription: 'RMLO', + study: 'prior' + }, { + seriesDescription: 'LMLO', + study: 'prior' + }, { + seriesDescription: 'RCC', + study: 'current' + }, { + seriesDescription: 'LCC', + study: 'current' + }, { + seriesDescription: 'RMLO', + study: 'current' + }, { + seriesDescription: 'LMLO', + study: 'current' + } + ] + }, { + stage: 2, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'RCC', + study: 'current' + }, { + seriesDescription: 'LCC', + study: 'current' + } + ] + }, { + stage: 3, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'RMLO', + study: 'current' + }, { + seriesDescription: 'LMLO', + study: 'current' + } + ] + }, { + stage: 4, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'RCC', + study: 'current' + }, { + seriesDescription: 'RCC', + study: 'prior' + } + ] + }, { + stage: 5, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'LCC', + study: 'current' + }, { + seriesDescription: 'LCC', + study: 'prior' + } + ] + }, { + stage: 6, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'LMLO', + study: 'current' + }, { + seriesDescription: 'LMLO', + study: 'prior' + } + ] + }, { + stage: 7, + rows: 1, + columns: 2, + viewports: [{ + seriesDescription: 'RMLO', + study: 'current' + }, { + seriesDescription: 'RMLO', + study: 'prior' + } + ] + }, { + stage: 8, + rows: 2, + columns: 4, + viewports: [{ + seriesDescription: 'RCC', + study: 'prior', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'LCC', + study: 'prior', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'RMLO', + study: 'prior', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'LMLO', + study: 'prior', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'RCC', + study: 'current', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'LCC', + study: 'current', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'RMLO', + study: 'current', + options: { + includeCADMarkers: true + } + }, { + seriesDescription: 'LMLO', + study: 'current', + options: { + includeCADMarkers: true + } + } + ] + } + ]; + + return protocol; +} + +function findSeriesByDescription(seriesDescription, study) { + var seriesInstanceUid; + study.seriesList.forEach(function(series) { + if (!series.seriesDescription) { + return; + } + + var currentSeriesDescription = series.seriesDescription.replace(' ', ''); + if (currentSeriesDescription === seriesDescription) { + seriesInstanceUid = series.seriesInstanceUid; + return false; + } + }); + + return seriesInstanceUid; +} + +/** + * (Work in progress) Uses the information from a DICOM Hanging Protocol + * to identify and display studies and series in the image viewer + * + * @param hangingProtocol + * @param inputData + * @returns {Array} Array of viewport data to be displayed + */ +function applyHangingProtocol(hangingProtocol, inputData) { + var presentationGroup = inputData.DisplaySetPresentationGroup || 1; + var studies = ViewerStudies; + var currentProtocolData = hangingProtocol[presentationGroup - 1]; + + var viewportData = { + viewports: [], + viewportRows: currentProtocolData.rows, + viewportColumns: currentProtocolData.columns + }; + + var currentStudy = ViewerStudies.find({}, {$sort: {studyDate: 1}}).fetch()[0]; + var otherStudies = WorklistStudies.find({ + patientId: currentStudy.patientId, + studyInstanceUid: { + $ne: currentStudy.studyInstanceUid + } + }, {$sort: { + studyDate: 1 + }}).fetch(); + var priorStudy = otherStudies[0]; + + currentProtocolData.viewports.forEach(function(viewport, index) { + if (viewport.study === 'current') { + study = currentStudy; + } else if (viewport.study === 'prior') { + study = currentStudy; + /*ViewerStudies.find({ + studyInstanceUid: { + $ne: currentStudy.studyInstanceUid + } + }, {$sort: {studyDate: 1}}).fetch()[0];*/ + } + + seriesInstanceUid = findSeriesByDescription(viewport.seriesDescription, study); + + viewportData.viewports[index] = { + seriesInstanceUid: seriesInstanceUid, + studyInstanceUid: study.studyInstanceUid, + currentImageIdIndex: 0, + options: viewport.options + }; + }); + + return viewportData; +} + + /** * This is a temporary function which will return a hardcoded hanging protocol as a JavaScript object * The purpose of this is to act as a stub until we are actually parsing DICOM Hanging Protocol files, @@ -61,7 +301,7 @@ function getMammoHangingProtocol() { { ImageSetNumber: 2, ImageSetSelectorCategory: 'ABSTRACT_PRIOR', - AbstractPriorValue: '1\1', + AbstractPriorValue: '1\\1', ImageSetLabel: 'Prior MG Breast' }] }], @@ -103,7 +343,6 @@ function getMammoHangingProtocol() { FilterByOperator: 'MEMBER_OF' }] }, - // Stage 3 { // Left side image (R MLO Current) ImageSetNumber: 1, @@ -143,6 +382,39 @@ function getMammoHangingProtocol() { FilterByCategory: 'SERIES_DESCRIPTION', FilterByOperator: 'MEMBER_OF' }] + }, { + // Testing purposes + // Left side image (R MLO Current) + ImageSetNumber: 1, + DisplaySetNumber: 1, + DisplaySetPresentationGroup: 3, + DisplaySetPresentationGroupDescription: 'Current Mediolateral only', + ImageBoxesSequence: [{ + DisplayEnvironmentSpatialPosition: '0\0.2\0.16667\0', + ImageBoxNumber: 1, + ImageBoxLayoutType: 'STACK' + }], + FilterOperationsSequence: [{ + SelectorAttributeVR: 'CS', + SelectorCSValue: 'R MLO', + FilterByCategory: 'SERIES_DESCRIPTION', + FilterByOperator: 'MEMBER_OF' + }] + }, { + // Right side image (R CC Current) + ImageSetNumber: 1, + DisplaySetNumber: 1, + DisplaySetPresentationGroup: 3, + ImageBoxesSequence: [{ + ImageBoxNumber: 1, + ImageBoxLayoutType: 'STACK' + }], + FilterOperationsSequence: [{ + SelectorAttributeVR: 'CS', + SelectorCSValue: 'R CC', + FilterByCategory: 'SERIES_DESCRIPTION', + FilterByOperator: 'MEMBER_OF' + }] }], PartialDataDisplayHandling: 'MAINTAIN_LAYOUT', SynchronizedScrollingSequence: [], @@ -215,7 +487,10 @@ function applyDICOMHangingProtocol(hangingProtocol, inputData) { var studies = ViewerStudies; var currentDisplaySets = findSetsByPresentationGroup(hangingProtocol.DisplaySetsSequence, presentationGroup); - var viewportData = []; + var viewportData = { + viewports: [] + }; + currentDisplaySets.forEach(function(displaySet, index) { // TODO= Find study information by image set properties and // image set number of this display set @@ -226,7 +501,7 @@ function applyDICOMHangingProtocol(hangingProtocol, inputData) { var study = ViewerStudies.findOne({studyInstanceUid: studyInstanceUid}); seriesInstanceUid = findSeries(displaySet, study); - viewportData[index] = { + viewportData.viewports[index] = { seriesInstanceUid: seriesInstanceUid, studyInstanceUid: studyInstanceUid, currentImageIdIndex: 0 @@ -272,7 +547,9 @@ function defaultHangingProtocol(inputData) { }); }); - var viewportData = []; + var viewportData = { + viewports: [] + }; var numViewports = viewportRows * viewportColumns; for (var i=0; i < numViewports; ++i) { @@ -280,7 +557,7 @@ function defaultHangingProtocol(inputData) { // We don't have enough stacks to fill the desired number of viewports, so stop here break; } - viewportData[i] = { + viewportData.viewports[i] = { seriesInstanceUid: stacks[i].series.seriesInstanceUid, studyInstanceUid: stacks[i].study.studyInstanceUid, currentImageIdIndex: 0 @@ -312,9 +589,14 @@ function getHangingProtocol(inputData) { }); if (modalities.indexOf('MG') > -1) { - var hangingProtocol = getMammoHangingProtocol(); + var hp = getMammoHangingProtocolObject(); Session.set('WindowManagerPresentationGroup', presentationGroup); - return applyDICOMHangingProtocol(hangingProtocol, inputData); + var testData = applyHangingProtocol(hp, inputData); + return testData; + // Commenting this out for now so we can hardcode a MG protocol + /*var hangingProtocol = getMammoHangingProtocol(); + Session.set('WindowManagerPresentationGroup', presentationGroup); + return applyDICOMHangingProtocol(hangingProtocol, inputData);*/ } Session.set('WindowManagerPresentationGroup', undefined); @@ -349,7 +631,7 @@ function getNumPresentationGroups() { // TODO=Pull this information from the largest // value of DisplaySetPresentationGroup in the DisplaySetsSequence // after the DICOM-HP is parsed - return 2; + return 8; } function getCurrentPresentationGroup() {