LT-296: Clicking on measurement data in table activates the measurement
This commit is contained in:
parent
42c938fb44
commit
80337f271e
@ -119,7 +119,9 @@ Template.viewer.onCreated(() => {
|
||||
Template.viewer.helpers({
|
||||
dataSourcesReady() {
|
||||
// TODO: Find a better way to do this
|
||||
return Session.get('TimepointsReady') && Session.get('MeasurementsReady');
|
||||
const ready = Session.get('TimepointsReady') && Session.get('MeasurementsReady');
|
||||
console.log('dataSourcesReady? : ' + ready);
|
||||
return ready;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ Template.caseProgress.onRendered(() => {
|
||||
const api = instance.data.measurementApi;
|
||||
|
||||
const getNumMeasurementsAtTimepoint = timepointId => {
|
||||
OHIF.log.info('getNumMeasurementsAtTimepoint');
|
||||
const filter = {
|
||||
timepointId: timepointId
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
|
||||
@ -48,28 +49,42 @@ Template.measurementTable.onRendered(() => {
|
||||
});
|
||||
|
||||
Template.measurementTable.onRendered(() => {
|
||||
// Find the first measurement by Lesion Number
|
||||
let firstLesion; // = instance.data.measurementApi.firstLesion();
|
||||
// Find and activate the first measurement by Lesion Number
|
||||
// NOTE: This is inefficient, we should be using a hanging protocol
|
||||
// to hang the first measurement's imageId immediately, rather
|
||||
// than changing images after initial loading...
|
||||
const instance = Template.instance();
|
||||
|
||||
// Create an object to store the ContentId inside
|
||||
const templateData = {
|
||||
contentId: Session.get('activeContentId')
|
||||
const config = OHIF.measurements.MeasurementApi.getConfiguration();
|
||||
const measurementTypeId = config.measurementTools[0].id;
|
||||
const measurementApi = instance.data.measurementApi;
|
||||
const collection = measurementApi[measurementTypeId];
|
||||
const sorting = {
|
||||
sort: {
|
||||
measurementNumber: -1
|
||||
}
|
||||
};
|
||||
|
||||
// Activate the first lesion
|
||||
if (firstLesion) {
|
||||
OHIF.measurements.activateLesion(firstLesion._id, templateData);
|
||||
}
|
||||
});
|
||||
const data = collection.find({}, sorting).fetch();
|
||||
|
||||
Template.measurementTable.events({
|
||||
/**
|
||||
* Retrieve the lesion id from the DOM data for this row
|
||||
*/
|
||||
/*'click table#tblLesion tbody tr': function(e, template) {
|
||||
var measurementId = $(e.currentTarget).data('measurementid');
|
||||
OHIF.measurements.activateLesion(measurementId, template.data);
|
||||
},*/
|
||||
const timepoints = instance.data.timepoints.get();
|
||||
|
||||
// TODO: Clean this up, it's probably an inefficient way to get what we need
|
||||
const groupObject = _.groupBy(data, entry => entry.measurementNumber);
|
||||
|
||||
// Reformat the data
|
||||
const rows = Object.keys(groupObject).map(key => ({
|
||||
measurementTypeId: measurementTypeId,
|
||||
measurementNumber: key,
|
||||
entries: groupObject[key]
|
||||
}));
|
||||
|
||||
const rowItem = rows[0];
|
||||
|
||||
// Activate the first lesion
|
||||
if (rowItem) {
|
||||
OHIF.measurements.jumpToRowItem(rowItem, timepoints);
|
||||
}
|
||||
});
|
||||
|
||||
Template.measurementTable.helpers({
|
||||
|
||||
@ -46,19 +46,35 @@ Template.measurementTableHeaderRow.onCreated(() => {
|
||||
|
||||
Template.measurementTableHeaderRow.helpers({
|
||||
numberOfMeasurements() {
|
||||
return Template.instance().data.measurements.length;
|
||||
const instance = Template.instance();
|
||||
if (!instance.data.measurements) {
|
||||
return;
|
||||
}
|
||||
return instance.data.measurements.length;
|
||||
},
|
||||
|
||||
maxNumMeasurements() {
|
||||
return Template.instance().maxNumMeasurements.get();
|
||||
const instance = Template.instance();
|
||||
if (!instance.data.measurements) {
|
||||
return;
|
||||
}
|
||||
return instance.maxNumMeasurements.get();
|
||||
},
|
||||
|
||||
anyUnmarkedLesionsLeft() {
|
||||
// Skip New Lesions section
|
||||
const instance = Template.instance();
|
||||
const measurementType = instance.data.measurementType;
|
||||
const measurementApi = instance.data.measurementApi;
|
||||
const timepointApi = instance.data.timepointApi;
|
||||
if (!instance.data.measurements) {
|
||||
return;
|
||||
}
|
||||
|
||||
const measurementType = instance.data.measurementType;
|
||||
const config = OHIF.measurements.MeasurementApi.getConfiguration();
|
||||
if (measurementType.id === config.newMeasurementTool.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timepointApi = instance.data.timepointApi;
|
||||
const current = instance.data.timepointApi.current();
|
||||
const prior = instance.data.timepointApi.prior();
|
||||
if (!prior) {
|
||||
@ -69,6 +85,7 @@ Template.measurementTableHeaderRow.helpers({
|
||||
const priorFilter = { timepointId: prior.timepointId };
|
||||
const measurementTypeId = measurementType.id;
|
||||
|
||||
const measurementApi = instance.data.measurementApi;
|
||||
const numCurrent = measurementApi.fetch(measurementTypeId, currentFilter).length;
|
||||
const numPrior = measurementApi.fetch(measurementTypeId, priorFilter).length;
|
||||
const remaining = Math.max(numPrior - numCurrent, 0);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template name="measurementTableTimepointCell">
|
||||
{{#if hasDataAtThisTimepoint}}
|
||||
<div class="measurementTableTimepointCell" tabindex="1">
|
||||
<div class="measurementTableTimepointCell noselect" tabindex="1">
|
||||
{{displayData}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="measurementTableTimepointCell empty">
|
||||
<div class="measurementTableTimepointCell noselect empty">
|
||||
...
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
@ -59,39 +59,17 @@ const keys = {
|
||||
};
|
||||
|
||||
Template.measurementTableTimepointCell.events({
|
||||
'dblclick .measurementTableTimepointCell': function() {
|
||||
OHIF.log.info('Double clicked on a timepoint cell');
|
||||
// Search Measurements by lesion and timepoint
|
||||
const currentMeasurement = Template.parentData(1).rowItem;
|
||||
|
||||
// Create some fake measurement data
|
||||
const currentTimepointID = this.timepointId;
|
||||
|
||||
const timepointData = currentMeasurement.timepoints[currentTimepointID];
|
||||
if (!timepointData) {
|
||||
'click .measurementTableTimepointCell'(event, instance) {
|
||||
if (!instance.data.timepointId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let measurementData = {
|
||||
id: currentMeasurement._id,
|
||||
timepointId: currentTimepointID,
|
||||
response: timepointData.response,
|
||||
imageId: timepointData.imageId,
|
||||
handles: timepointData.handles,
|
||||
seriesInstanceUid: timepointData.seriesInstanceUid,
|
||||
studyInstanceUid: timepointData.studyInstanceUid
|
||||
};
|
||||
|
||||
if (currentMeasurement.isTarget) {
|
||||
showConfirmDialog(function() {
|
||||
OHIF.lesiontracker.clearMeasurementTimepointData(currentMeasurement._id, currentTimepointID);
|
||||
});
|
||||
} else {
|
||||
changeNonTargetResponse(measurementData, null, doneCallback);
|
||||
}
|
||||
},
|
||||
'keydown .measurementTableTimepointCell': function(e) {
|
||||
const keyCode = e.which;
|
||||
const rowItem = instance.data.rowItem;
|
||||
const timepoints = instance.data.timepoints.get();
|
||||
OHIF.measurements.jumpToRowItem(rowItem, timepoints);
|
||||
}/*,
|
||||
'keydown .measurementTableTimepointCell'(event, instance) {
|
||||
const keyCode = event.which;
|
||||
if (keyCode === keys.DELETE ||
|
||||
(keyCode === keys.D && e.ctrlKey === true)) {
|
||||
const currentMeasurement = Template.parentData(1).rowItem;
|
||||
@ -101,5 +79,5 @@ Template.measurementTableTimepointCell.events({
|
||||
OHIF.lesiontracker.clearMeasurementTimepointData(currentMeasurement._id, currentTimepointID);
|
||||
});
|
||||
}
|
||||
}
|
||||
}*/
|
||||
});
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.measurementTableTimepointCell
|
||||
theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
|
||||
theme('color', '$textPrimaryColor')
|
||||
padding: 0 10px
|
||||
theme('border-left', '%s solid $uiBorderColor' % $uiBorderThickness)
|
||||
theme('color', '$textPrimaryColor')
|
||||
padding: 0 10px
|
||||
outline: none
|
||||
cursor: pointer
|
||||
|
||||
&:hover
|
||||
theme('color', '$activeColor')
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
{{#if config.newMeasurementTool}}
|
||||
{{#let measurementType=(getNewMeasurementType config.newMeasurementTool)}}
|
||||
{{#let newMeasure=(newMeasurements measurementType)}}
|
||||
{{#if newMeasure}}
|
||||
{{#if newMeasure.length}}
|
||||
{{#let collection=newMeasure}}
|
||||
{{>measurementTableHeaderRow
|
||||
measurementType=measurementType
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
const getLocation = collection => {
|
||||
OHIF.measurements.getLocation = collection => {
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
if (collection[i].location) {
|
||||
return collection[i].location;
|
||||
@ -9,6 +9,8 @@ const getLocation = collection => {
|
||||
}
|
||||
};
|
||||
|
||||
const getLocation = OHIF.measurements.getLocation;
|
||||
|
||||
Template.measurementTableView.helpers({
|
||||
getNewMeasurementType(tool) {
|
||||
// TODO: Check Conformance criteria here.
|
||||
@ -25,13 +27,13 @@ Template.measurementTableView.helpers({
|
||||
const instance = Template.instance();
|
||||
const measurementApi = instance.data.measurementApi;
|
||||
const timepointApi = instance.data.timepointApi;
|
||||
if (!measurementApi || !timepointApi) {
|
||||
const baseline = timepointApi.baseline();
|
||||
if (!measurementApi || !timepointApi || !baseline) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve all the data for this Measurement type (e.g. 'targets')
|
||||
// which was recorded at baseline.
|
||||
const baseline = timepointApi.baseline();
|
||||
const atBaseline = measurementApi.fetch(measurementTypeId, {
|
||||
timepointId: baseline.timepointId
|
||||
});
|
||||
@ -65,14 +67,15 @@ Template.measurementTableView.helpers({
|
||||
const instance = Template.instance();
|
||||
const measurementApi = instance.data.measurementApi;
|
||||
const timepointApi = instance.data.timepointApi;
|
||||
const measurementTypeId = measurementType.measurementTypeId;
|
||||
const current = instance.data.timepointApi.current();
|
||||
const baseline = timepointApi.baseline();
|
||||
|
||||
if (!timepointApi) {
|
||||
if (!measurementApi || !timepointApi || !current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a baseline, stop here since there are no new measurements to display
|
||||
const current = instance.data.timepointApi.current();
|
||||
|
||||
if (!current || current.timepointType === 'baseline') {
|
||||
console.log('Skipping New Measurements section');
|
||||
return;
|
||||
@ -80,7 +83,7 @@ Template.measurementTableView.helpers({
|
||||
|
||||
// Retrieve all the data for this Measurement type (e.g. 'targets')
|
||||
// which was recorded at baseline.
|
||||
const baseline = timepointApi.baseline();
|
||||
const measurementTypeId = measurementType.measurementTypeId;
|
||||
const atBaseline = measurementApi.fetch(measurementTypeId, {
|
||||
timepointId: baseline.timepointId
|
||||
});
|
||||
|
||||
@ -7,9 +7,8 @@
|
||||
overflow-y: auto
|
||||
margin-left: -6px
|
||||
margin-right: -16px
|
||||
padding-bottom: 100px
|
||||
padding-bottom: 116px
|
||||
padding-left: 6px
|
||||
|
||||
-webkit-overflow-scrolling: touch
|
||||
|
||||
&::-webkit-scrollbar
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
class MeasurementHandlers {
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* Activates a set of lesions when lesion table row is clicked
|
||||
*
|
||||
* @param measurementId The unique key for a specific Measurement
|
||||
*/
|
||||
OHIF.measurements.activateLesion = (measurementId, templateData) => {
|
||||
// Set background color of selected row
|
||||
$(`tr[data-measurementid=${measurementId}]`).addClass('selectedRow')
|
||||
.siblings().removeClass('selectedRow');
|
||||
|
||||
// Get the data for current measurement
|
||||
const measurementData = Measurements.findOne(measurementId);
|
||||
|
||||
// If there is no measurement with this ID, stop here
|
||||
if (!measurementData) {
|
||||
OHIF.OHIF.log.warn('No Measurements entry associated to an ID in a lesion table row');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an empty array to store the ordered timepoint data for this measurement
|
||||
const orderedTimepointEntries = [];
|
||||
|
||||
// Retrieve the Cursor for this patient's Timepoints in ascending order by studyDate
|
||||
const sortedTimepoints = Timepoints.find({}, {
|
||||
sort: {
|
||||
latestDate: 1
|
||||
}
|
||||
});
|
||||
|
||||
// Loop through each timepoint and populate the orderTimepointEntries array with the
|
||||
// measurement data at each timepoint. The most recent measurements will be first in the array.
|
||||
sortedTimepoints.forEach(timepoint => {
|
||||
const measurementDataAtTimepoint = measurementData.timepoints[timepoint.timepointId];
|
||||
if (!measurementDataAtTimepoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
orderedTimepointEntries.push(measurementDataAtTimepoint);
|
||||
});
|
||||
|
||||
// If there is no timepoint data to display for this Measurement, stop here
|
||||
if (!orderedTimepointEntries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the list of available viewports
|
||||
const $viewports = $('.imageViewerViewport').not('.empty');
|
||||
|
||||
// Remove earlier timepoint data entries
|
||||
if (orderedTimepointEntries.length > $viewports.length) {
|
||||
const difference = orderedTimepointEntries.length - $viewports.length;
|
||||
orderedTimepointEntries.splice(0, difference);
|
||||
}
|
||||
|
||||
// Loop through the viewports and display each timepoint
|
||||
$viewports.each((viewportIndex, element) => {
|
||||
// Stop if we run out of timepoints before viewports
|
||||
if (viewportIndex >= orderedTimepointEntries.length) {
|
||||
// Update the element anyway, to remove any other highlights that are present
|
||||
OHIF.measurements.deactivateAllToolData(element, 'bidirectional');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'nonTarget');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'crTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'unTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'exTool');
|
||||
|
||||
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?
|
||||
const measurementAtTimepoint = orderedTimepointEntries[viewportIndex];
|
||||
|
||||
// Find the image that is currently in this viewport
|
||||
const 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
|
||||
OHIF.measurements.deactivateAllToolData(element, 'bidirectional');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'nonTarget');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'crTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'unTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'exTool');
|
||||
cornerstone.updateImage(element);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check which study and series are required to display the measurement at this timepoint
|
||||
const requiredSeriesData = {
|
||||
seriesInstanceUid: measurementAtTimepoint.seriesInstanceUid,
|
||||
studyInstanceUid: measurementAtTimepoint.studyInstanceUid
|
||||
};
|
||||
|
||||
// Check if the study / series we need is already the one in the viewport
|
||||
const currentSeriesData = OHIF.viewer.loadedSeriesData[viewportIndex];
|
||||
const activateMeasurements = OHIF.measurements.activateMeasurements;
|
||||
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
|
||||
window.layoutManager.rerenderViewportWithNewDisplaySet(element, requiredSeriesData, element => {
|
||||
activateMeasurements(element, measurementId, templateData, viewportIndex);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1,51 +1,55 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* Activates a specific tool data instance and deactivates all other
|
||||
* target and non-target measurement data
|
||||
*
|
||||
* @param element
|
||||
* @param measurementData
|
||||
*/
|
||||
function activateTool(measurementData) {
|
||||
const toolType = measurementData.toolType;
|
||||
const imageId = measurementData.imageId;
|
||||
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
||||
const toolData = toolState[imageId][toolType];
|
||||
if (!toolData || !toolData.data || !toolData.data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When a measurement is selected, it will be activated in Cornerstone's
|
||||
// tool data
|
||||
const tool = toolData.data.find(data => data._id === measurementData._id);
|
||||
tool.active = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
OHIF.measurements.activateMeasurements = (element, measurementId, templateData, viewportIndex) => {
|
||||
// TODO=Switch this to use the new CornerstoneToolMeasurementModified event,
|
||||
// Once it has 'modified on activation' set up
|
||||
OHIF.measurements.activateMeasurements = (element, measurementData) => {
|
||||
console.log('activateMeasurements');
|
||||
|
||||
// Activate the tool in the tool data
|
||||
activateTool(measurementData);
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const timepointData = OHIF.measurements.getTimepointObject(imageId);
|
||||
const measurementData = Measurements.findOne(measurementId);
|
||||
const currentImageId = enabledElement.image.imageId;
|
||||
const toolData = cornerstoneTools.getToolState(element, 'stack');
|
||||
const imageIdIndex = toolData.data[0].imageIds.indexOf(measurementData.imageId);
|
||||
|
||||
if (!timepointData) {
|
||||
return;
|
||||
// If we aren't currently displaying the image that this tool is on,
|
||||
// scroll to it now.
|
||||
if (currentImageId !== measurementData.imageId) {
|
||||
cornerstoneTools.scrollToIndex(element, imageIdIndex);
|
||||
}
|
||||
|
||||
const 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
|
||||
const viewerData = window.ViewerData[templateData.contentId];
|
||||
const loadedSeriesData = viewerData.loadedSeriesData[viewportIndex];
|
||||
const elementCurrentImageIdIndex = loadedSeriesData.currentImageIdIndex;
|
||||
|
||||
const stackToolDataSource = cornerstoneTools.getToolState(element, 'stack');
|
||||
const stackData = stackToolDataSource.data[0];
|
||||
const imageIds = stackData.imageIds;
|
||||
const imageIdIndex = imageIds.indexOf(measurementAtTimepoint.imageId);
|
||||
|
||||
if (imageIdIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (imageIdIndex === elementCurrentImageIdIndex) {
|
||||
OHIF.measurements.activateTool(element, measurementData, timepointData.timepointId);
|
||||
} else {
|
||||
cornerstone.loadAndCacheImage(measurementAtTimepoint.imageId).then(image => {
|
||||
cornerstone.displayImage(element, image);
|
||||
OHIF.measurements.activateTool(element, measurementData, timepointData.timepointId);
|
||||
});
|
||||
}
|
||||
};
|
||||
// TODO: Find another way to do this?
|
||||
// This might update one element twice, but at least it makes sure all viewports are
|
||||
// updated and the highlight is removed from inactive tools in all visible viewports
|
||||
const $viewports = $('.imageViewerViewport');
|
||||
$viewports.each((index, element) => {
|
||||
cornerstone.updateImage(element)
|
||||
});
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* Activates a specific tool data instance and deactivates all other
|
||||
* target and non-target measurement data
|
||||
*
|
||||
* @param element
|
||||
* @param measurementData
|
||||
* @param timepointId
|
||||
*/
|
||||
OHIF.measurements.activateTool = (element, measurementData, timepointId) => {
|
||||
OHIF.measurements.deactivateAllToolData(element, 'bidirectional');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'nonTarget');
|
||||
|
||||
// Deactivate CRUNEX Tools
|
||||
OHIF.measurements.deactivateAllToolData(element, 'crTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'unTool');
|
||||
OHIF.measurements.deactivateAllToolData(element, 'exTool');
|
||||
|
||||
const toolType = measurementData.toolType;
|
||||
const toolData = cornerstoneTools.getToolState(element, toolType);
|
||||
if (!toolData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const measurementAtTimepoint = measurementData.timepoints[timepointId];
|
||||
|
||||
for (let i = 0; i < toolData.data.length; i++) {
|
||||
const 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);
|
||||
};
|
||||
@ -3,17 +3,22 @@ import { OHIF } from 'meteor/ohif:core';
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
OHIF.measurements.deactivateAllToolData = (element, toolType) => {
|
||||
const toolData = cornerstoneTools.getToolState(element, toolType);
|
||||
if (!toolData) {
|
||||
return;
|
||||
}
|
||||
OHIF.measurements.deactivateAllToolData = () => {
|
||||
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
||||
|
||||
for (let i = 0; i < toolData.data.length; i++) {
|
||||
toolData.data[i].active = false;
|
||||
}
|
||||
Object.keys(toolState).forEach(imageId => {
|
||||
const toolData = toolState[imageId];
|
||||
|
||||
Object.keys(toolData).forEach(toolType => {
|
||||
const specificToolData = toolData[toolType];
|
||||
if (!specificToolData || !specificToolData.data || !specificToolData.data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
specificToolData.data.forEach(data => {
|
||||
data.active = false;
|
||||
});
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* Returns timepoint object given a specified imageId
|
||||
*
|
||||
* @param imageId
|
||||
* @returns {*|{}} Timepoint object
|
||||
*/
|
||||
OHIF.measurements.getTimepointObject = imageId => {
|
||||
const study = cornerstoneTools.metaData.get('study', imageId);
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Timepoints.findOne({
|
||||
studyInstanceUids: {
|
||||
$in: [study.studyInstanceUid]
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -1,9 +1,7 @@
|
||||
import './activateLesion.js';
|
||||
import './jumpToRowItem.js';
|
||||
import './activateMeasurements.js';
|
||||
import './activateTool.js';
|
||||
import './deactivateAllToolData.js';
|
||||
import './getTimepointName.js';
|
||||
import './getTimepointObject.js';
|
||||
import './hangingProtocolCustomizations.js';
|
||||
import './MeasurementHandlers.js';
|
||||
import './MeasurementManager.js';
|
||||
|
||||
137
Packages/ohif-measurements/client/lib/jumpToRowItem.js
Normal file
137
Packages/ohif-measurements/client/lib/jumpToRowItem.js
Normal file
@ -0,0 +1,137 @@
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
function findAndRenderDisplaySet(displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback) {
|
||||
// Find the proper stack to display
|
||||
const stacksFromSeries = displaySets.filter(stack => stack.seriesInstanceUid === seriesInstanceUid);
|
||||
stack = stacksFromSeries.find(stack => {
|
||||
const imageIndex = stack.images.findIndex(image => image.sopInstanceUid === sopInstanceUid);
|
||||
return imageIndex > -1;
|
||||
});
|
||||
|
||||
// TODO: make this work for multi-frame instances
|
||||
const specificImageIndex = stack.images.findIndex(image => image.sopInstanceUid === sopInstanceUid);
|
||||
|
||||
const displaySetData = {
|
||||
studyInstanceUid: studyInstanceUid,
|
||||
seriesInstanceUid: seriesInstanceUid,
|
||||
sopInstanceUid: sopInstanceUid,
|
||||
displaySetInstanceUid: stack.displaySetInstanceUid,
|
||||
currentImageIdIndex: specificImageIndex
|
||||
};
|
||||
|
||||
// Add a renderedCallback to activate the measurements once it's
|
||||
if (renderedCallback) {
|
||||
displaySetData.renderedCallback = renderedCallback;
|
||||
}
|
||||
|
||||
window.layoutManager.rerenderViewportWithNewDisplaySet(viewportIndex, displaySetData);
|
||||
}
|
||||
|
||||
function renderIntoViewport(viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback) {
|
||||
// First, check if we already have this study loaded
|
||||
const alreadyLoadedStudy = ViewerStudies.findOne({studyInstanceUid});
|
||||
|
||||
if (alreadyLoadedStudy) {
|
||||
// If the Study is already loaded, find the display set and render it
|
||||
findAndRenderDisplaySet(alreadyLoadedStudy.displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback)
|
||||
} else {
|
||||
// If not, retrieve the study metadata and then find the relevant display set and
|
||||
// render it.
|
||||
getStudyMetadata(study.studyInstanceUid, loadedStudy => {
|
||||
loadedStudy.displaySets = createStacks(loadedStudy);
|
||||
ViewerStudies.insert(loadedStudy);
|
||||
|
||||
findAndRenderDisplaySet(loadedStudy.displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplaySetData(instance, series, study) {
|
||||
// First, check if we already have this study loaded
|
||||
var alreadyLoaded = ViewerStudies.findOne({
|
||||
studyInstanceUid: study.studyInstanceUid
|
||||
});
|
||||
|
||||
// If not, retrieve it.
|
||||
if (!alreadyLoaded) {
|
||||
getStudyMetadata(priorStudy.studyInstanceUid, study => {
|
||||
study.abstractPriorValue = abstractPriorValue;
|
||||
study.displaySets = createStacks(study);
|
||||
ViewerStudies.insert(study);
|
||||
this.studies.push(study);
|
||||
this.matchImages(viewport);
|
||||
this.updateViewports();
|
||||
});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates a set of lesions when lesion table row is clicked
|
||||
*
|
||||
* @param measurementId The unique key for a specific Measurement
|
||||
*/
|
||||
OHIF.measurements.jumpToRowItem = (rowItem, timepoints) => {
|
||||
OHIF.measurements.deactivateAllToolData();
|
||||
|
||||
const activateMeasurements = OHIF.measurements.activateMeasurements;
|
||||
console.log('jumpToRowItem');
|
||||
|
||||
// Retrieve the timepoints that are currently being displayed in the
|
||||
// Measurement Table
|
||||
const numTimepoints = timepoints.length;
|
||||
|
||||
// Retrieve the list of available viewports
|
||||
const $viewports = $('.imageViewerViewport');
|
||||
const numViewports = $viewports.length;
|
||||
|
||||
/*
|
||||
Two Timepoints, Two measurements, load Followup (FU and BA), display FU in left and BA in right
|
||||
Two Timepoints, One measurement (BA), on 2x1 view: Display BA in right
|
||||
Two Timepoints, One measurement (FU), on 2x1 view: Display FU in left
|
||||
|
||||
Two Timepoints, Two measurements, load Baseline (FU and BA) on 1x1 view: Display whichever is clicked on?
|
||||
One Timepoint, One measurement: Display clicked on in 1x1
|
||||
*/
|
||||
const numViewportsToUpdate = Math.min(numTimepoints, numViewports);
|
||||
|
||||
for (var i=0; i < numViewportsToUpdate; i++) {
|
||||
const timepoint = timepoints[i];
|
||||
const timepointId = timepoint.timepointId;
|
||||
|
||||
const dataAtThisTimepoint = _.where(rowItem.entries, {timepointId: timepointId});
|
||||
if (!dataAtThisTimepoint || !dataAtThisTimepoint.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const measurementData = dataAtThisTimepoint[0];
|
||||
|
||||
// Check if the study / series we need is already the one in the viewport
|
||||
const element = $viewports.get(i);
|
||||
const enabledElement = cornerstone.getEnabledElement(element)
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
const series = cornerstoneTools.metaData.get('series', imageId);
|
||||
const study = cornerstoneTools.metaData.get('study', imageId);
|
||||
|
||||
if (series.seriesInstanceUid === measurementData.seriesInstanceUid &&
|
||||
study.studyInstanceUid === measurementData.studyInstanceUid) {
|
||||
// If it is, activate the measurements in this viewport and stop here
|
||||
activateMeasurements(element, measurementData);
|
||||
} else {
|
||||
// Otherwise, re-render the viewport with the required study/series, then
|
||||
// add an onRendered callback to activate the measurements
|
||||
const renderedCallback = element => {
|
||||
activateMeasurements(element, measurementData);
|
||||
};
|
||||
|
||||
// TODO: Support frames? e.g. for measurements on multi-frame instances
|
||||
renderIntoViewport(i,
|
||||
measurementData.studyInstanceUid,
|
||||
measurementData.seriesInstanceUid,
|
||||
measurementData.sopInstanceUid,
|
||||
renderedCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -151,7 +151,7 @@ LayoutManager = class LayoutManager {
|
||||
seriesInstanceUid: data.seriesInstanceUid,
|
||||
studyInstanceUid: data.studyInstanceUid,
|
||||
renderedCallback: data.renderedCallback,
|
||||
currentImageIdIndex: 0
|
||||
currentImageIdIndex: data.currentImageIdIndex || 0
|
||||
};
|
||||
|
||||
// Remove the hover styling
|
||||
|
||||
Loading…
Reference in New Issue
Block a user