/** * Activates a set of lesions when lesion table row is clicked * * @param measurementId The unique key for a specific Measurement */ activateLesion = function(measurementId, templateData) { // Set background color of selected row $("tr[data-measurementid="+measurementId+"]").addClass("selectedRow").siblings().removeClass("selectedRow"); 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; // Get all non-dummy timepoint entries in the Measurement // TODO=Re-evaluate this approach to populating viewports with timepoints // What is the desired behaviour here? var timepointsWithEntries = []; Object.keys(timepoints).forEach(function(key) { var timepoint = timepoints[key]; if (timepoint.imageId === "" || timepoint.studyInstanceUid === "" || timepoint.seriesInstanceUid === "") { return; } timepointsWithEntries.push(timepoint); }); // If there are no non-dummy timepoint entries, stop here if (!timepointsWithEntries.length) { return; } // Loop through the viewports and display each timepoint $(".imageViewerViewport").each(function(viewportIndex, element) { // Stop if we run out of timepoints before viewports if (viewportIndex >= timepointsWithEntries.length) { // Update the element anyway, to remove any other highlights that are present deactivateAllToolData(element, 'lesion'); deactivateAllToolData(element, 'nonTarget'); cornerstone.updateImage(element); return false; } // Find measurements related to the Nth timepoint // TODO=Re-evaluate this approach to populating viewports with timepoints // What is the desired behaviour here? var measurementAtTimepoint = timepointsWithEntries[viewportIndex]; // Find the image that is currently in this viewport var enabledElement = cornerstone.getEnabledElement(element); if (!enabledElement || !enabledElement.image) { return; } // If there is no measurement data to display, stop here if (!measurementAtTimepoint) { // Update the element anyway, to remove any other highlights that are present deactivateAllToolData(element, 'lesion'); deactivateAllToolData(element, 'nonTarget'); cornerstone.updateImage(element); return; } // Check which study and series are required to display the measurement at this timepoint var requiredSeriesData = { seriesInstanceUid: measurementAtTimepoint.seriesInstanceUid, studyInstanceUid: measurementAtTimepoint.studyInstanceUid }; // 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, templateData, viewportIndex); return; } // 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, templateData, viewportIndex); }); }); } /** * 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); return Timepoints.findOne({timepointName: study.studyDate}); } /** * 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, templateData, viewportIndex) { // 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; } // If type is active, load image and activate lesion // If type is inactive, update lesions of enabledElement as inactive //TODO: !stackData.currentImageIdIndex returns incorrect value // Get loadedSeriesData currentImageIdIndex from ViewerData var contentId = templateData.contentId; var viewerData = ViewerData[contentId]; var elementCurrentImageIdIndex = viewerData.loadedSeriesData[viewportIndex].currentImageIdIndex; var stackToolDataSource = cornerstoneTools.getToolState(element, 'stack'); var stackData = stackToolDataSource.data[0]; var imageIds = stackData.imageIds; var imageIdIndex = imageIds.indexOf(measurementAtTimepoint.imageId); if (imageIdIndex < 0) { return; } if (imageIdIndex === elementCurrentImageIdIndex){ activateTool(element, measurementData, timepointData.timepointID); } else { cornerstone.loadAndCacheImage(imageIds[imageIdIndex]).then(function(image) { cornerstone.displayImage(element, image); activateTool(element, measurementData, timepointData.timepointID); }); } } /** * Activates a specific tool data instance and deactivates all other * target and non-target measurement data * * @param element * @param measurementData * @param timepointID */ function activateTool(element, measurementData, timepointID) { deactivateAllToolData(element, 'lesion'); deactivateAllToolData(element, 'nonTarget'); var toolType = measurementData.isTarget ? 'lesion' : 'nonTarget'; var toolData = cornerstoneTools.getToolState(element, toolType); if (!toolData) { return; } var measurementAtTimepoint = measurementData.timepoints[timepointID]; for (var i = 0; i < toolData.data.length; i++) { data = toolData.data[i]; // When click a row of table measurements, measurement will be active and color will be green // TODO= Remove this with the measurementId once it is in the tool data if (data.seriesInstanceUid === measurementAtTimepoint.seriesInstanceUid && data.studyInstanceUid === measurementAtTimepoint.studyInstanceUid && data.lesionNumber === measurementData.lesionNumber && data.isTarget == measurementData.isTarget) { data.active = true; break; } } cornerstone.updateImage(element); } /** * Sets all tool data entries value for 'active' to false * This is used to remove the active color on entire sets of tools * * @param element The Cornerstone element that is being used * @param toolType The tooltype of the tools that will be deactivated */ function deactivateAllToolData(element, toolType) { var toolData = cornerstoneTools.getToolState(element, toolType); if (!toolData) { return; } for (var i = 0; i < toolData.data.length; i++) { var data = toolData.data[i]; data.active = false; } } Template.lesionTable.helpers({ 'measurement': function() { return Measurements.find({}, {sort: {number: 1}}); }, 'timepoints': function() { return Timepoints.find({}, {sort: {timepointName: 1}}); } }); Template.lesionTable.events({ 'click table#tblLesion tbody tr': function(e, template) { // Retrieve the lesion id from the DOM data for this row var measurementId = $(e.currentTarget).data('measurementid'); activateLesion(measurementId,template.data); }, 'mousedown div#dragbar': function(e, template) { var pY = e.pageY; var draggableParent = $(e.currentTarget).parent(); var startHeight = draggableParent.height(); template.dragging.set(true); $(document).on('mouseup', function(e) { template.dragging.set(false); console.log(e.pageY); $(document).off('mouseup').off('mousemove'); }); $(document).on('mousemove', function(e) { var topPosition = e.pageY - pY; var newHeight = startHeight - topPosition; // Min lesion table height = 5px if(newHeight < 5) { return; } draggableParent.css({ top: topPosition, height: newHeight }); var viewportAndLesionTableHeight = $("#viewportAndLesionTable").height(); var newPercentageHeightofLesionTable = (startHeight - topPosition) / viewportAndLesionTableHeight * 100; var newPercentageHeightofViewermain = 100 - newPercentageHeightofLesionTable; $(".viewerMain").height(newPercentageHeightofViewermain+"%"); // Resize viewport resizeViewportElements(); }); } }); Template.lesionTable.onCreated(function(){ this.dragging = new ReactiveVar(false); // Bind document mouse events // $(document).on('mousemove', dragbarMove); }); Template.lesionTable.onDestroyed(function(){ }); // Track ViewerData to get active timepoints // Put a visual indicator(<) in timepoint header in lesion table for active timepoints // timepointLoaded property is used to put indicator for loaded timepoints in viewport Tracker.autorun(function () { var allViewerData = Session.get('ViewerData'); var contentId = Session.get('activeContentId'); if(allViewerData && contentId) { var viewerData = allViewerData[contentId]; if (viewerData) { // Get study dates of imageViewerViewport elements var loadedStudyDates = { patientId: "", dates: [] }; $(".imageViewerViewport").each(function(viewportIndex, element) { var enabledElement = cornerstone.getEnabledElement(element); if(enabledElement && enabledElement.image){ var imageId = enabledElement.image.imageId; var study = cornerstoneTools.metaData.get('study', imageId); var studyDate = study.studyDate; loadedStudyDates.patientId = study.patientId; // Check studyDate is added before if (loadedStudyDates.dates.indexOf(studyDate) < 0) { loadedStudyDates.dates.push(studyDate); } } }); // If study date is loaded into viewport, set timepointLoaded property in Timepoints collection as true // Else set timepointLoaded property as false if(loadedStudyDates.dates.length) { var timepoints = Timepoints.find({patientId: loadedStudyDates.patientId}).fetch(); timepoints.forEach(function(timepoint){ var timepointLoaded = false; if(loadedStudyDates.dates.indexOf(timepoint.timepointName) > -1) { timepointLoaded = true; } Timepoints.update(timepoint._id,{ $set: { timepointLoaded: timepointLoaded } }); }); } } } });