Allowing W/L Presets customization
This commit is contained in:
parent
4d9300f6c4
commit
e6b5a21ae2
@ -31,6 +31,7 @@ import './templates/form.html';
|
|||||||
import './templates/input.html';
|
import './templates/input.html';
|
||||||
import './templates/link.html';
|
import './templates/link.html';
|
||||||
import './templates/select.html';
|
import './templates/select.html';
|
||||||
|
import './templates/tr.html';
|
||||||
|
|
||||||
// wrappers
|
// wrappers
|
||||||
import './wrappers/label.html';
|
import './wrappers/label.html';
|
||||||
|
|||||||
@ -141,19 +141,23 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
|
|||||||
component.parseData = value => {
|
component.parseData = value => {
|
||||||
// Get the current schema data using component's key
|
// Get the current schema data using component's key
|
||||||
const { currentSchema } = getCurrentSchemaDefs(component.parent, instance.data.pathKey);
|
const { currentSchema } = getCurrentSchemaDefs(component.parent, instance.data.pathKey);
|
||||||
|
const { dataType } = instance.data;
|
||||||
|
|
||||||
// Stop here if there's no schema data for current key
|
// Stop here if there's no schema data for current key or no dataType defined
|
||||||
if (!currentSchema) {
|
if (!currentSchema && !dataType) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the schema is a Number
|
// Get the schema type
|
||||||
if (currentSchema.type === Number) {
|
const schemaType = currentSchema && currentSchema.type;
|
||||||
|
|
||||||
|
// Check if the type is Number
|
||||||
|
if (schemaType === Number || dataType === 'Number') {
|
||||||
return parseFloat(value);
|
return parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the schema is a Boolean
|
// Check if the type is Boolean
|
||||||
if (currentSchema.type === Boolean) {
|
if (schemaType === Boolean || dataType === 'Boolean') {
|
||||||
return !!value;
|
return !!value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
<template name="baseTr">
|
||||||
|
<tr
|
||||||
|
id="{{this.id}}"
|
||||||
|
class="{{this.class}}"
|
||||||
|
{{this.tagAttributes}}
|
||||||
|
>
|
||||||
|
{{>UI.contentBlock}}
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<template name="group">
|
<template name="group">
|
||||||
{{#baseComponent (extend this
|
{{#baseComponent (extend this
|
||||||
class=(concat 'component-group ' this.class)
|
class=(concat 'component-group ' this.class)
|
||||||
base='baseDiv'
|
base=(choose this.base 'baseDiv')
|
||||||
mixins=(concat 'group ' this.mixins)
|
mixins=(concat 'group ' this.mixins)
|
||||||
)}}
|
)}}
|
||||||
{{>UI.contentBlock}}
|
{{>UI.contentBlock}}
|
||||||
|
|||||||
@ -15,3 +15,7 @@ Template.registerHelper('sum', (...values) => {
|
|||||||
Template.registerHelper('isValidNumber', value => {
|
Template.registerHelper('isValidNumber', value => {
|
||||||
return typeof value === 'number' && !isNaN(value);
|
return typeof value === 'number' && !isNaN(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.registerHelper('filterNaN', value => {
|
||||||
|
return isNaN(value) ? '' : value;
|
||||||
|
});
|
||||||
|
|||||||
@ -11,7 +11,7 @@ Template.registerHelper('concat', (...args) => {
|
|||||||
const values = _.initial(args, 1);
|
const values = _.initial(args, 1);
|
||||||
let result = '';
|
let result = '';
|
||||||
_.each(values, value => {
|
_.each(values, value => {
|
||||||
result += value || '';
|
result += typeof value !== 'undefined' ? value : '';
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -42,7 +42,7 @@ HP.setEngine = protocolEngine => {
|
|||||||
|
|
||||||
Meteor.startup(() => {
|
Meteor.startup(() => {
|
||||||
HP.addCustomViewportSetting('wlPreset', 'Window/Level Preset', Object.create(null), (element, optionValue) => {
|
HP.addCustomViewportSetting('wlPreset', 'Window/Level Preset', Object.create(null), (element, optionValue) => {
|
||||||
if (optionValue in OHIF.viewer.wlPresets) {
|
if (_.findWhere(OHIF.viewer.wlPresets, { id: optionValue })) {
|
||||||
OHIF.viewerbase.wlPresets.applyWLPreset(optionValue, element);
|
OHIF.viewerbase.wlPresets.applyWLPreset(optionValue, element);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -113,7 +113,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
* with the given study. The best protocol are orded by score and returned in an array
|
* with the given study. The best protocol are orded by score and returned in an array
|
||||||
* @param {Object} study StudyMetadata instance object
|
* @param {Object} study StudyMetadata instance object
|
||||||
* @return {Array} Array of match objects or an empty array if no match was found
|
* @return {Array} Array of match objects or an empty array if no match was found
|
||||||
* Each match object has the score of the matching and the matched
|
* Each match object has the score of the matching and the matched
|
||||||
* protocol
|
* protocol
|
||||||
*/
|
*/
|
||||||
findMatchByStudy(study) {
|
findMatchByStudy(study) {
|
||||||
@ -122,7 +122,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
const matched = [];
|
const matched = [];
|
||||||
const studyInstance = study.getFirstInstance();
|
const studyInstance = study.getFirstInstance();
|
||||||
|
|
||||||
// Set custom attribute for study metadata
|
// Set custom attribute for study metadata
|
||||||
const numberOfAvailablePriors = this.getNumberOfAvailablePriors(study.getObjectID());
|
const numberOfAvailablePriors = this.getNumberOfAvailablePriors(study.getObjectID());
|
||||||
|
|
||||||
HP.ProtocolStore.getProtocol().forEach(protocol => {
|
HP.ProtocolStore.getProtocol().forEach(protocol => {
|
||||||
@ -318,7 +318,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
// Set the custom attribute abstractPriorValue for the study metadata
|
// Set the custom attribute abstractPriorValue for the study metadata
|
||||||
studyMetadata.setCustomAttribute(ABSTRACT_PRIOR_VALUE, abstractPriorValue);
|
studyMetadata.setCustomAttribute(ABSTRACT_PRIOR_VALUE, abstractPriorValue);
|
||||||
|
|
||||||
// Also add custom attribute
|
// Also add custom attribute
|
||||||
const firstInstance = studyMetadata.getFirstInstance();
|
const firstInstance = studyMetadata.getFirstInstance();
|
||||||
if (firstInstance instanceof InstanceMetadata) {
|
if (firstInstance instanceof InstanceMetadata) {
|
||||||
firstInstance.setCustomAttribute(ABSTRACT_PRIOR_VALUE, abstractPriorValue);
|
firstInstance.setCustomAttribute(ABSTRACT_PRIOR_VALUE, abstractPriorValue);
|
||||||
@ -329,7 +329,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
|
|
||||||
// Update the viewport to refresh layout manager with new study
|
// Update the viewport to refresh layout manager with new study
|
||||||
this.updateViewports(viewportIndex);
|
this.updateViewports(viewportIndex);
|
||||||
}, error => {
|
}, error => {
|
||||||
OHIF.log.warn(error);
|
OHIF.log.warn(error);
|
||||||
throw new OHIFError(`ProtocolEngine::matchImages could not get study metadata for the Study with the following ObjectID: ${priorStudyObjectID}`);
|
throw new OHIFError(`ProtocolEngine::matchImages could not get study metadata for the Study with the following ObjectID: ${priorStudyObjectID}`);
|
||||||
});
|
});
|
||||||
@ -650,7 +650,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the current stage to a new stage index in the display set sequence.
|
* Changes the current stage to a new stage index in the display set sequence.
|
||||||
* It checks if the next stage exists.
|
* It checks if the next stage exists.
|
||||||
*
|
*
|
||||||
* @param {Integer} stageAction An integer value specifying wheater next (1) or previous (-1) stage
|
* @param {Integer} stageAction An integer value specifying wheater next (1) or previous (-1) stage
|
||||||
@ -673,7 +673,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
// Set stage Session variable for reactivity
|
// Set stage Session variable for reactivity
|
||||||
Session.set('HangingProtocolStage', this.stage);
|
Session.set('HangingProtocolStage', this.stage);
|
||||||
|
|
||||||
// Since stage has changed, we need to update the viewports
|
// Since stage has changed, we need to update the viewports
|
||||||
// and redo matchings
|
// and redo matchings
|
||||||
this.updateViewports();
|
this.updateViewports();
|
||||||
|
|
||||||
@ -682,7 +682,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the number of Stages in the current Protocol or
|
* Retrieves the number of Stages in the current Protocol or
|
||||||
* undefined if no protocol or stages are set
|
* undefined if no protocol or stages are set
|
||||||
*/
|
*/
|
||||||
getNumProtocolStages() {
|
getNumProtocolStages() {
|
||||||
@ -710,7 +710,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
*/
|
*/
|
||||||
previousProtocolStage() {
|
previousProtocolStage() {
|
||||||
OHIF.log.info('ProtocolEngine::previousProtocolStage');
|
OHIF.log.info('ProtocolEngine::previousProtocolStage');
|
||||||
|
|
||||||
if (!this.setCurrentProtocolStage(-1)) {
|
if (!this.setCurrentProtocolStage(-1)) {
|
||||||
// Just for logging purpose
|
// Just for logging purpose
|
||||||
OHIF.log.info('ProtocolEngine::previousProtocolStage failed');
|
OHIF.log.info('ProtocolEngine::previousProtocolStage failed');
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
import { HotkeysContext } from 'meteor/ohif:hotkeys/client/classes/HotkeysContext';
|
import { HotkeysContext } from 'meteor/ohif:hotkeys/client/classes/HotkeysContext';
|
||||||
|
|
||||||
export class HotkeysManager {
|
export class HotkeysManager {
|
||||||
constructor(retrieveFunction, storeFunction) {
|
constructor() {
|
||||||
this.contexts = {};
|
this.contexts = {};
|
||||||
this.defaults = {};
|
this.defaults = {};
|
||||||
this.currentContextName = null;
|
this.currentContextName = null;
|
||||||
this.enabled = new ReactiveVar(true);
|
this.enabled = new ReactiveVar(true);
|
||||||
this.retrieveFunction = retrieveFunction;
|
this.retrieveFunction = null;
|
||||||
this.storeFunction = storeFunction;
|
this.storeFunction = null;
|
||||||
this.changeObserver = new Tracker.Dependency();
|
this.changeObserver = new Tracker.Dependency();
|
||||||
|
|
||||||
Tracker.autorun(() => {
|
Tracker.autorun(() => {
|
||||||
@ -21,6 +21,14 @@ export class HotkeysManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRetrieveFunction(retrieveFunction) {
|
||||||
|
this.retrieveFunction = retrieveFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStoreFunction(storeFunction) {
|
||||||
|
this.storeFunction = storeFunction;
|
||||||
|
}
|
||||||
|
|
||||||
store(contextName, definitions) {
|
store(contextName, definitions) {
|
||||||
const storageKey = `hotkeysDefinitions.${contextName}`;
|
const storageKey = `hotkeysDefinitions.${contextName}`;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -73,7 +81,11 @@ export class HotkeysManager {
|
|||||||
const context = this.getContext(contextName);
|
const context = this.getContext(contextName);
|
||||||
if (!context) return;
|
if (!context) return;
|
||||||
this.retrieve(contextName).then(definitions => {
|
this.retrieve(contextName).then(definitions => {
|
||||||
if (!definitions) return reject();
|
if (!definitions) {
|
||||||
|
this.changeObserver.changed();
|
||||||
|
return reject();
|
||||||
|
}
|
||||||
|
|
||||||
context.extend(definitions);
|
context.extend(definitions);
|
||||||
this.changeObserver.changed();
|
this.changeObserver.changed();
|
||||||
resolve(definitions);
|
resolve(definitions);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template name="hotkeysDialog">
|
<template name="hotkeysDialog">
|
||||||
{{#dialogSimple (extend this dialogClass='dialog-hotkeys' title='Keyboard Shortcuts')}}
|
{{#dialogSimple (extend this dialogClass='dialog-hotkeys' title='Keyboard Shortcuts')}}
|
||||||
{{>hotkeysForm this}}
|
{{! >hotkeysForm this}}
|
||||||
<hr>
|
<hr>
|
||||||
{{>windowLevelPresetsForm this}}
|
{{>windowLevelPresetsForm this}}
|
||||||
{{/dialogSimple}}
|
{{/dialogSimple}}
|
||||||
|
|||||||
@ -19,9 +19,14 @@ OHIF.user.getData = key => {
|
|||||||
// Get the user persistent data
|
// Get the user persistent data
|
||||||
const data = profile && profile.persistent;
|
const data = profile && profile.persistent;
|
||||||
|
|
||||||
if (data) {
|
let result = data;
|
||||||
return data[key];
|
const keys = key.split('.');
|
||||||
}
|
keys.forEach(key => {
|
||||||
|
if (typeof result !== 'object') return;
|
||||||
|
result = result[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store the persistent data by giving a key and a value to store
|
// Store the persistent data by giving a key and a value to store
|
||||||
|
|||||||
@ -1,6 +1,25 @@
|
|||||||
<template name="windowLevelPresetsForm">
|
<template name="windowLevelPresetsForm">
|
||||||
{{#form (extend this class='form-themed' api=instance.api)}}
|
{{#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>
|
<hr>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
{{#button class='btn btn-primary pull-right' action='save'}}Save{{/button}}
|
{{#button class='btn btn-primary pull-right' action='save'}}Save{{/button}}
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
Template.windowLevelPresetsForm.onCreated(() => {
|
Template.windowLevelPresetsForm.onCreated(() => {
|
||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
|
const { wlPresets } = OHIF.viewerbase;
|
||||||
|
|
||||||
instance.api = {
|
instance.api = {
|
||||||
save() {
|
save() {
|
||||||
const form = instance.$('form').first().data('component');
|
const form = instance.$('form').first().data('component');
|
||||||
const definitions = form.value();
|
const definitions = form.value();
|
||||||
// TODO: return save method with promise
|
wlPresets.store(definitions);
|
||||||
},
|
},
|
||||||
|
|
||||||
resetDefaults() {
|
resetDefaults() {
|
||||||
@ -17,9 +19,14 @@ Template.windowLevelPresetsForm.onCreated(() => {
|
|||||||
message: 'Are you sure you want to reset all the window level presets to their defaults?'
|
message: 'Are you sure you want to reset all the window level presets to their defaults?'
|
||||||
};
|
};
|
||||||
|
|
||||||
return OHIF.ui.showDialog('dialogConfirm', dialogOptions).then(() => {
|
return OHIF.ui.showDialog('dialogConfirm', dialogOptions).then(() => wlPresets.resetDefaults());
|
||||||
// TODO: return reset method with promise
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.windowLevelPresetsForm.helpers({
|
||||||
|
getPresetsInputInformationList() {
|
||||||
|
OHIF.viewerbase.wlPresets.changeObserver.depend();
|
||||||
|
return _.toArray(OHIF.viewer.wlPresets);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@ -1,141 +1,213 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { Session } from 'meteor/session';
|
import { Session } from 'meteor/session';
|
||||||
|
import { Tracker } from 'meteor/tracker';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { viewportUtils } from './viewportUtils';
|
import { viewportUtils } from './viewportUtils';
|
||||||
|
|
||||||
const WL_PRESET_CUSTOM = 'Custom';
|
const WL_PRESET_CUSTOM = 'Custom';
|
||||||
const WL_PRESET_DEFAULT = 'Default';
|
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
|
// TODO: add this to a namespace definition
|
||||||
Meteor.startup(function() {
|
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.
|
* Export functions inside WLPresets namespace.
|
||||||
*/
|
*/
|
||||||
|
const WLPresets = new WindowLevelPresetsManager();
|
||||||
|
|
||||||
const WLPresets = {
|
Meteor.startup(() => {
|
||||||
applyWLPreset,
|
WLPresets.load();
|
||||||
applyWLPresetToActiveElement,
|
});
|
||||||
setOHIFWLPresets,
|
|
||||||
updateElementWLPresetData
|
|
||||||
};
|
|
||||||
|
|
||||||
export { WLPresets };
|
export { WLPresets };
|
||||||
|
|||||||
@ -65,11 +65,16 @@ Meteor.startup(function() {
|
|||||||
toggleCineDialog: '',
|
toggleCineDialog: '',
|
||||||
|
|
||||||
// Preset hotkeys
|
// Preset hotkeys
|
||||||
WLPresetSoftTissue: '1',
|
WLPreset0: '1',
|
||||||
WLPresetLung: '2',
|
WLPreset1: '2',
|
||||||
WLPresetLiver: '3',
|
WLPreset2: '3',
|
||||||
WLPresetBone: '4',
|
WLPreset3: '4',
|
||||||
WLPresetBrain: '5'
|
WLPreset4: '5',
|
||||||
|
WLPreset5: '6',
|
||||||
|
WLPreset6: '7',
|
||||||
|
WLPreset7: '8',
|
||||||
|
WLPreset8: '9',
|
||||||
|
WLPreset9: '0'
|
||||||
};
|
};
|
||||||
|
|
||||||
// For now
|
// For now
|
||||||
@ -140,23 +145,15 @@ Meteor.startup(function() {
|
|||||||
clearTools: 'Clear Tools'
|
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
|
// Register the preset switching commands
|
||||||
registerWLPresetCommands({
|
const applyPreset = presetName => WLPresets.applyWLPresetToActiveElement(presetName);
|
||||||
WLPresetSoftTissue: 'W/L Preset: Soft Tissue',
|
for (let i = 0; i < 10; i++) {
|
||||||
WLPresetLung: 'W/L Preset: Lung',
|
OHIF.commands.register(contextName, `WLPreset${i}`, {
|
||||||
WLPresetLiver: 'W/L Preset: Liver',
|
name: `W/L Preset ${i + 1}`,
|
||||||
WLPresetBone: 'W/L Preset: Bone',
|
action: applyPreset,
|
||||||
WLPresetBrain: 'W/L Preset: Brain'
|
params: i
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Register viewport navigation commands
|
// Register viewport navigation commands
|
||||||
OHIF.commands.set(contextName, {
|
OHIF.commands.set(contextName, {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user