First pass at a Hanging Protocol implementation and editor (LT-131)
This commit is contained in:
1 parent
dfcb33153f
commit
2e52d9e3c1
157 files changed
+8869
-23585
No files matched your search
@@ -19,7 +19,6 @@ function updateFramerate(rate) {
|
||||
|
||||
// If the movie is playing, stop/start to update the framerate
|
||||
if (isPlaying()) {
|
||||
var element = getActiveViewportElement();
|
||||
cornerstoneTools.stopClip(element);
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<template name="imageViewerViewports">
|
||||
<template name="gridLayout">
|
||||
<div id='imageViewerViewports'>
|
||||
{{ #each viewerWindow }}
|
||||
{{ #each viewports }}
|
||||
<div class="viewportContainer" style="height:{{height}}%;width:{{width}}%;">
|
||||
<div class="removable">
|
||||
{{ >imageViewerViewport}}
|
||||
@@ -0,0 +1,38 @@
|
||||
Template.gridLayout.helpers({
|
||||
height: function() {
|
||||
var rows = this.rows || 1;
|
||||
return 100 / rows;
|
||||
},
|
||||
width: function() {
|
||||
var columns = this.columns || 1;
|
||||
return 100 / columns;
|
||||
},
|
||||
viewports: function() {
|
||||
var numViewports = this.rows * this.columns;
|
||||
var viewportData = this.viewportData;
|
||||
var numViewportsWithData = this.viewportData.length;
|
||||
|
||||
if (numViewportsWithData < numViewports) {
|
||||
var difference = numViewports - numViewportsWithData;
|
||||
for (var i = 0; i < difference; i++) {
|
||||
viewportData.push({
|
||||
viewportIndex: numViewportsWithData + i + 1,
|
||||
rows: this.rows,
|
||||
columns: this.columns
|
||||
});
|
||||
}
|
||||
} else if (numViewportsWithData > numViewports) {
|
||||
return viewportData.slice(0, numViewports);
|
||||
}
|
||||
|
||||
return viewportData;
|
||||
}
|
||||
});
|
||||
|
||||
Template.gridLayout.onRendered(function() {
|
||||
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('CornerstoneNewImage', cornerstoneTools.updateImageSynchronizer);
|
||||
});
|
||||
|
||||
Template.gridLayout.onDestroyed(function() {
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
});
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#imageViewerViewports
|
||||
height: calc(100% - 30px)
|
||||
height: 100%
|
||||
width: 100%
|
||||
|
||||
|
||||
.viewportContainer
|
||||
float: left
|
||||
border: 2px solid #777
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<template name="hangingProtocolButtons">
|
||||
<button id="previousPresentationGroup" type="button"
|
||||
class="imageViewerCommand btn btn-sm btn-default"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Previous presentation group"
|
||||
disabled="{{ #unless isPreviousAvailable }} true {{ /unless }}">
|
||||
<span class="fa fa-step-backward"></span>
|
||||
</button>
|
||||
<button id="nextPresentationGroup" type="button"
|
||||
class="imageViewerCommand btn btn-sm btn-default"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Next presentation group"
|
||||
disabled="{{ #unless isNextAvailable }} true {{ /unless }}">
|
||||
<span class="fa fa-step-forward"></span>
|
||||
</button>
|
||||
</template>
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
Template.hangingProtocolButtons.helpers({
|
||||
isNextAvailable: function() {
|
||||
var presentationGroup = Session.get('WindowManagerPresentationGroup');
|
||||
var numPresentationGroups = WindowManager.getNumPresentationGroups();
|
||||
return presentationGroup < numPresentationGroups;
|
||||
},
|
||||
isPreviousAvailable: function() {
|
||||
var presentationGroup = Session.get('WindowManagerPresentationGroup');
|
||||
return presentationGroup > 1;
|
||||
}
|
||||
});
|
||||
+57
-35
@@ -21,6 +21,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
// The viewport index is often used to store information about a viewport element
|
||||
var element = data.element;
|
||||
var viewportIndex = $('.imageViewerViewport').index(element);
|
||||
layoutManager.viewportData[viewportIndex].viewportIndex = viewportIndex;
|
||||
|
||||
// Get the contentID of the current worklist tab, if the viewport is running
|
||||
// alongside the worklist package
|
||||
@@ -140,15 +141,47 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a saved object containing Cornerstone viewport data
|
||||
// (e.g. scale, invert, window settings) in the input data, apply it now.
|
||||
//
|
||||
// Otherwise, display the loaded image in the viewport element with the
|
||||
// default viewport settings.
|
||||
if (data.viewport) {
|
||||
// Update the enabled element with the image and viewport data
|
||||
// This is not usually necessary, but we need them stored in case
|
||||
// a sopClassUid-specific viewport setting is present.
|
||||
enabledElement.image = image;
|
||||
enabledElement.viewport = cornerstone.getDefaultViewport(enabledElement.canvas, image);
|
||||
|
||||
// Check if there are default viewport settings for this sopClassUid
|
||||
if (!series.instances || !series.instances.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var instance = series.instances[0];
|
||||
var instanceClassViewport = getInstanceClassDefaultViewport(instance, enabledElement, image.imageId);
|
||||
|
||||
// If there are sopClassUid-specific viewport settings, apply them
|
||||
if (instanceClassViewport) {
|
||||
cornerstone.displayImage(element, image, instanceClassViewport);
|
||||
|
||||
// Mark that this element should not be fit to the window in the resize listeners
|
||||
// TODO: Find another way to do this?
|
||||
enabledElement.fitToWindow = false;
|
||||
|
||||
// Resize the canvas to fit the current viewport element size.
|
||||
cornerstone.resize(element, false);
|
||||
} else if (data.viewport) {
|
||||
// If there is a saved object containing Cornerstone viewport data
|
||||
// (e.g. scale, invert, window settings) in the input data, apply it now.
|
||||
cornerstone.displayImage(element, image, data.viewport);
|
||||
|
||||
// Resize the canvas to fit the current viewport element size. Fit the displayed
|
||||
// image to the canvas dimensions.
|
||||
cornerstone.resize(element, true);
|
||||
} else {
|
||||
// If no saved viewport settings or modality-specific settings exists,
|
||||
// display the loaded image in the viewport element with no loaded viewport
|
||||
// settings.
|
||||
cornerstone.displayImage(element, image);
|
||||
|
||||
// Resize the canvas to fit the current viewport element size. Fit the displayed
|
||||
// image to the canvas dimensions.
|
||||
cornerstone.resize(element, true);
|
||||
}
|
||||
|
||||
// Remove the data for this viewport from the ViewportLoading object
|
||||
@@ -159,10 +192,6 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
// (e.g. hide the progress text box)
|
||||
endLoadingHandler(element);
|
||||
|
||||
// Resize the canvas to fit the current viewport element size. Fit the displayed
|
||||
// image to the canvas dimensions.
|
||||
cornerstone.resize(element, true);
|
||||
|
||||
// Remove the 'empty' class from the viewport to hide any instruction text
|
||||
element.classList.remove('empty');
|
||||
|
||||
@@ -172,7 +201,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
$(element).siblings('.imageViewerViewportOverlay').show();
|
||||
|
||||
// Add stack state managers for the stack tool, CINE tool, and reference lines
|
||||
cornerstoneTools.addStackStateManager(element, [ 'stack', 'playClip', 'referenceLines' ]);
|
||||
cornerstoneTools.addStackStateManager(element, ['stack', 'playClip', 'referenceLines']);
|
||||
|
||||
// Get the current viewport settings
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
@@ -206,7 +235,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
// Use the tool manager to enable the currently active tool for this
|
||||
// newly rendered element
|
||||
var activeTool = toolManager.getActiveTool();
|
||||
toolManager.setActiveTool(activeTool, [ element ]);
|
||||
toolManager.setActiveTool(activeTool, [element]);
|
||||
|
||||
// Define a function to run whenever the Cornerstone viewport is rendered
|
||||
// (e.g. following a change of window or zoom)
|
||||
@@ -220,6 +249,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
// Save the current viewport into the ViewerData global variable, as well as the
|
||||
// Meteor Session. This lets the viewport be saved/reloaded on a hot-code reload
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
layoutManager.viewportData[viewportIndex].viewport = viewport;
|
||||
ViewerData[contentId].loadedSeriesData[viewportIndex].viewport = viewport;
|
||||
Session.set('ViewerData', ViewerData);
|
||||
}
|
||||
@@ -240,6 +270,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
// This allows the template helpers to update reactively
|
||||
templateData.imageId = eventData.enabledElement.image.imageId;
|
||||
Session.set('CornerstoneNewImage' + viewportIndex, Random.id());
|
||||
layoutManager.viewportData[viewportIndex].imageId = eventData.enabledElement.image.imageId;
|
||||
|
||||
// Get the element and stack data
|
||||
var element = e.target;
|
||||
@@ -260,6 +291,7 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
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);
|
||||
layoutManager.viewportData[viewportIndex].currentImageIdIndex = imageIdIndex;
|
||||
ViewerData[contentId].loadedSeriesData[viewportIndex].currentImageIdIndex = imageIdIndex;
|
||||
Session.set('ViewerData', ViewerData);
|
||||
}
|
||||
@@ -327,14 +359,17 @@ function loadSeriesIntoViewport(data, templateData) {
|
||||
$(element).off(allCornerstoneEvents, sendActivationTrigger);
|
||||
$(element).on(allCornerstoneEvents, sendActivationTrigger);
|
||||
|
||||
// Store the current series data inside a global variable
|
||||
OHIF.viewer.loadedSeriesData[viewportIndex] = {
|
||||
// Store the current series data inside the Layout Manager
|
||||
layoutManager.viewportData[viewportIndex] = {
|
||||
imageId: imageId,
|
||||
studyInstanceUid: data.studyInstanceUid,
|
||||
seriesInstanceUid: data.seriesInstanceUid,
|
||||
currentImageIdIndex: data.currentImageIdIndex,
|
||||
viewport: viewport
|
||||
viewport: viewport,
|
||||
viewportIndex: viewportIndex
|
||||
};
|
||||
ViewerData[contentId].loadedSeriesData = OHIF.viewer.loadedSeriesData;
|
||||
|
||||
ViewerData[contentId].loadedSeriesData = layoutManager.viewportData;
|
||||
|
||||
// Check if image plane (orientation / loction) data is present for the current image
|
||||
var imagePlane = cornerstoneTools.metaData.get('imagePlane', image.imageId);
|
||||
@@ -443,7 +478,7 @@ Meteor.startup(function() {
|
||||
});
|
||||
|
||||
Template.imageViewerViewport.onCreated(function() {
|
||||
log.info('imageViewerViewport onCreated');
|
||||
log.info('imageViewerViewport onCreated');
|
||||
});
|
||||
|
||||
Template.imageViewerViewport.onRendered(function() {
|
||||
@@ -487,23 +522,6 @@ Template.imageViewerViewport.onRendered(function() {
|
||||
});
|
||||
var seriesInstanceUid = this.data.seriesInstanceUid;
|
||||
|
||||
// TODO: This code block might be refactored
|
||||
// Load previous measurement study when reloading a patient
|
||||
if (!study) {
|
||||
getStudyMetadata(this.data.studyInstanceUid, function(study) {
|
||||
// Once we have retrieved the data, we sort the series' by series
|
||||
// and instance number in ascending order
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
sortStudy(study);
|
||||
data.study = study;
|
||||
|
||||
setSeries(data, seriesInstanceUid, templateData);
|
||||
})
|
||||
}
|
||||
|
||||
data.study = study;
|
||||
setSeries(data, seriesInstanceUid, templateData);
|
||||
});
|
||||
@@ -539,5 +557,9 @@ Template.imageViewerViewport.events({
|
||||
'click .imageViewerViewport': function(e) {
|
||||
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
||||
Session.set('activeViewport', viewportIndex);
|
||||
},
|
||||
'dblclick .imageViewerViewport': function(e) {
|
||||
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
||||
layoutManager.toggleEnlargement(viewportIndex);
|
||||
}
|
||||
});
|
||||
});
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
ViewerWindows = new Meteor.Collection(null);
|
||||
ViewerWindows._debugName = 'ViewerWindows';
|
||||
|
||||
Template.imageViewerViewports.helpers({
|
||||
height: function() {
|
||||
var viewportRows = this.viewportRows || 1;
|
||||
return 100 / viewportRows;
|
||||
},
|
||||
width: function() {
|
||||
var viewportColumns = this.viewportColumns || 1;
|
||||
return 100 / viewportColumns;
|
||||
},
|
||||
viewerWindow: function() {
|
||||
log.info('ViewerWindows');
|
||||
//log.info(ViewerWindows.find().fetch());
|
||||
ViewerWindows.remove({});
|
||||
|
||||
log.info('imageViewerViewports viewportArray');
|
||||
|
||||
var viewportRows = this.viewportRows || 1;
|
||||
var viewportColumns = this.viewportColumns || 1;
|
||||
|
||||
var contentId = Session.get('activeContentId');
|
||||
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 viewportData;
|
||||
if (!$.isEmptyObject(ViewerData[contentId].loadedSeriesData)) {
|
||||
viewportData = ViewerData[contentId].loadedSeriesData;
|
||||
}
|
||||
|
||||
var inputData = {
|
||||
viewportColumns: viewportColumns,
|
||||
viewportRows: viewportRows
|
||||
};
|
||||
|
||||
inputData.DisplaySetPresentationGroup = Session.get('WindowManagerPresentationGroup');
|
||||
var hangingProtocolViewportData = WindowManager.getHangingProtocol(inputData);
|
||||
if (Session.get('UseHangingProtocol')) {
|
||||
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 = {
|
||||
viewportIndex: i,
|
||||
// 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 && !$.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 && !$.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);
|
||||
}
|
||||
|
||||
// Here we will find out if we need to load any other studies into the viewer
|
||||
|
||||
// We will make a list of unique studyInstanceUids
|
||||
var uniqueStudyInstanceUids = [];
|
||||
|
||||
// Meteor doesn't support Mongo's 'distinct' function, so we have to do this in a loop
|
||||
var windows = ViewerWindows.find({}, {
|
||||
reactive: false
|
||||
}).fetch();
|
||||
|
||||
windows.forEach(function(window) {
|
||||
var studyInstanceUid = window.studyInstanceUid;
|
||||
if (!studyInstanceUid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this studyInstanceUid is already in the list, stop here
|
||||
if (uniqueStudyInstanceUids.indexOf(studyInstanceUid) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, add it to the list
|
||||
uniqueStudyInstanceUids.push(studyInstanceUid);
|
||||
|
||||
// If any of the associated studies is not already loaded, load it now
|
||||
var loadedStudy = ViewerStudies.findOne({
|
||||
studyInstanceUid: studyInstanceUid
|
||||
}, {
|
||||
reactive: false
|
||||
});
|
||||
|
||||
if (!loadedStudy) {
|
||||
// Load the study
|
||||
getStudyMetadata(studyInstanceUid, function(study) {
|
||||
log.info("imageViewerViewports GetStudyMetadata: " + studyInstanceUid);
|
||||
|
||||
// Sort the study's series and instances by series and instance number
|
||||
sortStudy(study);
|
||||
|
||||
// Insert it into the ViewerStudies Collection
|
||||
ViewerStudies.insert(study);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ViewerWindows.find({}, {
|
||||
reactive: false
|
||||
}).fetch();
|
||||
}
|
||||
});
|
||||
|
||||
var savedSeriesData,
|
||||
savedViewportRows,
|
||||
savedViewportColumns;
|
||||
|
||||
Template.imageViewerViewports.events({
|
||||
'CornerstoneMouseDoubleClick .imageViewerViewport': function(e) {
|
||||
var container = $('.viewerMain').get(0);
|
||||
var data;
|
||||
var contentId = this.contentId || $('#viewer').parents('.tab-pane.active').attr('id');
|
||||
|
||||
// If there is more than one viewport on screen
|
||||
// And one of them is double-clicked, it should be rendered alone
|
||||
// If it is double-clicked again, the viewer should revert to the previous layout
|
||||
if ($(e.currentTarget).hasClass('zoomed')) {
|
||||
// Revert to saved settings
|
||||
ViewerData[contentId].loadedSeriesData = $.extend(true, {}, savedSeriesData);
|
||||
ViewerData[contentId].viewportRows = savedViewportRows;
|
||||
ViewerData[contentId].viewportColumns = savedViewportColumns;
|
||||
|
||||
savedViewportRows = 0;
|
||||
savedViewportColumns = 0;
|
||||
|
||||
data = {
|
||||
viewportRows: ViewerData[contentId].viewportRows,
|
||||
viewportColumns: ViewerData[contentId].viewportColumns
|
||||
};
|
||||
|
||||
// Render the imageViewerViewports template with these settings
|
||||
$('#imageViewerViewports').remove();
|
||||
UI.renderWithData(Template.imageViewerViewports, data, container);
|
||||
|
||||
// Remove the 'zoomed' class from any viewports
|
||||
$('.imageViewerViewport').removeClass('zoomed');
|
||||
} else {
|
||||
// Zoom to single viewport
|
||||
|
||||
// If only one viewport is on-screen, stop here
|
||||
if (ViewerData[contentId].viewportRows === 1 &&
|
||||
ViewerData[contentId].viewportColumns === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the current settings
|
||||
savedSeriesData = $.extend(true, {}, ViewerData[contentId].loadedSeriesData);
|
||||
savedViewportRows = ViewerData[contentId].viewportRows;
|
||||
savedViewportColumns = ViewerData[contentId].viewportColumns;
|
||||
|
||||
// Get the clicked-on viewport's index
|
||||
var viewportIndex = this.viewportIndex;
|
||||
|
||||
// Set the first viewport's data to be the same as the currently clicked-on viewport
|
||||
ViewerData[contentId].loadedSeriesData[0] = ViewerData[contentId].loadedSeriesData[viewportIndex];
|
||||
|
||||
// Set the basic template data
|
||||
data = {
|
||||
viewportRows: 1,
|
||||
viewportColumns: 1
|
||||
};
|
||||
|
||||
// Render the imageViewerViewports template with these settings
|
||||
$('#imageViewerViewports').remove();
|
||||
UI.renderWithData(Template.imageViewerViewports, data, container);
|
||||
|
||||
// Add the 'zoomed' class to the lone remaining viewport
|
||||
$('.imageViewerViewport').eq(0).addClass('zoomed');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
<template name="layoutButton">
|
||||
<button id="layout" type="button" class="btn btn-sm btn-default dropdown-toggle" data-container="body" data-toggle="dropdown" aria-expanded="false" data-placement="right" title="Layout" rel="tooltip">
|
||||
<span class="fa fa-th-large"></span>
|
||||
</button>
|
||||
{{ >layoutChooser }}
|
||||
</template>
|
||||
@@ -1,20 +1,29 @@
|
||||
<template name="layoutChooser">
|
||||
<div class="layoutChooser pull-right dropdown-menu" role="menu">
|
||||
<div class="layoutChooser pull-left dropdown-menu" role="menu">
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -33,19 +33,18 @@ Template.layoutChooser.events({
|
||||
highlightCells(evt.currentTarget);
|
||||
},
|
||||
'click .layoutChooser table td': function(evt) {
|
||||
$('#imageViewerViewports').remove();
|
||||
|
||||
var currentCell = $(evt.currentTarget);
|
||||
var rowIndex = currentCell.closest('tr').index();
|
||||
var columnIndex = currentCell.index();
|
||||
|
||||
// Add 1 because the indices start from zero
|
||||
var data = {
|
||||
viewportRows: rowIndex + 1,
|
||||
viewportColumns: columnIndex + 1,
|
||||
var layoutProps = {
|
||||
rows: rowIndex + 1,
|
||||
columns: columnIndex + 1
|
||||
};
|
||||
|
||||
var container = $(".viewerMain").get(0);
|
||||
UI.renderWithData(Template.imageViewerViewports, data, container);
|
||||
|
||||
layoutManager.layoutTemplateName = 'gridLayout';
|
||||
layoutManager.layoutProps = layoutProps;
|
||||
layoutManager.updateViewports();
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
.layoutChooser
|
||||
width: 75px
|
||||
min-width: 75px // to override bootstrap's dropdown-menu class
|
||||
height: 75px
|
||||
width: 92px
|
||||
height: 92px
|
||||
min-width: 92px // to override bootstrap's dropdown-menu class
|
||||
min-height: 92px
|
||||
|
||||
table
|
||||
margin: 0 auto
|
||||
@@ -11,7 +12,7 @@
|
||||
border: 2px solid black
|
||||
width: 20px
|
||||
height: 20px
|
||||
transition: background-color 0.1s linear
|
||||
transition: background-color 0.1s ease
|
||||
|
||||
&:hover, &.hover // Add the hover class here to be triggered by mouseenter/mouseleave
|
||||
background-color: gray
|
||||
@@ -10,11 +10,17 @@
|
||||
{{ /if }}
|
||||
|
||||
{{ #if includeLayoutButton }}
|
||||
{{ >layoutButton }}
|
||||
<div class="btn-group">
|
||||
<button id="layout" type="button" class="btn btn-sm btn-default dropdown-toggle" data-container="body" data-toggle="dropdown" aria-expanded="false" data-placement="right" title="Layout" rel="tooltip">
|
||||
<span class="fa fa-th-large"></span>
|
||||
</button>
|
||||
{{ >layoutChooser }}
|
||||
</div>
|
||||
{{ /if }}
|
||||
|
||||
{{ #if includeHangingProtocolButtons }}
|
||||
{{ >hangingProtocolButtons }}
|
||||
{{ >matchedProtocols }}
|
||||
{{ /if }}
|
||||
|
||||
{{ #each btnGroup }}
|
||||
|
||||
@@ -152,7 +152,7 @@ Template.toolbar.onRendered(function() {
|
||||
var states = toolManager.getToolDefaultStates();
|
||||
var disabledToolButtons = states.disabledToolButtons;
|
||||
var allToolbarButtons = $('#toolbar').find('button');
|
||||
if (disabledToolButtons.length > 0) {
|
||||
if (disabledToolButtons && disabledToolButtons.length > 0) {
|
||||
for (var i = 0; i < allToolbarButtons.length; i++) {
|
||||
var toolbarButton = allToolbarButtons[i];
|
||||
$(toolbarButton).prop('disabled', false);
|
||||
@@ -191,7 +191,7 @@ Template.toolbar.helpers({
|
||||
return this.toolbarOptions.includeHangingProtocolButtons;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
btnGroup: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.btnGroup !== undefined) {
|
||||
|
||||
+4
-3
@@ -22,7 +22,7 @@ updateOrientationMarkers = function(element) {
|
||||
|
||||
var markers = {
|
||||
top: oppositeColumnString,
|
||||
left: oppositeRowString,
|
||||
left: oppositeRowString
|
||||
};
|
||||
|
||||
// If any vertical or horizontal flips are applied, change the orientation strings ahead of
|
||||
@@ -30,12 +30,13 @@ updateOrientationMarkers = function(element) {
|
||||
if (viewport.vflip) {
|
||||
markers.top = cornerstoneTools.orientation.invertOrientationString(markers.top);
|
||||
}
|
||||
|
||||
if (viewport.hflip) {
|
||||
markers.left = cornerstoneTools.orientation.invertOrientationString(markers.left);
|
||||
}
|
||||
|
||||
// Get the viewport orientation marker DOM elements
|
||||
var viewportOrientationMarkers = $(element).siblings(".viewportOrientationMarkers");
|
||||
var viewportOrientationMarkers = $(element).siblings('.viewportOrientationMarkers');
|
||||
var topMarker = viewportOrientationMarkers.find('.topMid');
|
||||
var leftMarker = viewportOrientationMarkers.find('.leftMid');
|
||||
|
||||
@@ -54,4 +55,4 @@ updateOrientationMarkers = function(element) {
|
||||
topMarker.text(markers.top);
|
||||
leftMarker.text(markers.left);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
<template name="viewportOverlay">
|
||||
<div class="imageViewerViewportOverlay noselect">
|
||||
{{ #unless tagDisplaySpecified }}
|
||||
<div class="topleft dicomTag">
|
||||
<div>{{formatPN patientName}}</div>
|
||||
<div>{{patientId}}</div>
|
||||
@@ -21,6 +22,47 @@
|
||||
<div>{{imageDimensions}}</div>
|
||||
<div>{{seriesDescription}}</div>
|
||||
</div>
|
||||
{{ /unless }}
|
||||
|
||||
{{ #if tagDisplayRightOnly }}
|
||||
<div class="topright dicomTag">
|
||||
<div>{{formatPN patientName}}</div>
|
||||
<div>{{patientId}}</div>
|
||||
<div class='priorIndicator'>{{prior}}</div>
|
||||
<div>{{studyDescription}}</div>
|
||||
<div>{{formatDA studyDate}} {{formatTM studyTime}}</div>
|
||||
</div>
|
||||
<div class="bottomright dicomTag">
|
||||
<div>Ser: {{seriesNumber}}</div>
|
||||
<div>Img: {{imageNumber}} ({{imageIndex}}/{{numImages}})</div>
|
||||
<div>{{#if frameRate}}{{frameRate}} FPS{{/if}}</div>
|
||||
<div>{{imageDimensions}}</div>
|
||||
<div>{{seriesDescription}}</div>
|
||||
<div>Zoom: {{formatNumberPrecision zoom 2}}%</div>
|
||||
<div>{{compression}}</div>
|
||||
<div>{{wwwc}}</div>
|
||||
</div>
|
||||
{{ /if }}
|
||||
|
||||
{{ #if tagDisplayLeftOnly }}
|
||||
<div class="topleft dicomTag">
|
||||
<div>{{formatPN patientName}}</div>
|
||||
<div>{{patientId}}</div>
|
||||
<div class='priorIndicator'>{{prior}}</div>
|
||||
<div>{{studyDescription}}</div>
|
||||
<div>{{formatDA studyDate}} {{formatTM studyTime}}</div>
|
||||
</div>
|
||||
<div class="bottomleft dicomTag">
|
||||
<div>Ser: {{seriesNumber}}</div>
|
||||
<div>Img: {{imageNumber}} ({{imageIndex}}/{{numImages}})</div>
|
||||
<div>{{#if frameRate}}{{frameRate}} FPS{{/if}}</div>
|
||||
<div>{{imageDimensions}}</div>
|
||||
<div>{{seriesDescription}}</div>
|
||||
<div>Zoom: {{formatNumberPrecision zoom 2}}%</div>
|
||||
<div>{{compression}}</div>
|
||||
<div>{{wwwc}}</div>
|
||||
</div>
|
||||
{{ /if }}
|
||||
{{>imageControls}}
|
||||
</div>
|
||||
</template>
|
||||
@@ -80,6 +80,20 @@ function getInstance(property) {
|
||||
return instance[property];
|
||||
}
|
||||
|
||||
function getTagDisplay(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('tagDisplay', this.imageId);
|
||||
if (!instance) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return instance[property];
|
||||
}
|
||||
|
||||
function getImage(viewportIndex) {
|
||||
var element = getElementIfNotEmpty(viewportIndex);
|
||||
if (!element) {
|
||||
@@ -172,6 +186,15 @@ Template.viewportOverlay.helpers({
|
||||
imageNumber: function() {
|
||||
return getInstance.call(this, 'instanceNumber');
|
||||
},
|
||||
tagDisplayLeftOnly: function() {
|
||||
return getTagDisplay.call(this, 'side') === 'L';
|
||||
},
|
||||
tagDisplayRightOnly: function() {
|
||||
return getTagDisplay.call(this, 'side') === 'R';
|
||||
},
|
||||
tagDisplaySpecified: function() {
|
||||
return getTagDisplay.call(this, 'side');
|
||||
},
|
||||
imageIndex: function() {
|
||||
return getInstance.call(this, 'index');
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.imageViewerViewportOverlay
|
||||
font-family: Helvetica, OpenSans
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
font-size: 13px
|
||||
color: #979797
|
||||
line-height: 18px
|
||||
|
||||
Reference in new issue
Block a user