import { Meteor } from 'meteor/meteor'; import { Session } from 'meteor/session'; import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; import { OHIF } from 'ohif-core'; import { cornerstone } from 'meteor/ohif:cornerstone'; import { viewportUtils } from './viewportUtils'; const WL_PRESET_CUSTOM = 'WL_PRESET_CUSTOM'; const WL_PRESET_DEFAULT = 'WL_PRESET_DEFAULT'; const WL_STORAGE_KEY = `WindowLevelPresetsDefinitions`; OHIF.viewer.defaultWLPresets = { 0: { id: 'Soft Tissue', 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(); } setRetrieveFunction(retrieveFunction) { this.retrieveFunction = retrieveFunction; } setStoreFunction(storeFunction) { this.storeFunction = storeFunction; } /** * Updates the enabledElement data for the Cornerstone element * to reflect the current W/L preset which is applied. * * @param {HTMLElement} element */ 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; const currentPreset = Object.values(WLPresets).find(currentPreset => { return (windowCenter === currentPreset.wc && windowWidth === currentPreset.ww); }); if (currentPreset) { presetName = currentPreset.id; } else { presetName = WL_PRESET_CUSTOM; } } wlPresetData.name = presetName; 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 {HTMLElement} 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); // Notify other components about W/L Preset changes Session.set('OHIFWlPresetApplied', presetName); } store(wlPresets) { return new Promise((resolve, reject) => { if (this.storeFunction) { this.storeFunction.call(this, WL_STORAGE_KEY, wlPresets).then(resolve).catch(reject); } else if (OHIF.user.userLoggedIn()) { OHIF.user.setData(WL_STORAGE_KEY, wlPresets).then(resolve).catch(reject); } else { const wlPresetsJSON = JSON.stringify(wlPresets); localStorage.setItem(WL_STORAGE_KEY, wlPresetsJSON); resolve(); } }).then(() => this.setOHIFWLPresets.call(this, wlPresets)); } retrieve() { return new Promise((resolve, reject) => { if (this.retrieveFunction) { this.retrieveFunction.call(this).then(resolve).catch(reject); } else if (OHIF.user.userLoggedIn()) { try { resolve(OHIF.user.getData(WL_STORAGE_KEY)); } catch(error) { reject(error); } } else { const wlPresetsJSON = localStorage.getItem(WL_STORAGE_KEY) || ''; const wlPresets = JSON.parse(wlPresetsJSON) || undefined; resolve(wlPresets); } }); } load() { return new Promise((resolve, reject) => { this.retrieve().then(wlPresets => { if (wlPresets) { this.setOHIFWLPresets.call(this, wlPresets); } else { this.loadDefaults.call(this); } }).catch(() => this.loadDefaults.call(this)); }); } 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(); } loadDefaults() { this.setOHIFWLPresets(OHIF.viewer.defaultWLPresets); } resetDefaults() { return this.store(OHIF.viewer.defaultWLPresets); } } /** * Export functions inside WLPresets namespace. */ const WLPresets = new WindowLevelPresetsManager(); Meteor.startup(() => { WLPresets.loadDefaults(); WLPresets.load(); }); export { WLPresets };