LT-398: Manual stack scroll synchronization
This commit is contained in:
parent
e3561690f1
commit
1d2c1c55fe
@ -78,15 +78,6 @@ Template.toolbarSection.helpers({
|
|||||||
disabled: isToolDisabled
|
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 = [];
|
const extraTools = [];
|
||||||
|
|
||||||
extraTools.push({
|
extraTools.push({
|
||||||
@ -145,14 +136,6 @@ Template.toolbarSection.helpers({
|
|||||||
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-elliptical-roi'
|
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({
|
extraTools.push({
|
||||||
id: 'toggleCineDialog',
|
id: 'toggleCineDialog',
|
||||||
title: 'CINE',
|
title: 'CINE',
|
||||||
@ -184,15 +167,13 @@ Template.toolbarSection.helpers({
|
|||||||
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-pan'
|
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({
|
buttonData.push({
|
||||||
id: 'link',
|
id: 'link',
|
||||||
title: 'Link',
|
title: 'Link',
|
||||||
classes: 'imageViewerCommand',
|
classes: 'imageViewerCommand toolbarSectionButton nonAutoDisableState',
|
||||||
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link'
|
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link',
|
||||||
});*/
|
disableFunction: isStackScrollLinkingDisabled
|
||||||
|
});
|
||||||
|
|
||||||
buttonData.push({
|
buttonData.push({
|
||||||
id: 'toggleTarget',
|
id: 'toggleTarget',
|
||||||
|
|||||||
@ -40,7 +40,7 @@ Template.viewer.onCreated(() => {
|
|||||||
flipH,
|
flipH,
|
||||||
rotateL,
|
rotateL,
|
||||||
rotateR,
|
rotateR,
|
||||||
linkStackScroll
|
link
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ViewerData[contentId].loadedSeriesData) {
|
if (ViewerData[contentId].loadedSeriesData) {
|
||||||
|
|||||||
@ -9,6 +9,13 @@
|
|||||||
<div class="topright dicomTag">
|
<div class="topright dicomTag">
|
||||||
<div>{{studyDescription}}</div>
|
<div>{{studyDescription}}</div>
|
||||||
<div>{{formatDA studyDate}} {{formatTM studyTime}}</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>
|
||||||
<div class="bottomright dicomTag">
|
<div class="bottomright dicomTag">
|
||||||
<div>{{#if zoom}}Zoom: {{formatNumberPrecision zoom 0}}%{{/if}}</div>
|
<div>{{#if zoom}}Zoom: {{formatNumberPrecision zoom 0}}%{{/if}}</div>
|
||||||
|
|||||||
@ -25,5 +25,10 @@ Template[defaultTemplate].helpers({
|
|||||||
const timepoint = timepoints[0];
|
const timepoint = timepoints[0];
|
||||||
|
|
||||||
return timepointApi.name(timepoint);
|
return timepointApi.name(timepoint);
|
||||||
|
},
|
||||||
|
|
||||||
|
linked: function() {
|
||||||
|
const linkedViewports = Session.get('StackImagePositionOffsetSynchronizerLinkedViewports') || [];
|
||||||
|
return (linkedViewports.indexOf(this.viewportIndex) !== -1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -262,11 +262,11 @@
|
|||||||
<rect x="26" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
|
<rect x="26" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
|
||||||
</g>
|
</g>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol id="icon-tools-link-stack-scroll" viewBox="0 0 32 32">
|
<symbol id="icon-viewport-link" viewBox="0 0 32 32">
|
||||||
<title>
|
<title>
|
||||||
Stack Scroll Sync
|
Viewport Link
|
||||||
</title>
|
</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="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"/>
|
<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>
|
</g>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -13,9 +13,11 @@ Template.toolbarSectionButton.onCreated(() => {
|
|||||||
const currentId = instance.data.id;
|
const currentId = instance.data.id;
|
||||||
const isCurrentTool = currentId === activeToolId;
|
const isCurrentTool = currentId === activeToolId;
|
||||||
const isSubTool = subTools && _.findWhere(subTools, { id: 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
|
// Check if the current tool, a sub tool or a command button is active
|
||||||
return isCurrentTool || isSubTool;
|
return isCurrentTool || isSubTool || isActiveCommandButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
instance.getActiveToolSubProperty = (propertyName, activeToolId) => {
|
instance.getActiveToolSubProperty = (propertyName, activeToolId) => {
|
||||||
|
|||||||
@ -39,3 +39,12 @@ $viewportTagPadding = 20px
|
|||||||
&.controlsVisible
|
&.controlsVisible
|
||||||
.topright, .bottomright
|
.topright, .bottomright
|
||||||
right: "calc(%s + 19px)" % $viewportTagPadding
|
right: "calc(%s + 19px)" % $viewportTagPadding
|
||||||
|
|
||||||
|
svg
|
||||||
|
theme('color', '$defaultColor')
|
||||||
|
theme('fill', '$defaultColor')
|
||||||
|
theme('stroke', '$defaultColor')
|
||||||
|
background-color: transparent
|
||||||
|
margin: 2px
|
||||||
|
width: 18px
|
||||||
|
height: 18px
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { $ } from 'meteor/jquery';
|
|||||||
class StackImagePositionOffsetSynchronizer {
|
class StackImagePositionOffsetSynchronizer {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.syncedViewportElements = [];
|
this.syncedViewports = [];
|
||||||
this.synchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.stackImagePositionOffsetSynchronizer)
|
this.synchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.stackImagePositionOffsetSynchronizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,16 +22,24 @@ class StackImagePositionOffsetSynchronizer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportElements = this.getLinkableViewports();
|
const viewports = this.getLinkableViewports();
|
||||||
|
const viewportIndexes = [];
|
||||||
|
|
||||||
viewportElements.forEach((viewportElement, index) => {
|
if(viewports.length <= 1) {
|
||||||
this.synchronizer.add(viewportElement);
|
return;
|
||||||
this.syncedViewportElements.push(viewportElement);
|
}
|
||||||
|
|
||||||
|
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;
|
this.active = true;
|
||||||
|
toolManager.activateCommandButton('link');
|
||||||
|
Session.set('StackImagePositionOffsetSynchronizerLinkedViewports', viewportIndexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
@ -39,12 +47,13 @@ class StackImagePositionOffsetSynchronizer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(this.syncedViewportElements.length) {
|
while(this.syncedViewports.length) {
|
||||||
const viewportElement = this.syncedViewportElements[0];
|
const viewport = this.syncedViewports[0];
|
||||||
this.removeViewportElement(viewportElement);
|
this.removeViewport(viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
toolManager.deactivateCommandButton('link');
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@ -62,43 +71,68 @@ class StackImagePositionOffsetSynchronizer {
|
|||||||
this.activate();
|
this.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isViewportSynced(viewportElement) {
|
||||||
|
return !!this.getViewportByElement(viewportElement);
|
||||||
|
}
|
||||||
|
|
||||||
getActiveViewportElement() {
|
getActiveViewportElement() {
|
||||||
const viewportIndex = Session.get('activeViewport') || 0;
|
const viewportIndex = Session.get('activeViewport') || 0;
|
||||||
return $('.imageViewerViewport').get(viewportIndex);
|
return $('.imageViewerViewport').get(viewportIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
isViewportSynced(viewportElement) {
|
removeViewport(viewport) {
|
||||||
let isSynced = false;
|
const index = this.syncedViewports.indexOf(viewport);
|
||||||
|
|
||||||
this.syncedViewportElements.forEach(syncedViewportElement => {
|
|
||||||
isSynced = isSynced || (syncedViewportElement === viewportElement);
|
|
||||||
});
|
|
||||||
|
|
||||||
return isSynced;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeViewportElement(viewportElement) {
|
|
||||||
const index = this.syncedViewportElements.indexOf(viewportElement);
|
|
||||||
|
|
||||||
if(index === -1) {
|
if(index === -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncedViewportElements.splice(index, 1);
|
this.syncedViewports.splice(index, 1);
|
||||||
this.synchronizer.remove(viewportElement);
|
this.synchronizer.remove(viewport.element);
|
||||||
$(viewportElement).off(StackImagePositionOffsetSynchronizer.ELEMENT_DISABLED_EVENT);
|
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) {
|
elementDisabledHandler(context) {
|
||||||
return (e, eventData) => {
|
return (e, eventData) => {
|
||||||
context.removeViewportElement(eventData.element);
|
context.removeViewportByElement(eventData.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinkableViewports() {
|
getLinkableViewports() {
|
||||||
const activeViewportElement = this.getActiveViewportElement();
|
const activeViewportElement = this.getActiveViewportElement();
|
||||||
const activeViewportImageNormal = this.getViewportImageNormal(activeViewportElement);
|
const activeViewportImageNormal = this.getViewportImageNormal(activeViewportElement);
|
||||||
const viewportElements = [];
|
const viewports = [];
|
||||||
|
|
||||||
$('.imageViewerViewport').each((index, viewportElement) => {
|
$('.imageViewerViewport').each((index, viewportElement) => {
|
||||||
const viewportImageNormal = this.getViewportImageNormal(viewportElement);
|
const viewportImageNormal = this.getViewportImageNormal(viewportElement);
|
||||||
@ -109,12 +143,15 @@ class StackImagePositionOffsetSynchronizer {
|
|||||||
// Pi / 12 radians = 15 degrees
|
// Pi / 12 radians = 15 degrees
|
||||||
// If the angle between two vectors is Pi, it means they are just inverted
|
// If the angle between two vectors is Pi, it means they are just inverted
|
||||||
if (angleInRadians < Math.PI / 12 || angleInRadians === Math.PI) {
|
if (angleInRadians < Math.PI / 12 || angleInRadians === Math.PI) {
|
||||||
viewportElements.push(viewportElement)
|
viewports.push({
|
||||||
|
index: index,
|
||||||
|
element: viewportElement
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return viewportElements;
|
return viewports;
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewportImageNormal(element) {
|
getViewportImageNormal(element) {
|
||||||
|
|||||||
@ -276,6 +276,23 @@ toolManager = {
|
|||||||
},
|
},
|
||||||
getDefaultTool() {
|
getDefaultTool() {
|
||||||
return defaultTool;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -115,7 +115,7 @@ clearTools = () => {
|
|||||||
cornerstone.updateImage(element);
|
cornerstone.updateImage(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
linkStackScroll = () => {
|
link = () => {
|
||||||
const synchronizer = OHIF.viewer.stackImagePositionOffsetSynchronizer;
|
const synchronizer = OHIF.viewer.stackImagePositionOffsetSynchronizer;
|
||||||
|
|
||||||
if(synchronizer.isActive()) {
|
if(synchronizer.isActive()) {
|
||||||
|
|||||||
@ -297,5 +297,5 @@ Package.onUse(function(api) {
|
|||||||
api.export('flipH', 'client');
|
api.export('flipH', 'client');
|
||||||
api.export('rotateR', 'client');
|
api.export('rotateR', 'client');
|
||||||
api.export('rotateL', 'client');
|
api.export('rotateL', 'client');
|
||||||
api.export('linkStackScroll', 'client');
|
api.export('link', 'client');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user