Clips use DICOM frameTime by default (LT-210). Added floating clip control dialog (LT-209)

This commit is contained in:
Erik Ziegler committed 2016-03-17 12:10:23 +01:00
1 parent d1d4e09815
commit 7e64fb4109
21 files changed
+653 -142

No files matched your search

@@ -0,0 +1,42 @@
<template name="cineDialog">
<div id="cineDialog">
<h5>Cine Controls</h5>
<div id="cineButtons">
<a id="cineFirstButton" title="Skip to first image" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-fast-backward"></i>
</a>
<a id="cineBackButton" title="Previous image" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-step-backward"></i>
</a>
<a id="cineSlowPlaybackButton" title="Slow playback" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-backward"></i>
</a>
<a id="cinePlayButton" title="Play / Pause" class="cineButton" data-toggle="tooltip">
{{#if isPlaying}}
<i class="fa fa-lg fa-pause"></i>
{{else}}
<i class="fa fa-lg fa-play"></i>
{{/if}}
</a>
<a id="cineFastForwardButton" title="Increase playback speed" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-forward"></i>
</a>
<a id="cineNextButton" title="Next image" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-step-forward"></i>
</a>
<a id="cineLastButton" title="Skip to last image" class="cineButton" data-toggle="tooltip">
<i class="fa fa-lg fa-fast-forward"></i>
</a>
</div>
<div id="cineOptions">
<div id="loopSection">
<label>Loop?</label>
<input type="checkbox" checked id="cineLoopCheckbox" />
</div>
<div id="fpsSection">
<label>Cine Speed: <span id="fps">{{framerate}}</span></label>
<input type="range" id="cineSlider" min="1" max="90" value="{{framerate}}"/>
</div>
</div>
</div>
</template>
@@ -0,0 +1,78 @@
toggleCinePlay = function() {
var element = getActiveViewportElement();
var playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
if (isPlaying()) {
cornerstoneTools.stopClip(element);
} else {
cornerstoneTools.playClip(element);
}
};
function updateFramerate(rate) {
OHIF.viewer.cine.framesPerSecond = rate;
// Update playClip toolData for this imageId
var element = getActiveViewportElement();
var playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
playClipToolData.data[0].framesPerSecond = OHIF.viewer.cine.framesPerSecond;
// If the movie is playing, stop/start to update the framerate
if (isPlaying()) {
var element = getActiveViewportElement();
cornerstoneTools.stopClip(element);
cornerstoneTools.playClip(element);
}
Session.set('UpdateCine', Random.id());
}
Template.cineDialog.helpers({
isPlaying: function() {
return isPlaying();
},
framerate: function() {
Session.get('UpdateCine');
return OHIF.viewer.cine.framesPerSecond.toFixed(1);
}
});
Template.cineDialog.events({
'click #cineFirstButton': function() {
switchToImageByIndex(0);
},
'click #cineBackButton': function() {
switchToImageRelative(-1);
},
'click #cineSlowPlaybackButton': function() {
updateFramerate(OHIF.viewer.cine.framesPerSecond - 1);
},
'click #cinePlayButton': function() {
toggleCinePlay();
},
'click #cineNextButton': function() {
switchToImageRelative(1);
},
'click #cineFastForwardButton': function() {
updateFramerate(OHIF.viewer.cine.framesPerSecond + 1);
},
'click #cineLastButton': function() {
switchToImageByIndex(-1);
},
'change #cineLoopCheckbox': function(e) {
var element = getActiveViewportElement();
var playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
playClipToolData.data[0].loop = $(e.currentTarget).is(':checked');
OHIF.viewer.cine.loop = playClipToolData.data[0].loop;
},
'input #cineSlider': function(e) {
// Update the FPS text onscreen
var rate = parseFloat($(e.currentTarget).val());
updateFramerate(rate);
}
});
Template.cineDialog.onRendered(function() {
var dialog = $('#cineDialog');
dialog.draggable();
});
@@ -0,0 +1,44 @@
#cineDialog
display: none
z-index: 1000
position: absolute
top: auto
left: auto
bottom: 3px
right: 3px
overflow: hidden
padding: 10px
width: 250px
height: 175px
border: 1px black solid
border-radius: 3px
background: white
color: black
box-sizing: border-box
-webkit-user-select: none
-moz-user-select: none
-o-user-select: none
-ms-user-select: none
-khtml-user-select: none
user-select: none
#cineButtons
padding-bottom: 10px
overflow: auto
.cineButton
cursor: pointer
margin: 0 6px
text-decoration: none
#cineOptions
padding: 5px 0
overflow: auto
#loopSection
padding-right: 12px
@@ -180,6 +180,17 @@ function loadSeriesIntoViewport(data, templateData) {
cornerstoneTools.clearToolState(element, 'stack');
cornerstoneTools.addToolState(element, 'stack', stack);
// Set the default CINE settings
var frameRate = 1000 / series.instances[0].frameTime;
var cineToolData = {
loop: OHIF.viewer.cine.loop,
framesPerSecond: frameRate || OHIF.viewer.cine.framesPerSecond
};
cornerstoneTools.addToolState(element, 'playClip', cineToolData);
// Autoplay datasets that have framerates set
cornerstoneTools.playClip(element);
// Enable mouse, mouseWheel, touch, and keyboard input on the element
cornerstoneTools.mouseInput.enable(element);
cornerstoneTools.touchInput.enable(element);
@@ -224,6 +235,20 @@ function loadSeriesIntoViewport(data, templateData) {
templateData.imageId = eventData.enabledElement.image.imageId;
Session.set('CornerstoneNewImage' + viewportIndex, Random.id());
// Get the element and stack data
var element = e.target;
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (!toolData || !toolData.data || !toolData.data.length) {
return;
}
var stack = toolData.data[0];
// Update the imageSlider value
var currentOverlay = $(element).siblings('.imageViewerViewportOverlay');
var currentImageSlider = currentOverlay.find('#imageSlider');
currentImageSlider.val(stack.currentImageIdIndex + 1);
// If this viewport is displaying a stack of images, save the current image
// index in the stack to the global ViewerData object, as well as the Meteor Session.
var stack = cornerstoneTools.getToolState(element, 'stack');
@@ -1,9 +1,14 @@
<template name="playClipButton">
<button id="playClip" type="button" class="imageViewerCommand btn btn-sm btn-default" data-container="body" data-toggle="tooltip" data-placement="bottom" title="Play/Stop Clip">
<button id="playClip" type="button" class="imageViewerCommand btn btn-sm btn-default"
data-container="body" data-toggle="tooltip" data-placement="bottom" title="Play/Stop Clip">
{{ #if isPlaying }}
<span class="fa fa-stop"></span>
{{ else }}
<span class="fa fa-play"></span>
{{ /if }}
</button>
<button id="toggleCineDialog" type="button" class="imageViewerCommand btn btn-sm btn-default"
data-container="body" data-toggle="tooltip" data-placement="bottom" title="Toggle CINE Dialog">
<span class="fa fa-youtube-play"></span>
</button>
</template>
@@ -1,41 +1,52 @@
toggleCinePlay = function(element) {
var viewports = $('.imageViewerViewport');
if (!element) {
var activeViewport = Session.get('activeViewport');
element = $('.imageViewerViewport').get(activeViewport);
var viewportIndex = Session.get('activeViewport');
element = viewports.get(viewportIndex);
}
if (!element) {
return;
}
var viewportIndex = $('.imageViewerViewport').index(element);
var isPlaying = OHIF.viewer.isPlaying[viewportIndex] || false;
if (isPlaying === true) {
if (isPlaying) {
cornerstoneTools.stopClip(element);
} else {
cornerstoneTools.playClip(element);
}
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
Session.set('UpdateCINE', Random.id());
};
Template.playClipButton.helpers({
'isPlaying': function() {
Session.get('UpdateCINE');
var activeViewport = Session.get('activeViewport');
isPlaying = function() {
Session.get('UpdateCINE');
var activeViewport = Session.get('activeViewport');
// TODO=Check best way to make sure this is always defined
// Right now it is initialized in enableHotkeys AND in
// imageViewer onCreated, but this appears to break some things
if (!OHIF.viewer.isPlaying) {
return;
}
return !!OHIF.viewer.isPlaying[activeViewport];
// TODO=Check best way to make sure this is always defined
// Right now it is initialized in enableHotkeys AND in
// imageViewer onCreated, but this appears to break some things
if (!OHIF.viewer.isPlaying) {
return;
}
return !!OHIF.viewer.isPlaying[activeViewport];
};
Template.playClipButton.helpers({
isPlaying: function() {
return isPlaying();
}
});
Template.playClipButton.events({
'click #playClip': function() {
toggleCinePlay();
},
'click #toggleCineDialog': function() {
var cineDialog = document.getElementById('cineDialog');
toggleDialog(cineDialog);
}
});
});
@@ -133,6 +133,7 @@ Template.toolbar.events({
if (!OHIF.viewer.functionList.hasOwnProperty(command)) {
return;
}
var activeViewport = Session.get('activeViewport');
var element = $('.imageViewerViewport').get(activeViewport);
OHIF.viewer.functionList[command](element);
@@ -150,48 +151,53 @@ Template.toolbar.onRendered(function() {
// Set disabled/enabled tool buttons that are set in toolManager
var states = toolManager.getToolDefaultStates();
var disabledToolButtons = states.disabledToolButtons;
var allToolbarButtons = $("#toolbar").find("button");
var allToolbarButtons = $('#toolbar').find('button');
if (disabledToolButtons.length > 0) {
for (var i=0; i < allToolbarButtons.length; i++) {
for (var i = 0; i < allToolbarButtons.length; i++) {
var toolbarButton = allToolbarButtons[i];
$(toolbarButton).prop("disabled", false);
var index = disabledToolButtons.indexOf($(toolbarButton).attr("id"));
$(toolbarButton).prop('disabled', false);
var index = disabledToolButtons.indexOf($(toolbarButton).attr('id'));
if (index !== -1) {
$(toolbarButton).prop("disabled", true);
$(toolbarButton).prop('disabled', true);
}
}
}
});
Template.toolbar.helpers({
'button': function() {
button: function() {
if (this.toolbarOptions && this.toolbarOptions.buttonData) {
return this.toolbarOptions.buttonData;
}
return getDefaultButtonData();
},
'includePlayClipButton': function() {
includePlayClipButton: function() {
if (this.toolbarOptions && this.toolbarOptions.includePlayClipButton !== undefined) {
return this.toolbarOptions.includePlayClipButton;
}
return true;
},
'includeLayoutButton': function() {
includeLayoutButton: function() {
if (this.toolbarOptions && this.toolbarOptions.includeLayoutButton !== undefined) {
return this.toolbarOptions.includeLayoutButton;
}
return true;
},
'includeHangingProtocolButtons': function() {
includeHangingProtocolButtons: function() {
if (this.toolbarOptions && this.toolbarOptions.includeHangingProtocolButtons !== undefined) {
return this.toolbarOptions.includeHangingProtocolButtons;
}
return true;
return false;
},
'btnGroup': function() {
btnGroup: function() {
if (this.toolbarOptions && this.toolbarOptions.btnGroup !== undefined) {
return this.toolbarOptions.btnGroup;
}
return [];
}
});
});
@@ -17,7 +17,7 @@
<div class="bottomleft dicomTag">
<div>Ser: {{seriesNumber}}</div>
<div>Img: {{imageNumber}} ({{imageIndex}}/{{numImages}})</div>
<div>{{frameRate}}</div>
<div>{{#if frameRate}}{{frameRate}} FPS{{/if}}</div>
<div>{{imageDimensions}}</div>
<div>{{seriesDescription}}</div>
</div>
@@ -157,6 +157,11 @@ Template.viewportOverlay.helpers({
seriesDescription: function() {
return getSeries.call(this, 'seriesDescription');
},
frameRate: function() {
var frameTime = getInstance.call(this, 'frameTime');
var frameRate = 1000 / frameTime;
return frameRate.toFixed(1);
},
seriesNumber: function() {
return getSeries.call(this, 'seriesNumber');
},