diff --git a/LesionTracker/client/components/init.js b/LesionTracker/client/components/init.js index fc816f8a7..6404dee68 100644 --- a/LesionTracker/client/components/init.js +++ b/LesionTracker/client/components/init.js @@ -8,4 +8,6 @@ Meteor.startup(function() { mouse: cornerstoneTools.nonTarget, touch: cornerstoneTools.nonTargetTouch }); + + toolManager.setAlwaysEnabledTools(['lesion', 'nonTarget']); }); \ No newline at end of file diff --git a/Packages/lesiontracker/compatibility/lesionTool.js b/Packages/lesiontracker/compatibility/lesionTool.js index 6058eedfb..a5e0e0553 100644 --- a/Packages/lesiontracker/compatibility/lesionTool.js +++ b/Packages/lesiontracker/compatibility/lesionTool.js @@ -9,7 +9,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo var configuration = { getLesionLocationCallback: getLesionLocationCallback, - changeLesionLocationCallback: changeLesionLocationCallback, + changeLesionLocationCallback: changeLesionLocationCallback }; // Define a callback to get your text annotation @@ -179,19 +179,19 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo return cornerstoneMath.point.insideRect(coords, handle.boundingBox); } - function suscribeLesionToolSelectedEvent(element) { + function subscribeLesionToolSelectedEvent(element) { var elementEvents = $._data(element, "events"); var index = Object.keys(elementEvents).indexOf("LesionToolSelected"); if (index < 0) { - // Subscribe LesionToolSelected and calls measurementModified function when lesion measurement is changed or updated. - $(element).on("LesionToolSelected", measurementModified); + // Subscribe LesionToolSelected and calls loadImage when lesion measurement is changed or updated. + $(element).on("LesionToolSelected", loadImage); } } ///////// BEGIN IMAGE RENDERING /////// function onImageRendered(e, eventData) { - suscribeLesionToolSelectedEvent(e.currentTarget); + subscribeLesionToolSelectedEvent(e.currentTarget); // if we have no toolData for this element, return immediately as there is nothing to do var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); @@ -228,14 +228,13 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo var color; var element = eventData.element; var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); - var font = cornerstoneTools.textStyle.getFont(); var config = cornerstoneTools.lesion.getConfiguration(); // configurable shadow from CornerstoneTools if (config && config.shadow) { context.shadowColor = '#000000'; - context.shadowOffsetX = +1; - context.shadowOffsetY = +1; + context.shadowOffsetX = 1; + context.shadowOffsetY = 1; } if (data.active) { @@ -364,21 +363,23 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo } + // TODO=Refactor because this function is repeated in nonTargetTool! function loadImage(e, eventData) { // If type is active, load image and activate lesion // If type is inactive, update lesions of enabledElement as inactive - + log.info('lesionTool loadImage'); + var element = eventData.enabledElement.element; + var imageId = eventData.lesionData.imageId; if (eventData.type === "active") { - var stackToolDataSource = cornerstoneTools.getToolState(e.currentTarget, 'stack'); + var stackToolDataSource = cornerstoneTools.getToolState(element, 'stack'); var stackData = stackToolDataSource.data[0]; - var imageIds = stackData.imageIds; - var imageIdIndex = imageIds.indexOf(eventData.lesionData.imageId); + var imageIdIndex = stackData.imageIds.indexOf(imageId); if (imageIdIndex < 0) { return; } cornerstone.loadAndCacheImage(imageIds[imageIdIndex]).then(function(image) { - cornerstone.displayImage(eventData.enabledElement.element, image); + cornerstone.displayImage(element, image); updateLesion(e, eventData); }); } else if (eventData.type === "inactive") { @@ -386,11 +387,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo } } - //This function is called from cornerstone-viewport.html and updates lesion measurement and makes the lesion active - function measurementModified(e, eventData) { - loadImage(e, eventData); - } - // module exports cornerstoneTools.lesion = cornerstoneTools.mouseButtonTool({ createNewMeasurement: createNewMeasurement, diff --git a/Packages/lesiontracker/compatibility/nonTargetTool.js b/Packages/lesiontracker/compatibility/nonTargetTool.js index 3bc2a75a1..e9f48b170 100644 --- a/Packages/lesiontracker/compatibility/nonTargetTool.js +++ b/Packages/lesiontracker/compatibility/nonTargetTool.js @@ -186,19 +186,18 @@ context.fill(); } - function suscribeNonTargetToolModifiedEvent(element) { + function subscribeNonTargetToolModifiedEvent(element) { var elementEvents = $._data(element, "events"); var index = Object.keys(elementEvents).indexOf("NonTargetToolSelected"); if (index < 0) { - // Subscribe LesionToolModified and calls measurementModified function when lesion measurement is changed or updated. - $(element).on("NonTargetToolSelected", measurementModified); + // Subscribe LesionToolModified and calls loadImage function when lesion measurement is changed or updated. + $(element).on("NonTargetToolSelected", loadImage); } } ///////// BEGIN IMAGE RENDERING /////// function onImageRendered(e, eventData) { - - suscribeNonTargetToolModifiedEvent(e.currentTarget); + subscribeNonTargetToolModifiedEvent(e.currentTarget); // if we have no toolData for this element, return immediately as there is nothing to do var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); @@ -268,7 +267,7 @@ context.beginPath(); context.strokeStyle = color; context.lineWidth = 1 / eventData.viewport.scale; - var mid = { + mid = { x: mid.x, y: mid.y }; @@ -390,7 +389,7 @@ // if we have no toolData for this element, return immediately as there is nothing to do var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); - if (toolData === undefined) { + if (!toolData) { return; } @@ -463,31 +462,27 @@ onImageRendered(e, eventData); } - function loadImage(e, eventObject) { + function loadImage(e, eventData) { // If type is active, load image and activate lesion // If type is inactive, update lesions of enabledElement as inactive - - if (eventObject.type === "active") { - var stackToolDataSource = cornerstoneTools.getToolState(e.currentTarget, 'stack'); + log.info('nonTargetTool loadImage'); + var element = eventData.enabledElement.element; + var imageId = eventData.lesionData.imageId; + if (eventData.type === "active") { + var stackToolDataSource = cornerstoneTools.getToolState(element, 'stack'); var stackData = stackToolDataSource.data[0]; - var imageIdsArr = stackData.imageIds; - var indexOfImage = imageIdsArr.indexOf(eventObject.lesionData.imageId); - if (indexOfImage > -1) { - cornerstone.loadAndCacheImage(stackData.imageIds[indexOfImage]).then(function(image) { - cornerstone.displayImage(eventObject.enabledElement.element, image); - updateLesion(e, eventObject); - }); + var imageIdIndex = stackData.imageIds.indexOf(imageId); + if (imageIdIndex < 0) { + return; } - } else if (eventObject.type === "inactive") { - updateLesion(e, eventObject); + + cornerstone.loadAndCacheImage(stackData.imageIds[imageIdIndex]).then(function(image) { + cornerstone.displayImage(element, image); + updateLesion(e, eventData); + }); + } else if (eventData.type === "inactive") { + updateLesion(e, eventData); } - - - } - - //This function is called from cornerstone-viewport.html and updates lesion measurement and makes the lesion active - function measurementModified(e, eventObject) { - loadImage(e, eventObject); } cornerstoneTools.nonTarget = cornerstoneTools.mouseButtonTool({ diff --git a/Packages/lesiontracker/components/lesionTable/lesionTable.js b/Packages/lesiontracker/components/lesionTable/lesionTable.js index 5f49cb33b..d7190bd04 100644 --- a/Packages/lesiontracker/components/lesionTable/lesionTable.js +++ b/Packages/lesiontracker/components/lesionTable/lesionTable.js @@ -1,153 +1,137 @@ -// Get enabled elements in viewport -// Returns enabled elements with associated elements as object array -function getEnabledElementsInViewport() { - var enabledElementsInViewport = []; +/** + * Returns timepoint object based on timepoint id of the enabled element + * + * @param timepoints + * @param enabledElement + * @returns {*|{}} Timepoint object based on timepoint id of the enabled element (or an empty Object) + */ +function getTimepointObject(imageId) { + var study = cornerstoneTools.metaData.get('study', imageId); - $(".imageViewerViewport").each(function(index, element) { + var timepoint = Timepoints.findOne({timepointName: study.studyDate}); + return timepoint; +} + +/** + * Switch to the image of the correct image index + * Activate the selected measurement on the switched image (color to be green) + * Deactivate all other measurements on the switched image (color to be white) + */ +function activateMeasurements(element, measurementId) { + // TODO=Switch this to use the new CornerstoneToolMeasurementModified event, + // Once it has 'modified on activation' set up + + var enabledElement = cornerstone.getEnabledElement(element); + var imageId = enabledElement.image.imageId; + var timepointData = getTimepointObject(imageId); + var measurementData = Measurements.findOne(measurementId); + + var measurementAtTimepoint = measurementData.timepoints[timepointData.timepointID]; + if (!measurementAtTimepoint) { + return; + } + + // Defines event data + var eventData = { + enabledElement: enabledElement, + lesionData: { + id: measurementId, + isTarget: measurementData.isTarget, + lesionNumber: measurementData.lesionNumber, + imageId: imageId, + seriesInstanceUid: measurementAtTimepoint.seriesInstanceUid, + studyInstanceUid: measurementAtTimepoint.studyInstanceUid + }, + type: "active" + }; + + // If isTarget = false, this measurement is nonTarget measurement + // Activate related nonTarget measurement + // Deactivate all target measurements to activate only nonTarget measurement + if (!isTarget) { + $(element).trigger("NonTargetToolSelected", eventData); + + // Deactivate lesion tool measurements + eventData.type = "inactive"; + + $(element).trigger("LesionToolSelected", eventData); + + } else { + // Trigger event for target measurements + $(element).trigger("LesionToolSelected", eventData); + + // Deactivate nonTarget tool measurements + eventData.type = "inactive"; + + // Trigger event for nonTarget measurements + // Inactivate all nonTarget measurements if any measurement is active + $(element).trigger("NonTargetToolSelected", eventData); + } +} + +/** + * Activates a set of lesions when lesion table row is clicked + * + * @param measurementId The unique key for a specific Measurement + */ +function activateLesion(measurementId) { + // Find Measurement data for this lesion + var measurementData = Measurements.findOne(measurementId); + + // If there is no measurement with this ID, stop here + if (!measurementData) { + log.warn('No Measurements entry associated to an ID in a lesion table row'); + return; + } + + // Get the timepoint data from this Measurement + var timepoints = measurementData.timepoints; + + // Loop through the viewports and display each timepoint + $(".imageViewerViewport").each(function(viewportIndex, element) { + // Stop if we run out of timepoints before viewports + if (viewportIndex >= Object.keys(timepoints).length) { + return false; + } + + // Find the image that is currently in this viewport var enabledElement = cornerstone.getEnabledElement(element); - if (!enabledElement) { + if (!enabledElement || !enabledElement.image) { return; } - enabledElementsInViewport.push({element: element, enabledElement: enabledElement}); - }); + // Find measurements related to the Nth timepoint + // TODO=Re-evaluate this approach to populating viewports with timepoints + // What is the desired behaviour here? + var key = Object.keys(measurementData.timepoints)[viewportIndex]; + var measurementAtTimepoint = measurementData.timepoints[key]; - return enabledElementsInViewport; -} - -// Switch to the image of the correct image index -// Activate the selected measurement on the switched image (color to be green) -// Deactivate all other measurements on the switched image (color to be white) -function activateMeasurementsInRelatedImages(timepoints, lesionNumber, isTarget) { - - //TODO: This is a bad hack! We need to listen an event of the element and handle it when rendering of the element is completed - //TODO: we can switch to the related image and activate the selected measurement on the switched image. - var intervalListener = setInterval(function() { - window.clearInterval(intervalListener); - - var enabledElementsInViewport = getEnabledElementsInViewport(); - - for(var i = 0; i < enabledElementsInViewport.length; i++) { - var element = enabledElementsInViewport[i].element; - var enabledElement = enabledElementsInViewport[i].enabledElement; - var timepointObject = getTimepointObject(timepoints, enabledElement); - - if (timepointObject === undefined) { - continue; - } - - // Defines event data - var eventData = { - enabledElement: enabledElement, - lesionData: { - isTarget: isTarget, - lesionNumber: lesionNumber, - imageId: timepointObject.imageId, - seriesInstanceUid: timepointObject.seriesInstanceUid, - studyInstanceUid: timepointObject.studyInstanceUid - }, - type: "active" - }; - - // If isTarget = false, this measurement is nonTarget measurement - // Activate related nonTarget measurement - // Deactivate all target measurements to activate only nonTarget measurement - if (!isTarget) { - $(element).trigger("NonTargetToolSelected", eventData); - - // Deactivate lesion tool measurements - eventData.type = "inactive"; - - $(element).trigger("LesionToolSelected", eventData); - - } else { - // Trigger event for target measurements - $(element).trigger("LesionToolSelected", eventData); - - // Deactivate nonTarget tool measurements - eventData.type = "inactive"; - - // Triggger event for nonTarget measurements - // Inactivate all nonTarget measurements if any measurement is active - $(element).trigger("NonTargetToolSelected", eventData); - } - } - }, 100); -} - -// Returns timepoint object based on timepoint id of the enabled element -function getTimepointObject(timepoints, enabledElement) { - var imageId = enabledElement.image.imageId; - var study = cornerstoneTools.metaData.get('study', imageId); - var series = cornerstoneTools.metaData.get('series', imageId); - - // Get the timepointID related to the image viewer viewport - // from the DOM itself. This will be changed later when a - // real association between viewports and timepoints is created. - var timepoint = Timepoints.findOne({timepointName: study.studyDate}); - if (!timepoint) { - return; - } - var timepointID = timepoint.timepointID; - - var timepointObject = timepoints[timepointID]; - - if (timepointObject === undefined) { - return; - } - - if (!timepointObject ) { - timepointObject = {} - } - - return timepointObject; -} - -// Activate selected lesions when lesion table row is clicked -function updateLesions(e) { - // lesionNumber of measurement = id of row - var lesionNumber = parseInt($(e.currentTarget).attr("id"), 10); - - // TODO= Clarify this - // Get Target column value - // Search and update data according to target type - var isTarget = $(e.currentTarget).find('td').eq(2).html().trim() === 'N'?false:true; - - // Find data for specific lesion - var measurementData = Measurements.findOne({ - lesionNumber: lesionNumber, - isTarget: isTarget - }); - - if (!measurementData) { - return; - } - - var timepoints = measurementData.timepoints; - var enabledElementsInViewport = getEnabledElementsInViewport(); - - // Render related series - for(var i = 0; i < enabledElementsInViewport.length; i++) { - var element = enabledElementsInViewport[i].element; - var enabledElement = enabledElementsInViewport[i].enabledElement; - - var timepointObject = getTimepointObject(timepoints, enabledElement); - - if (timepointObject === undefined || timepointObject.seriesInstanceUid === "") { - continue; + // If there is no measurement data to display, stop here + if (!measurementAtTimepoint) { + return; } - var newSeriesData = { - seriesInstanceUid: timepointObject.seriesInstanceUid, - studyInstanceUid: timepointObject.studyInstanceUid + // Check which study and series are required to display the measurement at this timepoint + var requiredSeriesData = { + seriesInstanceUid: measurementAtTimepoint.seriesInstanceUid, + studyInstanceUid: measurementAtTimepoint.studyInstanceUid }; - // Rerender the viewport using data for new series - rerenderViewportWithNewSeries(element, newSeriesData); - } + // Check if the study / series we need is already the one in the viewport + var currentSeriesData = OHIF.viewer.loadedSeriesData[viewportIndex]; + if (currentSeriesData.seriesInstanceUid === measurementAtTimepoint.seriesInstanceUid && + currentSeriesData.studyInstanceUid === measurementAtTimepoint.studyInstanceUid) { + // If it is, activate the measurements in this viewport and stop here + activateMeasurements(element, measurementId); + return; + } - // Activate selected measurements on the rendered series - activateMeasurementsInRelatedImages(timepoints, lesionNumber, isTarget); + // Otherwise, re-render the viewport with the required study/series, then + // add an onRendered callback to activate the measurements + rerenderViewportWithNewSeries(element, requiredSeriesData, function(element) { + activateMeasurements(element, measurementId); + }); + }); } Template.lesionTable.helpers({ @@ -155,12 +139,14 @@ Template.lesionTable.helpers({ return Measurements.find({}, {sort: {number: 1}}); }, 'timepoints': function() { - return Timepoints.find(); + return Timepoints.find({}, {sort: {timepointName: 1}}); } }); Template.lesionTable.events({ 'click table#tblLesion tbody tr': function(e) { - updateLesions(e); + // Retrieve the lesion id from the DOM data for this row + var measurementId = $(e.currentTarget).data('measurementid'); + activateLesion(measurementId); } }); \ No newline at end of file diff --git a/Packages/lesiontracker/components/lesionTableRow/lesionTableRow.html b/Packages/lesiontracker/components/lesionTableRow/lesionTableRow.html index 3796d05b8..e064f07a6 100644 --- a/Packages/lesiontracker/components/lesionTableRow/lesionTableRow.html +++ b/Packages/lesiontracker/components/lesionTableRow/lesionTableRow.html @@ -1,5 +1,5 @@