diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index b33db6600..4dd657d40 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -34,8 +34,8 @@ "cornerstone-core": "^2.3.0", "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.15.1", - "cornerstone-wado-image-loader": "^3.1.0", "dcmjs": "^0.12.3", + "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3", "hammerjs": "^2.0.8", "prop-types": "^15.6.2", diff --git a/extensions/cornerstone/src/commandsModule.js b/extensions/cornerstone/src/commandsModule.js index 99d93cc30..74c0d56f2 100644 --- a/extensions/cornerstone/src/commandsModule.js +++ b/extensions/cornerstone/src/commandsModule.js @@ -12,6 +12,9 @@ const { setViewportSpecificData } = OHIF.redux.actions; const commandsModule = ({ servicesManager }) => { const actions = { + getCornerstoneLibraries: () => { + return { cornerstone, cornerstoneTools }; + }, rotateViewport: ({ viewports, rotation }) => { const enabledElement = getEnabledElement(viewports.activeViewportIndex); @@ -262,23 +265,28 @@ const commandsModule = ({ servicesManager }) => { StudyInstanceUID, SOPInstanceUID, frameIndex, - activeViewportIndex + activeViewportIndex, }) => { const study = studyMetadataManager.get(StudyInstanceUID); const displaySet = study.findDisplaySet(ds => { - return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID) + return ( + ds.images && + ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID) + ); }); displaySet.SOPInstanceUID = SOPInstanceUID; displaySet.frameIndex = frameIndex; - window.store.dispatch(setViewportSpecificData(activeViewportIndex, displaySet)); + window.store.dispatch( + setViewportSpecificData(activeViewportIndex, displaySet) + ); cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.updateImage(enabledElement.element); }); - } + }, }; const definitions = { @@ -287,6 +295,12 @@ const commandsModule = ({ servicesManager }) => { storeContexts: [], options: {}, }, + getCornerstoneLibraries: { + commandFn: actions.getCornerstoneLibraries, + storeContexts: [], + options: {}, + context: 'VIEWER', + }, getNearbyToolData: { commandFn: actions.getNearbyToolData, storeContexts: [], diff --git a/extensions/cornerstone/src/init.js b/extensions/cornerstone/src/init.js index 27e962b80..4002ca9b2 100644 --- a/extensions/cornerstone/src/init.js +++ b/extensions/cornerstone/src/init.js @@ -4,6 +4,7 @@ import cornerstone from 'cornerstone-core'; import csTools from 'cornerstone-tools'; import merge from 'lodash.merge'; import initCornerstoneTools from './initCornerstoneTools.js'; +import initWADOImageLoader from './initWADOImageLoader.js'; import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory'; /** diff --git a/extensions/cornerstone/src/initWADOImageLoader.js b/extensions/cornerstone/src/initWADOImageLoader.js new file mode 100644 index 000000000..c33dded36 --- /dev/null +++ b/extensions/cornerstone/src/initWADOImageLoader.js @@ -0,0 +1,18 @@ +import cornerstone from 'cornerstone-core'; +import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'; +import dicomParser from 'dicom-parser'; + +//import { initWebWorkers } from './utils/index.js'; + +cornerstoneWADOImageLoader.external.cornerstone = cornerstone; +cornerstoneWADOImageLoader.external.dicomParser = dicomParser; + +cornerstoneWADOImageLoader.configure({ + beforeSend: function(xhr) { + /*const headers = OHIF.DICOMWeb.getAuthorizationHeader(); + + if (headers.Authorization) { + xhr.setRequestHeader('Authorization', headers.Authorization); + }*/ + }, +}); diff --git a/extensions/default/src/ViewerLayout/index.jsx b/extensions/default/src/ViewerLayout/index.jsx index b163d7d94..6bec8f7a2 100644 --- a/extensions/default/src/ViewerLayout/index.jsx +++ b/extensions/default/src/ViewerLayout/index.jsx @@ -31,7 +31,7 @@ function ViewerLayout({ const getPanelData = id => { const entry = extensionManager.getModuleEntry(id); // TODO, not sure why sidepanel content has to be JSX, and not a children prop? - const content = entry.component({ extensionManager }); + const content = entry.component; return { iconName: entry.iconName, diff --git a/extensions/default/src/getPanelModule.js b/extensions/default/src/getPanelModule.js index 392392bd9..9051116f8 100644 --- a/extensions/default/src/getPanelModule.js +++ b/extensions/default/src/getPanelModule.js @@ -7,37 +7,86 @@ import { displaySetManager, } from '@ohif/core'; -function StudyBrowserPanel({ extensionManager }) { - // TODO: need to check how this is intended to be passed in - const dataSource = extensionManager.dataSourceMap.dicomweb[0]; +// Create map in local state from displaySetInstanceUids to thumbnails +// Get thumbnail imageId from displaySet +// When displaySetInstanceUids change, initiate async render into canvas +// Get image data-uri from canvas offscreen +// Set image data-uri into state (NOTE: This will probably end up in the fucking browser cache, NEED to find a way to prevent that from happening) +// state triggers rerender +// +// TODO: +// - No loading UI exists yet +// - cancel promises when component is destroyed +// - show errors in UI for thumbnails if promise fails +function getImageSrc(imageId, { cornerstone }) { + // TODO: Switch to async/await when it stops failing + return new Promise((resolve, reject) => { + cornerstone.loadAndCacheImage(imageId).then(image => { + const canvas = document.createElement('canvas'); + cornerstone.renderToCanvas(canvas, image); + + resolve(canvas.toDataURL()); + }); + }); +} + +function StudyBrowserPanel({ getDataSources, commandsManager }) { const viewModel = useViewModel(); + const dataSource = getDataSources('dicomweb')[0]; + const [studyData, setStudyData] = useState([]); + const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map()); + const updateThumbnailMap = (k, v) => { + setThumbnailImageSrcMap(thumbnailImageSrcMap.set(k, v)); + }; + + useEffect(() => { + const command = commandsManager.getCommand( + 'getCornerstoneLibraries', + 'VIEWER' + ); + + if (!command) { + throw new Error('Required command not found'); + } + + const { cornerstone, cornerstoneTools } = command.commandFn(); + + if (!viewModel.displaySetInstanceUids.length) { + return; + } + + viewModel.displaySetInstanceUids.forEach(uid => { + const imageIds = dataSource.getImageIdsForDisplaySet(uid); + const imageId = imageIds[0]; + + getImageSrc(imageId, { cornerstone }).then(imageSrc => { + updateThumbnailMap(uid, imageSrc); + }); + }); + }, [viewModel.displaySetInstanceUids]); + // TODO const viewportData = []; //useViewportGrid(); const seriesTracking = {}; //useSeriesTracking(); - const [studyData, setStudyData] = useState([]); - - console.log(viewModel); - const displaySets = viewModel.displaySetInstanceUids.map( displaySetManager.getDisplaySetByUID ); - console.log(displaySets); - - if (!displaySets.length) { - return; - } - // TODO: // - Put this in something so it only runs once // - Have update the query update the dicom data store at the study level and then have this component use the data in the view model useEffect(() => { + if (!viewModel.displaySetInstanceUids.length) { + return; + } + const dSets = viewModel.displaySetInstanceUids.map( displaySetManager.getDisplaySetByUID ); + const aDisplaySet = dSets[0]; const firstStudy = dicomMetadataStore.getStudy( aDisplaySet.StudyInstanceUID @@ -74,6 +123,7 @@ function StudyBrowserPanel({ extensionManager }) { modality: ds.Modality, date: ds.SeriesDate, numInstances: ds.numImageFrames, + //imageSrc, }; const displaySetViewportData = viewportData.find( @@ -203,21 +253,30 @@ function StudyBrowserPanel({ extensionManager }) { return ; } -function getPanelModule() { +function getPanelModule({ getDataSources, commandsManager }) { + const wrappedStudyBrowserPanel = () => { + return ( + + ); + }; + return [ { name: 'seriesList', iconName: 'group-layers', iconLabel: 'Studies', label: 'Studies', - component: StudyBrowserPanel, + component: wrappedStudyBrowserPanel, }, { name: 'measure', iconName: 'list-bullets', iconLabel: 'Measure', label: 'Measurements', - component: StudyBrowserPanel, + component: wrappedStudyBrowserPanel, }, ]; } diff --git a/extensions/vtk/package.json b/extensions/vtk/package.json index cd87400dd..422d61c6f 100644 --- a/extensions/vtk/package.json +++ b/extensions/vtk/package.json @@ -28,13 +28,12 @@ "start": "yarn run dev" }, "peerDependencies": { - "@ohif/core": "^0.50.0", - "@ohif/i18n": "^0.50.0", - "@ohif/ui": "^0.50.0", - "cornerstone-core": "^2.2.8", - "cornerstone-tools": "4.15.1", - "cornerstone-wado-image-loader": "^3.1.0", - "dcmjs": "^0.12.2", + "@ohif/core": "^2.5.1", + "@ohif/i18n": "^0.52.6", + "@ohif/ui": "^2.0.0", + "cornerstone-core": "^2.3.0", + "cornerstone-wado-image-loader": "^3.1.2", + "dcmjs": "0.12.2", "dicom-parser": "^1.8.3", "i18next": "^17.0.3", "i18next-browser-languagedetector": "^3.0.1", @@ -56,9 +55,10 @@ }, "devDependencies": { "@ohif/core": "^2.9.6", - "@ohif/ui": "^1.4.4", "cornerstone-tools": "4.15.1", - "cornerstone-wado-image-loader": "^3.1.0", + "@ohif/ui": "^2.0.0", + "cornerstone-core": "^2.3.0", + "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3", "gh-pages": "^2.0.1", "i18next": "^17.0.3", diff --git a/platform/core/package.json b/platform/core/package.json index 115f1de21..fa97ec8da 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -32,7 +32,7 @@ "peerDependencies": { "cornerstone-core": "^2.3.0", "cornerstone-tools": "^4.12.0", - "cornerstone-wado-image-loader": "^3.0.0", + "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3" }, "dependencies": { diff --git a/platform/core/src/extensions/ExtensionManager.js b/platform/core/src/extensions/ExtensionManager.js index 062738f83..954a50ed1 100644 --- a/platform/core/src/extensions/ExtensionManager.js +++ b/platform/core/src/extensions/ExtensionManager.js @@ -177,6 +177,7 @@ export default class ExtensionManager { try { const extensionModule = getModuleFn({ + getDataSources: this.getDataSources, servicesManager: this._servicesManager, commandsManager: this._commandsManager, appConfig: this._appConfig, diff --git a/platform/ui/src/components/SidePanel/SidePanel.jsx b/platform/ui/src/components/SidePanel/SidePanel.jsx index 78eb036ef..cc49896f8 100644 --- a/platform/ui/src/components/SidePanel/SidePanel.jsx +++ b/platform/ui/src/components/SidePanel/SidePanel.jsx @@ -141,7 +141,7 @@ const SidePanel = ({ - {childComponent.content} + ) : ( {getPanelButtons()} diff --git a/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx b/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx index 375ecbd91..8007ea8fb 100644 --- a/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx +++ b/platform/ui/src/components/ThumbnailList/ThumbnailList.jsx @@ -18,6 +18,8 @@ const ThumbnailList = ({ thumbnails }) => { viewportIdentificator, isTracked, isActive, + imageSrc, + imageAltText, }) => { switch (componentType) { case 'thumbnail': @@ -27,6 +29,9 @@ const ThumbnailList = ({ thumbnails }) => { description={description} seriesNumber={seriesNumber} numInstances={numInstances} + imageSrc={imageSrc} + imageAltText={imageAltText} + viewportIdentificator={viewportIdentificator} isActive={isActive} onClick={() => {}} /> @@ -48,6 +53,8 @@ const ThumbnailList = ({ thumbnails }) => { description={description} seriesNumber={seriesNumber} numInstances={numInstances} + imageSrc={imageSrc} + imageAltText={imageAltText} viewportIdentificator={viewportIdentificator} isTracked={isTracked} isActive={isActive} diff --git a/platform/viewer/package.json b/platform/viewer/package.json index bc6dd2ccd..79e2ed2cf 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -67,8 +67,8 @@ "core-js": "^3.2.1", "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.15.1", - "cornerstone-wado-image-loader": "^3.1.0", "dcmjs": "^0.12.2", + "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3", "dicomweb-client": "^0.4.4", "dotenv-webpack": "^1.7.0", diff --git a/yarn.lock b/yarn.lock index 5f114ffea..0637cef56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5791,10 +5791,10 @@ cornerstone-tools@^4.12.0: "@babel/runtime" "7.1.2" cornerstone-math "0.1.7" -cornerstone-wado-image-loader@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.0.6.tgz#7b2be91f8f658de3a7474e029aa8314a35725132" - integrity sha512-1luWOh1GzkboZ3uxfu4T8vh7K9nlDiiEbQ4dYIY9oobRZVZQbQiLT8wEZPbb4JTUe7UWJISHPCNgtAU8UE9DNw== +cornerstone-wado-image-loader@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.1.2.tgz#5113c317938510670061d11d05deb70266e4a35e" + integrity sha512-cnDHrsa2GREqdMb9pjsoHgue+suHkDyuS/W0R8ZVD1lr+LQdv56dVM8oK9n7yOeorA4/IXG8630IL7uHdCHAQA== dependencies: dicom-parser "^1.8.3" pako "^1.0.10"