LT-398: Manual stack scroll synchronization

This commit is contained in:
Leonardo Campos 2017-01-05 17:39:58 -02:00
parent e3561690f1
commit 1d2c1c55fe
11 changed files with 117 additions and 59 deletions

View File

@ -78,15 +78,6 @@ Template.toolbarSection.helpers({
disabled: isToolDisabled
});
// Disabling this on Lesion Tracker
// targetSubTools.push({
// id: 'targetEX',
// title: 'EX Target',
// classes: 'imageViewerTool',
// svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-measure-target-ex',
// disabled: isToolDisabled
// });
const extraTools = [];
extraTools.push({
@ -145,14 +136,6 @@ Template.toolbarSection.helpers({
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-elliptical-roi'
});
extraTools.push({
id: 'linkStackScroll',
title: 'Link Scroll',
classes: 'imageViewerCommand nonAutoDisableState',
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link-stack-scroll',
disableFunction: isStackScrollLinkingDisabled
});
extraTools.push({
id: 'toggleCineDialog',
title: 'CINE',
@ -184,15 +167,13 @@ Template.toolbarSection.helpers({
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-pan'
});
/*
TODO: design the link functionality
Commenting this out until we build this tool
buttonData.push({
id: 'link',
title: 'Link',
classes: 'imageViewerCommand',
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link'
});*/
classes: 'imageViewerCommand toolbarSectionButton nonAutoDisableState',
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link',
disableFunction: isStackScrollLinkingDisabled
});
buttonData.push({
id: 'toggleTarget',

View File

@ -40,7 +40,7 @@ Template.viewer.onCreated(() => {
flipH,
rotateL,
rotateR,
linkStackScroll
link
});
if (ViewerData[contentId].loadedSeriesData) {

View File

@ -9,6 +9,13 @@
<div class="topright dicomTag">
<div>{{studyDescription}}</div>
<div>{{formatDA studyDate}} {{formatTM studyTime}}</div>
{{#if linked}}
<div class="link-icon">
<svg>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/packages/ohif_viewerbase/assets/icons.svg#icon-viewport-link"></use>
</svg>
</div>
{{/if}}
</div>
<div class="bottomright dicomTag">
<div>{{#if zoom}}Zoom: {{formatNumberPrecision zoom 0}}%{{/if}}</div>

View File

@ -25,5 +25,10 @@ Template[defaultTemplate].helpers({
const timepoint = timepoints[0];
return timepointApi.name(timepoint);
},
linked: function() {
const linkedViewports = Session.get('StackImagePositionOffsetSynchronizerLinkedViewports') || [];
return (linkedViewports.indexOf(this.viewportIndex) !== -1);
}
});

View File

@ -262,11 +262,11 @@
<rect x="26" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
</g>
</symbol>
<symbol id="icon-tools-link-stack-scroll" viewBox="0 0 32 32">
<symbol id="icon-viewport-link" viewBox="0 0 32 32">
<title>
Stack Scroll Sync
Viewport Link
</title>
<g id="icon-tools-link-stack-scroll-group" stroke="none">
<g id="icon-viewport-link-group" stroke="none">
<path d="m5.364 28.917c1.047 1.046 2.422 1.57 3.797 1.57 1.376 0 2.751-0.523 3.798-1.572l7.552-7.554c1.016-1.014 1.574-2.361 1.574-3.797 -0.001-1.432-0.558-2.781-1.574-3.796l-2.297-2.298 -2.009 2.009 2.297 2.298c0.478 0.478 0.741 1.113 0.741 1.788 0 0.674-0.264 1.309-0.741 1.786l-7.553 7.555c-0.987 0.985-2.59 0.985-3.576 0l-2.297-2.298c-0.987-0.985-0.987-2.589 0-3.576l3.775-3.776 -2.009-2.009 -3.776 3.776c-2.094 2.096-2.092 5.502 0 7.595l2.299 2.3zM26.695 2.992"/>
<path d="m26.695 2.992c-1.014-1.016-2.362-1.575-3.797-1.575 -0.001 0-0.001 0-0.002 0 -1.435 0-2.784 0.56-3.798 1.573l-7.551 7.553c-1.017 1.016-1.576 2.363-1.576 3.799 0 1.434 0.558 2.784 1.574 3.797l2.297 2.297 2.01-2.009 -2.298-2.297c-0.477-0.477-0.741-1.113-0.741-1.788 0-0.676 0.265-1.311 0.742-1.788l7.553-7.555c0.477-0.477 1.111-0.74 1.789-0.74 0 0 0 0 0.001 0 0.674 0 1.309 0.264 1.786 0.74l2.297 2.299c0.477 0.477 0.74 1.111 0.74 1.788 0 0.674-0.264 1.311-0.74 1.788l-3.776 3.777 2.009 2.009 3.776-3.777c1.014-1.013 1.573-2.363 1.574-3.797 0-1.435-0.56-2.784-1.574-3.797l-2.295-2.296z"/>
</g>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -13,9 +13,11 @@ Template.toolbarSectionButton.onCreated(() => {
const currentId = instance.data.id;
const isCurrentTool = currentId === activeToolId;
const isSubTool = subTools && _.findWhere(subTools, { id: activeToolId });
const activeCommandButtons = Session.get('ToolManagerActiveCommandButtons') || [];
const isActiveCommandButton = activeCommandButtons.indexOf(instance.data.id) !== -1;
// Check if the current tool or a sub tool is the active one
return isCurrentTool || isSubTool;
// Check if the current tool, a sub tool or a command button is active
return isCurrentTool || isSubTool || isActiveCommandButton;
};
instance.getActiveToolSubProperty = (propertyName, activeToolId) => {

View File

@ -39,3 +39,12 @@ $viewportTagPadding = 20px
&.controlsVisible
.topright, .bottomright
right: "calc(%s + 19px)" % $viewportTagPadding
svg
theme('color', '$defaultColor')
theme('fill', '$defaultColor')
theme('stroke', '$defaultColor')
background-color: transparent
margin: 2px
width: 18px
height: 18px

View File

@ -5,7 +5,7 @@ import { $ } from 'meteor/jquery';
class StackImagePositionOffsetSynchronizer {
constructor() {
this.active = false;
this.syncedViewportElements = [];
this.syncedViewports = [];
this.synchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.stackImagePositionOffsetSynchronizer)
}
@ -22,16 +22,24 @@ class StackImagePositionOffsetSynchronizer {
return;
}
const viewportElements = this.getLinkableViewports();
const viewports = this.getLinkableViewports();
const viewportIndexes = [];
viewportElements.forEach((viewportElement, index) => {
this.synchronizer.add(viewportElement);
this.syncedViewportElements.push(viewportElement);
if(viewports.length <= 1) {
return;
}
viewports.forEach((viewport, index) => {
this.synchronizer.add(viewport.element);
this.syncedViewports.push(viewport);
viewportIndexes.push(viewport.index)
$(viewportElement).on(StackImagePositionOffsetSynchronizer.ELEMENT_DISABLED_EVENT, this.elementDisabledHandler(this));
$(viewport.element).on(StackImagePositionOffsetSynchronizer.ELEMENT_DISABLED_EVENT, this.elementDisabledHandler(this));
});
this.active = true;
toolManager.activateCommandButton('link');
Session.set('StackImagePositionOffsetSynchronizerLinkedViewports', viewportIndexes);
}
deactivate() {
@ -39,12 +47,13 @@ class StackImagePositionOffsetSynchronizer {
return;
}
while(this.syncedViewportElements.length) {
const viewportElement = this.syncedViewportElements[0];
this.removeViewportElement(viewportElement);
while(this.syncedViewports.length) {
const viewport = this.syncedViewports[0];
this.removeViewport(viewport);
}
this.active = false;
toolManager.deactivateCommandButton('link');
}
update() {
@ -62,43 +71,68 @@ class StackImagePositionOffsetSynchronizer {
this.activate();
}
isViewportSynced(viewportElement) {
return !!this.getViewportByElement(viewportElement);
}
getActiveViewportElement() {
const viewportIndex = Session.get('activeViewport') || 0;
return $('.imageViewerViewport').get(viewportIndex);
}
isViewportSynced(viewportElement) {
let isSynced = false;
this.syncedViewportElements.forEach(syncedViewportElement => {
isSynced = isSynced || (syncedViewportElement === viewportElement);
});
return isSynced;
}
removeViewportElement(viewportElement) {
const index = this.syncedViewportElements.indexOf(viewportElement);
removeViewport(viewport) {
const index = this.syncedViewports.indexOf(viewport);
if(index === -1) {
return;
}
this.syncedViewportElements.splice(index, 1);
this.synchronizer.remove(viewportElement);
$(viewportElement).off(StackImagePositionOffsetSynchronizer.ELEMENT_DISABLED_EVENT);
this.syncedViewports.splice(index, 1);
this.synchronizer.remove(viewport.element);
this.removeLinkedViewportFromSession(viewport);
$(viewport.element).off(StackImagePositionOffsetSynchronizer.ELEMENT_DISABLED_EVENT);
}
getViewportByElement(viewportElement) {
const length = this.syncedViewports.length;
for(let i = 0; i < length; i++) {
const viewport = this.syncedViewports[i];
if(viewport.element === viewportElement) {
return viewport;
}
}
}
removeViewportByElement(viewportElement) {
let viewport = this.getViewportByElement(viewportElement);
if(viewport) {
this.removeViewport(viewport);
}
}
removeLinkedViewportFromSession(viewport) {
const linkedViewports = Session.get('StackImagePositionOffsetSynchronizerLinkedViewports');
const index = linkedViewports.indexOf(viewport.index);
if(index !== -1) {
linkedViewports.splice(index, 1);
Session.set('StackImagePositionOffsetSynchronizerLinkedViewports', linkedViewports);
}
}
elementDisabledHandler(context) {
return (e, eventData) => {
context.removeViewportElement(eventData.element);
context.removeViewportByElement(eventData.element);
}
}
getLinkableViewports() {
const activeViewportElement = this.getActiveViewportElement();
const activeViewportImageNormal = this.getViewportImageNormal(activeViewportElement);
const viewportElements = [];
const viewports = [];
$('.imageViewerViewport').each((index, viewportElement) => {
const viewportImageNormal = this.getViewportImageNormal(viewportElement);
@ -109,12 +143,15 @@ class StackImagePositionOffsetSynchronizer {
// Pi / 12 radians = 15 degrees
// If the angle between two vectors is Pi, it means they are just inverted
if (angleInRadians < Math.PI / 12 || angleInRadians === Math.PI) {
viewportElements.push(viewportElement)
viewports.push({
index: index,
element: viewportElement
});
}
}
});
return viewportElements;
return viewports;
}
getViewportImageNormal(element) {

View File

@ -276,6 +276,23 @@ toolManager = {
},
getDefaultTool() {
return defaultTool;
},
activateCommandButton(button) {
const activeCommandButtons = Session.get('ToolManagerActiveCommandButtons') || [];
if(activeCommandButtons.indexOf(button) === -1) {
activeCommandButtons.push('link');
Session.set('ToolManagerActiveCommandButtons', activeCommandButtons);
}
},
deactivateCommandButton(button) {
const activeCommandButtons = Session.get('ToolManagerActiveCommandButtons') || [];
const index = activeCommandButtons.indexOf(button);
if(index !== -1) {
activeCommandButtons.splice(index, 1);
Session.set('ToolManagerActiveCommandButtons', activeCommandButtons);
}
}
};

View File

@ -115,7 +115,7 @@ clearTools = () => {
cornerstone.updateImage(element);
};
linkStackScroll = () => {
link = () => {
const synchronizer = OHIF.viewer.stackImagePositionOffsetSynchronizer;
if(synchronizer.isActive()) {

View File

@ -297,5 +297,5 @@ Package.onUse(function(api) {
api.export('flipH', 'client');
api.export('rotateR', 'client');
api.export('rotateL', 'client');
api.export('linkStackScroll', 'client');
api.export('link', 'client');
});