Allowing W/L Presets customization
This commit is contained in:
1 parent
4d9300f6c4
commit
e6b5a21ae2
14 files changed
+303
-173
No files matched your search
@@ -1,6 +1,25 @@
|
||||
<template name="windowLevelPresetsForm">
|
||||
{{#form (extend this class='form-themed' api=instance.api)}}
|
||||
{{>inputText label='W/L Preset 1' key='WLPreset1'}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Preset</th>
|
||||
<th>Name</th>
|
||||
<th>Window Level (WL)</th>
|
||||
<th>Window Width (WW)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each presetInputInformation in getPresetsInputInformationList}}
|
||||
{{#group base='baseTr' key=(concat @index)}}
|
||||
<td>{{sum @index 1}}</td>
|
||||
<td>{{>inputText key='id' value=presetInputInformation.id}}</td>
|
||||
<td>{{>inputText key='wc' value=(filterNaN presetInputInformation.wc) dataType='Number'}}</td>
|
||||
<td>{{>inputText key='ww' value=(filterNaN presetInputInformation.ww) dataType='Number'}}</td>
|
||||
{{/group}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<div class="clearfix">
|
||||
{{#button class='btn btn-primary pull-right' action='save'}}Save{{/button}}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Template.windowLevelPresetsForm.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
const { wlPresets } = OHIF.viewerbase;
|
||||
|
||||
instance.api = {
|
||||
save() {
|
||||
const form = instance.$('form').first().data('component');
|
||||
const definitions = form.value();
|
||||
// TODO: return save method with promise
|
||||
wlPresets.store(definitions);
|
||||
},
|
||||
|
||||
resetDefaults() {
|
||||
@@ -17,9 +19,14 @@ Template.windowLevelPresetsForm.onCreated(() => {
|
||||
message: 'Are you sure you want to reset all the window level presets to their defaults?'
|
||||
};
|
||||
|
||||
return OHIF.ui.showDialog('dialogConfirm', dialogOptions).then(() => {
|
||||
// TODO: return reset method with promise
|
||||
});
|
||||
return OHIF.ui.showDialog('dialogConfirm', dialogOptions).then(() => wlPresets.resetDefaults());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Template.windowLevelPresetsForm.helpers({
|
||||
getPresetsInputInformationList() {
|
||||
OHIF.viewerbase.wlPresets.changeObserver.depend();
|
||||
return _.toArray(OHIF.viewer.wlPresets);
|
||||
}
|
||||
});
|
||||
@@ -1,141 +1,213 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Session } from 'meteor/session';
|
||||
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { viewportUtils } from './viewportUtils';
|
||||
|
||||
const WL_PRESET_CUSTOM = 'Custom';
|
||||
const WL_PRESET_DEFAULT = 'Default';
|
||||
const WL_STORAGE_KEY = `WindowLevelPresetsDefinitions`;
|
||||
|
||||
OHIF.viewer.defaultWLPresets = {
|
||||
0: {
|
||||
id: 'SoftTissue',
|
||||
wc: 40,
|
||||
ww: 400
|
||||
},
|
||||
1: {
|
||||
id: 'Lung',
|
||||
wc: -600,
|
||||
ww: 1500
|
||||
},
|
||||
2: {
|
||||
id: 'Liver',
|
||||
wc: 90,
|
||||
ww: 150
|
||||
},
|
||||
3: {
|
||||
id: 'Bone',
|
||||
wc: 480,
|
||||
ww: 2500
|
||||
},
|
||||
4: {
|
||||
id: 'Brain',
|
||||
wc: 40,
|
||||
ww: 80
|
||||
},
|
||||
5: {},
|
||||
6: {},
|
||||
7: {},
|
||||
8: {},
|
||||
9: {}
|
||||
};
|
||||
|
||||
class WindowLevelPresetsManager {
|
||||
constructor() {
|
||||
this.defaults = {};
|
||||
this.retrieveFunction = null;
|
||||
this.storeFunction = null;
|
||||
this.changeObserver = new Tracker.Dependency();
|
||||
}
|
||||
|
||||
updateElementWLPresetData(element) {
|
||||
const wlPresetData = cornerstone.getElementData(element, 'wlPreset');
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { viewport, image } = enabledElement;
|
||||
const { windowCenter, windowWidth } = viewport.voi;
|
||||
let preset, presetName;
|
||||
|
||||
if (windowWidth === image.windowWidth && windowCenter === image.windowCenter) {
|
||||
presetName = WL_PRESET_DEFAULT;
|
||||
} else {
|
||||
const WLPresets = OHIF.viewer.wlPresets;
|
||||
for (let index in WLPresets) {
|
||||
const currentPreset = WLPresets[index];
|
||||
if (windowCenter === currentPreset.wc && windowWidth === currentPreset.ww) {
|
||||
preset = currentPreset;
|
||||
presetName = preset.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wlPresetData.name = presetName || WL_PRESET_CUSTOM;
|
||||
wlPresetData.ww = windowWidth;
|
||||
wlPresetData.wc = windowCenter;
|
||||
|
||||
if (wlPresetData.name === WL_PRESET_CUSTOM) {
|
||||
const custom = wlPresetData.custom || (wlPresetData.custom = Object.create(null));
|
||||
custom.ww = windowWidth;
|
||||
custom.wc = windowCenter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specified W/L preset on given element on fallback to default W/L preset if the specified preset is not valid.
|
||||
* @param {String} presetName The desired W/L preset to be applied
|
||||
* @param {DOMElement} element An enabled viewport DOM Element.
|
||||
*/
|
||||
applyWLPreset(presetName, element) {
|
||||
const wlPresets = OHIF.viewer.wlPresets;
|
||||
const wlPresetData = cornerstone.getElementData(element, 'wlPreset');
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
|
||||
const preset = wlPresets[presetName] || _.findWhere(wlPresets, { id: presetName });
|
||||
if (presetName === WL_PRESET_CUSTOM && wlPresetData.custom) {
|
||||
viewport.voi.windowWidth = wlPresetData.custom.ww;
|
||||
viewport.voi.windowCenter = wlPresetData.custom.wc;
|
||||
} else if (preset && !_.isEmpty(preset) && preset.id) {
|
||||
presetName = preset.id;
|
||||
viewport.voi.windowWidth = preset.ww;
|
||||
viewport.voi.windowCenter = preset.wc;
|
||||
} else {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
viewport.voi.windowWidth = enabledElement.image.windowWidth;
|
||||
viewport.voi.windowCenter = enabledElement.image.windowCenter;
|
||||
presetName = WL_PRESET_DEFAULT;
|
||||
}
|
||||
|
||||
wlPresetData.name = presetName;
|
||||
wlPresetData.ww = viewport.voi.windowWidth;
|
||||
wlPresetData.wc = viewport.voi.windowCenter;
|
||||
|
||||
// Update the viewport
|
||||
cornerstone.setViewport(element, viewport);
|
||||
|
||||
OHIF.log.info('WLPresets::Applying WL Preset: ' + presetName);
|
||||
|
||||
// Notify other components about W/L Preset changes
|
||||
Session.set('OHIFWlPresetApplied', presetName);
|
||||
}
|
||||
|
||||
store(wlPresets) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.storeFunction) {
|
||||
this.storeFunction(wlPresets).then(resolve).catch(reject);
|
||||
} else if (Meteor.userId()) {
|
||||
OHIF.user.setData(WL_STORAGE_KEY, wlPresets).then(resolve).catch(reject);
|
||||
} else {
|
||||
Session.setPersistent(WL_STORAGE_KEY, wlPresets);
|
||||
resolve();
|
||||
}
|
||||
}).then(() => this.setOHIFWLPresets(wlPresets));
|
||||
}
|
||||
|
||||
retrieve() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.retrieveFunction) {
|
||||
this.retrieveFunction().then(resolve).catch(reject);
|
||||
} else if (Meteor.userId()) {
|
||||
try {
|
||||
resolve(OHIF.user.getData(WL_STORAGE_KEY));
|
||||
} catch(error) {
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
resolve(Session.get(WL_STORAGE_KEY));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
load() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.retrieve().then(wlPresets => {
|
||||
if (wlPresets) {
|
||||
this.setOHIFWLPresets(wlPresets);
|
||||
} else {
|
||||
this.loadDefauls();
|
||||
}
|
||||
}).catch(this.loadDefauls);
|
||||
});
|
||||
}
|
||||
|
||||
applyWLPresetToActiveElement(presetName) {
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyWLPreset(presetName, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides OHIF's wlPresets
|
||||
* @param {Object} wlPresets Object with wlPresets mapping
|
||||
*/
|
||||
setOHIFWLPresets(wlPresets) {
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
const presetMap = Object.create(null); // Objects without prototype have much faster lookup times
|
||||
for (let index in wlPresets) {
|
||||
if (hasOwn.call(wlPresets, index)) {
|
||||
presetMap[index] = wlPresets[index];
|
||||
}
|
||||
}
|
||||
|
||||
OHIF.viewer.wlPresets = presetMap;
|
||||
this.changeObserver.changed();
|
||||
}
|
||||
|
||||
loadDefauls() {
|
||||
this.setOHIFWLPresets(OHIF.viewer.defaultWLPresets);
|
||||
}
|
||||
|
||||
resetDefaults() {
|
||||
return this.store(OHIF.viewer.defaultWLPresets);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add this to a namespace definition
|
||||
Meteor.startup(function() {
|
||||
OHIF.viewer.defaultWLPresets = {
|
||||
SoftTissue: {
|
||||
wc: 40,
|
||||
ww: 400
|
||||
},
|
||||
Lung: {
|
||||
wc: -600,
|
||||
ww: 1500
|
||||
},
|
||||
Liver: {
|
||||
wc: 90,
|
||||
ww: 150
|
||||
},
|
||||
Bone: {
|
||||
wc: 480,
|
||||
ww: 2500
|
||||
},
|
||||
Brain: {
|
||||
wc: 40,
|
||||
ww: 80
|
||||
}
|
||||
};
|
||||
|
||||
setOHIFWLPresets(OHIF.viewer.defaultWLPresets);
|
||||
});
|
||||
|
||||
function updateElementWLPresetData(element) {
|
||||
const wlPresetData = cornerstone.getElementData(element, 'wlPreset');
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { viewport, image } = enabledElement;
|
||||
const { windowCenter, windowWidth } = viewport.voi;
|
||||
let presetName;
|
||||
|
||||
if (windowWidth === image.windowWidth && windowCenter === image.windowCenter) {
|
||||
presetName = WL_PRESET_DEFAULT;
|
||||
} else {
|
||||
const WLPresets = OHIF.viewer.wlPresets;
|
||||
for (let name in WLPresets) {
|
||||
const preset = WLPresets[name];
|
||||
if (windowCenter === preset.wc && windowWidth === preset.ww) {
|
||||
presetName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wlPresetData.name = presetName || WL_PRESET_CUSTOM;
|
||||
wlPresetData.ww = windowWidth;
|
||||
wlPresetData.wc = windowCenter;
|
||||
|
||||
if (wlPresetData.name === WL_PRESET_CUSTOM) {
|
||||
const custom = wlPresetData.custom || (wlPresetData.custom = Object.create(null));
|
||||
custom.ww = windowWidth;
|
||||
custom.wc = windowCenter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specified W/L preset on given element on fallback to default W/L preset if the specified preset is not valid.
|
||||
* @param {String} presetName The desired W/L preset to be applied
|
||||
* @param {DOMElement} element An enabled viewport DOM Element.
|
||||
*/
|
||||
function applyWLPreset(presetName, element) {
|
||||
const wlPresets = OHIF.viewer.wlPresets;
|
||||
const wlPresetData = cornerstone.getElementData(element, 'wlPreset');
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
|
||||
if (presetName === WL_PRESET_CUSTOM && wlPresetData.custom) {
|
||||
viewport.voi.windowWidth = wlPresetData.custom.ww;
|
||||
viewport.voi.windowCenter = wlPresetData.custom.wc;
|
||||
} else if (presetName in wlPresets) {
|
||||
const preset = wlPresets[presetName];
|
||||
viewport.voi.windowWidth = preset.ww;
|
||||
viewport.voi.windowCenter = preset.wc;
|
||||
} else {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
viewport.voi.windowWidth = enabledElement.image.windowWidth;
|
||||
viewport.voi.windowCenter = enabledElement.image.windowCenter;
|
||||
presetName = WL_PRESET_DEFAULT;
|
||||
}
|
||||
|
||||
wlPresetData.name = presetName;
|
||||
wlPresetData.ww = viewport.voi.windowWidth;
|
||||
wlPresetData.wc = viewport.voi.windowCenter;
|
||||
|
||||
// Update the viewport
|
||||
cornerstone.setViewport(element, viewport);
|
||||
|
||||
OHIF.log.info('WLPresets::Applying WL Preset: ' + presetName);
|
||||
|
||||
// Notify other components about W/L Preset changes
|
||||
Session.set('OHIFWlPresetApplied', presetName);
|
||||
}
|
||||
|
||||
function applyWLPresetToActiveElement(presetName) {
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyWLPreset(presetName, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides OHIF's wlPresets
|
||||
* @param {Object} wlPresets Object with wlPresets mapping
|
||||
*/
|
||||
function setOHIFWLPresets(wlPresets) {
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
const presetMap = Object.create(null); // Objects without prototype have much faster lookup times
|
||||
for (let name in wlPresets) {
|
||||
if (hasOwn.call(wlPresets, name)) {
|
||||
presetMap[name] = wlPresets[name];
|
||||
}
|
||||
}
|
||||
OHIF.viewer.wlPresets = presetMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export functions inside WLPresets namespace.
|
||||
*/
|
||||
const WLPresets = new WindowLevelPresetsManager();
|
||||
|
||||
const WLPresets = {
|
||||
applyWLPreset,
|
||||
applyWLPresetToActiveElement,
|
||||
setOHIFWLPresets,
|
||||
updateElementWLPresetData
|
||||
};
|
||||
Meteor.startup(() => {
|
||||
WLPresets.load();
|
||||
});
|
||||
|
||||
export { WLPresets };
|
||||
@@ -65,11 +65,16 @@ Meteor.startup(function() {
|
||||
toggleCineDialog: '',
|
||||
|
||||
// Preset hotkeys
|
||||
WLPresetSoftTissue: '1',
|
||||
WLPresetLung: '2',
|
||||
WLPresetLiver: '3',
|
||||
WLPresetBone: '4',
|
||||
WLPresetBrain: '5'
|
||||
WLPreset0: '1',
|
||||
WLPreset1: '2',
|
||||
WLPreset2: '3',
|
||||
WLPreset3: '4',
|
||||
WLPreset4: '5',
|
||||
WLPreset5: '6',
|
||||
WLPreset6: '7',
|
||||
WLPreset7: '8',
|
||||
WLPreset8: '9',
|
||||
WLPreset9: '0'
|
||||
};
|
||||
|
||||
// For now
|
||||
@@ -140,23 +145,15 @@ Meteor.startup(function() {
|
||||
clearTools: 'Clear Tools'
|
||||
});
|
||||
|
||||
// Functions to register the preset switching commands
|
||||
const registerWLPresetCommands = map => _.each(map, (commandName, presetName) => {
|
||||
OHIF.commands.register(contextName, presetName, {
|
||||
name: commandName,
|
||||
action: WLPresets.applyWLPresetToActiveElement,
|
||||
params: presetName.replace('WLPreset', '')
|
||||
});
|
||||
});
|
||||
|
||||
// Register the preset switching commands
|
||||
registerWLPresetCommands({
|
||||
WLPresetSoftTissue: 'W/L Preset: Soft Tissue',
|
||||
WLPresetLung: 'W/L Preset: Lung',
|
||||
WLPresetLiver: 'W/L Preset: Liver',
|
||||
WLPresetBone: 'W/L Preset: Bone',
|
||||
WLPresetBrain: 'W/L Preset: Brain'
|
||||
});
|
||||
const applyPreset = presetName => WLPresets.applyWLPresetToActiveElement(presetName);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
OHIF.commands.register(contextName, `WLPreset${i}`, {
|
||||
name: `W/L Preset ${i + 1}`,
|
||||
action: applyPreset,
|
||||
params: i
|
||||
});
|
||||
}
|
||||
|
||||
// Register viewport navigation commands
|
||||
OHIF.commands.set(contextName, {
|
||||
|
||||
Reference in new issue
Block a user