Compare commits

...
Author SHA1 Message Date
ohif-bot a7b5f1847a chore(release): publish [skip ci]
- @ohif/extension-debugging@0.1.4
 - @ohif/extension-dicom-rt@0.4.4
 - @ohif/extension-dicom-segmentation@0.4.4
 - @ohif/viewer@4.5.8
2020-09-10 09:14:18 +00:00
James Petts 5cb6e63b88 [IDC-1672] Highlight SEG segment/ RT structure when click to jump. (#2034)
* Highlight for RTSTRUCT.

* SEG temp crosshairs.

* Disable RT highlighting for now.

* Remove TODO
2020-09-10 11:11:59 +02:00
ohif-bot 48eb4a9ec3 chore(release): publish [skip ci]
- @ohif/viewer@4.5.7
2020-09-09 13:00:24 +00:00
James Petts d9231fd431 Split query parameter calls. (#2035) 2020-09-09 14:59:07 +02:00
ohif-bot e5b578bd8e chore(release): publish [skip ci]
- @ohif/extension-vtk@1.7.5
 - @ohif/core@2.10.4
 - @ohif/viewer@4.5.6
2020-09-03 13:51:56 +00:00
James Petts f38979037d IDC-1532 multiple series search google (#2026)
* Multiple series search for google cloud adapter.

* Revert IDC config.

* fix: 🐛 Series filtering on multiple series for google

* Revert changes to default config.

* Address reviewers comments.

* Fixed spelling mistakes
2020-09-03 15:48:32 +02:00
ohif-bot 45ca88f5b8 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.9.4
 - @ohif/extension-debugging@0.1.3
 - @ohif/extension-dicom-html@1.2.7
 - @ohif/extension-dicom-microscopy@0.51.2
 - @ohif/extension-dicom-pdf@1.0.5
 - @ohif/extension-dicom-rt@0.4.3
 - @ohif/extension-dicom-segmentation@0.4.3
 - @ohif/extension-lesion-tracker@0.2.1
 - @ohif/extension-vtk@1.7.4
 - @ohif/core@2.10.3
 - @ohif/viewer@4.5.5
2020-09-03 11:00:46 +00:00
James Petts a1dee16a3d [IDC-1939] Debug Dialog part 1 (#2011)
* WIP debug dialog

* Rename the p10 downloader extension to debugger extension, add button to toolbar. Deactivate it by default.

* Fix unit tests
2020-09-03 12:58:04 +02:00
ohif-bot 6a37a2a661 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.9.3
 - @ohif/extension-dicom-html@1.2.6
 - @ohif/extension-dicom-rt@0.4.2
 - @ohif/extension-dicom-segmentation@0.4.2
 - @ohif/extension-vtk@1.7.3
 - @ohif/core@2.10.2
 - @ohif/viewer@4.5.4
2020-09-02 12:19:23 +00:00
James Petts a8caf4bba4 [IDC-2017] Harden segmentation import to support more SEGs (#2024)
* fix: 🐛 Upgrade dcmjs version to support more SEGs
2020-09-02 14:17:30 +02:00
ohif-bot 29dcc82e28 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.2.5
 - @ohif/extension-dicom-microscopy@0.51.1
 - @ohif/extension-dicom-pdf@1.0.4
 - @ohif/extension-dicom-rt@0.4.1
 - @ohif/extension-dicom-segmentation@0.4.1
 - @ohif/extension-vtk@1.7.2
 - @ohif/core@2.10.1
 - @ohif/viewer@4.5.3
2020-08-28 15:52:27 +00:00
James Petts dbf0501031 [IDC-1994] Sort series list by SeriesNumber, and sort by same SeriesNumber by date/time. (#2010)
* Sort based on SeriesNumber and SeriesDate/SeriesTime.

* Harden, and perform final sort in algorithm if last N entries have the same SeriesNumber.

* Switch to insertion rather than sorting as sorting is too slow. Reimplement low priority sorting into new insertion method.

* Fix local file viewing.
2020-08-28 17:51:08 +02:00
ohif-bot ef28fa2ac1 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.7.1
 - @ohif/viewer@4.5.2
2020-08-24 09:45:34 +00:00
James Petts 779a7e0976 fix: 🐛 Fail gracefully on an MPR load error (#1992)
* feat: 🎸 Update react-vtkjs-viewport usage to use requestPool

* Fix import of react-vtkjs-viewport to cornerstone-tools path.

* Increase maximum load time of MPR test now we are throttling requests.

* fix: 🐛 Fail gracefully on an MPR load error

* Respond to reviewer comments.
2020-08-24 11:44:09 +02:00
ohif-bot 7f3ca1fc23 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.9.2
 - @ohif/viewer@4.5.1
2020-08-20 14:50:30 +00:00
Alex BroaddusandDanny Brown 59ab68b4ed fix: Updated react-cornerstone-viewport to version 4.0.2 (#2001)
Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
2020-08-20 10:48:16 -04:00
ohif-bot 248bc0a64a chore(release): publish [skip ci]
- @ohif/viewer@4.5.0
2020-08-20 13:13:52 +00:00
ladeirarodolfoandRodolfo Ladeira 5fdace1432 feat: 🎸 Filter/promote multiple series instances (#1533)
improve filter/promote to be applied on multiple series instances

✅ Closes: 1532

Co-authored-by: Rodolfo Ladeira <rodolfo_radical@hotmail.com>
2020-08-20 15:12:00 +02:00
ohif-bot 71b6b454a3 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.9.1
 - @ohif/viewer@4.4.1
2020-08-20 02:25:10 +00:00
Alex BroaddusandDanny Brown 3b02a06f5e perf(stackPrefetch): Added stackPrefetch config with 20 max concurrent requests (#2000)
Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
2020-08-19 22:23:00 -04:00
Erik Ziegler ebdcde1c4d fix: Fix incorrect command name in Percy test (#1999) 2020-08-19 15:42:25 -04:00
Erik Ziegler 829ac2b410 fix: Avoid lerna:restore unless we are on Netlify (closes #1926, #1996) (#1997) 2020-08-19 10:58:54 +02:00
Erik Ziegler 09745a0349 wip 2020-08-19 10:41:58 +02:00
Erik Ziegler cd73dd3027 fix: Avoid lerna:restore unless we are on Netlify (closes #1926, #1996) 2020-08-19 10:01:47 +02:00
ohif-bot ca511edeff chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.9.0
 - @ohif/extension-dicom-rt@0.4.0
 - @ohif/extension-dicom-segmentation@0.4.0
 - @ohif/extension-vtk@1.7.0
 - @ohif/core@2.10.0
 - @ohif/viewer@4.4.0
2020-08-18 12:03:55 +00:00
James PettsandErik Ziegler bb5f30ce2a feat: 🎸 Update react-vtkjs-viewport usage to use requestPool (#1984)
* feat: 🎸 Update react-vtkjs-viewport usage to use requestPool

* Fix import of react-vtkjs-viewport to cornerstone-tools path.

* Increase maximum load time of MPR test now we are throttling requests.

* Remove debugger

Co-authored-by: Erik Ziegler <erik.sweed@gmail.com>
2020-08-18 14:01:44 +02:00
ohif-bot 87d5c68e71 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.3.0
 - @ohif/viewer@4.3.1
2020-08-10 12:18:19 +00:00
James Petts 0e87ab37c2 feat: 🎸 Add support for POINT and OPEN_PLANAR for RT
* feat: 🎸 Add support for POINT and OPEN_PLANAR for RT
2020-08-10 14:16:19 +02:00
ohif-bot 756b005fe1 chore(release): publish [skip ci]
- @ohif/viewer@4.3.0
2020-08-10 09:42:47 +00:00
James Petts 3d86b5f876 feat: 🎸 Optional disable measurements panel in app config (#1912)
Can disable the measurements panel by adding disableMeasurementPanel:
true to the appConfig.

Closes: #1864
2020-08-10 11:40:11 +02:00
ohif-bot 2a647fd618 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.2.7
 - @ohif/extension-dicom-segmentation@0.3.4
 - @ohif/extension-vtk@1.6.10
 - @ohif/core@2.9.14
 - @ohif/viewer@4.2.16
2020-08-10 09:21:53 +00:00
James Petts f8fc31bcac [IDC-1670] Better display of derived datasets in side panel. (#1962)
* WIP

* WIP

* WIP

* WIP

* Working click on seg.

* Load SEG if not loaded and set active.

* RTSTRUCT

* feat: 🎸 Display SEG and RTSTRUCT in side panel

* Throw error if dataset not available.

* Add delay in microscopy viewer e2e test.

* Add delay in microscopy viewer e2e test.
2020-08-10 11:20:31 +02:00
ohif-bot b7ab88b0f1 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.5
 - @ohif/viewer@4.2.15
2020-08-05 09:48:08 +00:00
James Petts 57a30d7c34 [IDC-1956] - Remove loading %age which is always zero. (#1959)
* Remove loading percent.

* fix: 🐛 Remove % loading on cornerstone viewport
2020-08-05 11:46:42 +02:00
ohif-bot cd90665b0e chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.2.6
 - @ohif/viewer@4.2.14
2020-08-05 09:03:27 +00:00
James Petts 98c657231e IDC-1897 (#1940)
* show warnings for rt.

* Remove unneeded old code
2020-08-05 11:00:47 +02:00
91 changed files with 2021 additions and 516 deletions

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ node -v
# Install build deps and all monorepo package dependencies. Yarn Workspaces # Install build deps and all monorepo package dependencies. Yarn Workspaces
# should also symlink all projects appropriately # should also symlink all projects appropriately
yarn run lerna:restore
yarn install --no-ignore-optional --pure-lockfile yarn install --no-ignore-optional --pure-lockfile
# Build && Move PWA Output # Build && Move PWA Output
+57
View File
@@ -3,6 +3,63 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.9.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.3...@ohif/extension-cornerstone@2.9.4) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.9.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.2...@ohif/extension-cornerstone@2.9.3) (2020-09-02)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.9.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.1...@ohif/extension-cornerstone@2.9.2) (2020-08-20)
### Bug Fixes
* Updated react-cornerstone-viewport to version 4.0.2 ([#2001](https://github.com/OHIF/Viewers/issues/2001)) ([59ab68b](https://github.com/OHIF/Viewers/commit/59ab68b4edbfec010170c4429c300a53f688bf94))
## [2.9.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.0...@ohif/extension-cornerstone@2.9.1) (2020-08-20)
### Performance Improvements
* **stackPrefetch:** Added stackPrefetch config with 20 max concurrent requests ([#2000](https://github.com/OHIF/Viewers/issues/2000)) ([3b02a06](https://github.com/OHIF/Viewers/commit/3b02a06f5e250660edde4862ce44147db3dc3ab9))
# [2.9.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.5...@ohif/extension-cornerstone@2.9.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
## [2.8.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.4...@ohif/extension-cornerstone@2.8.5) (2020-08-05)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.8.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.3...@ohif/extension-cornerstone@2.8.4) (2020-07-13) ## [2.8.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.3...@ohif/extension-cornerstone@2.8.4) (2020-07-13)
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-cornerstone", "name": "@ohif/extension-cornerstone",
"version": "2.8.4", "version": "2.9.4",
"description": "OHIF extension for Cornerstone", "description": "OHIF extension for Cornerstone",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -34,9 +34,9 @@
"@ohif/ui": "^0.50.0", "@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
@@ -51,6 +51,6 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "2.3.9" "react-cornerstone-viewport": "4.0.2"
} }
} }
+2
View File
@@ -3,6 +3,7 @@ import init from './init.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm'; import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./OHIFCornerstoneViewport'); return import('./OHIFCornerstoneViewport');
@@ -24,6 +25,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'cornerstone', id: 'cornerstone',
version,
/** /**
* *
@@ -34,4 +34,11 @@ export default function(configuration = {}) {
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)'); cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
cornerstoneTools.store.state.touchProximity = 40; cornerstoneTools.store.state.touchProximity = 40;
// Configure stack prefetch
cornerstoneTools.stackPrefetch.setConfiguration({
maxImagesToPrefetch: Infinity,
preserveExistingPool: false,
maxSimultaneousRequests: 20,
});
} }
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.3...@ohif/extension-debugging@0.1.4) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-debugging
## 0.1.3 (2020-09-03)
**Note:** Version bump only for package @ohif/extension-debugging
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.1...@ohif/extension-dicom-p10-downloader@0.1.2) (2020-06-18) ## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.1...@ohif/extension-dicom-p10-downloader@0.1.2) (2020-06-18)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader **Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
{ {
"name": "@ohif/extension-dicom-p10-downloader", "name": "@ohif/extension-debugging",
"version": "0.1.2", "version": "0.1.4",
"description": "OHIF extension for downloading DICOM P10 files", "description": "OHIF extension for debugging.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
"repository": "OHIF/Viewers", "repository": "OHIF/Viewers",
@@ -33,6 +33,7 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"detect-browser": "5.1.1",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"jszip": "^3.2.2" "jszip": "^3.2.2"
@@ -0,0 +1,4 @@
.debugReportModalHeader {
padding: 10px 0 10px;
color: var(--active-color);
}
@@ -0,0 +1,159 @@
import React from 'react';
import { detect } from 'detect-browser';
import './DebugReportModal.css';
const DubugReportModal = ({
viewports,
studies,
servers,
extensionManager,
}) => {
return (
<div>
<table>
{getAppVersion()}
{getExtensionVersions(extensionManager)}
{getBrowserInfo()}
{getCurrentStudyUrl()}
{getLayout(viewports)}
</table>
</div>
);
};
const getAppVersion = () => {
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">App</th>
</tr>
<tr>
<td>Version</td>
<td>{window.version}</td>
</tr>
</React.Fragment>
);
};
const getCurrentStudyUrl = () => {
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">App</th>
</tr>
<tr>
<td>URL</td>
<td>{window.location.href}</td>
</tr>
</React.Fragment>
);
};
const getExtensionVersions = extensionManager => {
const { registeredExtensionVesions } = extensionManager;
const lineItems = Object.keys(registeredExtensionVesions).map(extensionId => {
const version = registeredExtensionVesions[extensionId];
return (
<tr>
<td>{extensionId}</td>
<td>{version}</td>
</tr>
);
});
return (
<React.Fragment>
<th className="debugReportModalHeader">Extensions</th>
{lineItems}
</React.Fragment>
);
};
const getLayout = viewports => {
const { numRows, numColumns } = viewports;
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">Viewports</th>
</tr>
<tr>
<th>Layout</th>
</tr>
<tr>
<td>Rows</td>
<td>{numRows}</td>
</tr>
<tr>
<td>Columns</td>
<td>{numColumns}</td>
</tr>
<tr>
<th>SeriesInstanceUIDs</th>
</tr>
{getSeriesInstanceUIDsPerRow(viewports)}
</React.Fragment>
);
};
const getBrowserInfo = () => {
const browser = detect();
const { name, os, type, version } = browser;
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">Platform</th>
</tr>
<tr>
<td>name</td>
<td>{name}</td>
</tr>
<tr>
<td>os</td>
<td>{os}</td>
</tr>
<tr>
<td>type</td>
<td>{type}</td>
</tr>
<tr>
<td>version</td>
<td>{version}</td>
</tr>
</React.Fragment>
);
};
const getSeriesInstanceUIDsPerRow = viewports => {
const { viewportSpecificData, numColumns } = viewports;
// NOTE viewportSpecificData is actually an object with numerical keys.
return Object.keys(viewportSpecificData).map(viewportIndex => {
const vsd = viewportSpecificData[viewportIndex];
const [row, column] = _viewportIndexToViewportPosition(
viewportIndex,
numColumns
);
return (
<tr>
<td>{`[${row},${column}]`}</td>
<td>{vsd.SeriesInstanceUID}</td>
</tr>
);
});
};
const _viewportIndexToViewportPosition = (viewportIndex, numColumns) => {
const row = Math.floor(viewportIndex / numColumns);
const column = viewportIndex % numColumns;
return [row, column];
};
export default DubugReportModal;
@@ -8,12 +8,14 @@ import {
getSOPInstanceReferencesFromViewports, getSOPInstanceReferencesFromViewports,
} from './utils'; } from './utils';
import _downloadAndZip, { downloadInstances } from './downloadAndZip'; import _downloadAndZip, { downloadInstances } from './downloadAndZip';
import DebugReportModal from './DebugReportModal';
import React from 'react';
const { const {
utils: { Queue }, utils: { Queue },
} = OHIF; } = OHIF;
export function getCommands(context) { export function getCommands(context, servicesManager, extensionManager) {
const queue = new Queue(1); const queue = new Queue(1);
const actions = { const actions = {
/** /**
@@ -87,9 +89,32 @@ export function getCommands(context) {
serverConfig serverConfig
); );
}, },
openDebugInfoModal({ viewports, studies, servers }) {
const { UIModalService } = servicesManager.services;
const WrappedDebugReportModal = function() {
return (
<DebugReportModal
viewports={viewports}
studies={studies}
servers={servers}
extensionManager={extensionManager}
/>
);
};
UIModalService.show({
content: WrappedDebugReportModal,
title: `Debugging Information`,
});
},
}; };
const definitions = { const definitions = {
openDebugInfoModal: {
commandFn: actions.openDebugInfoModal,
storeContexts: ['viewports', 'servers', 'studies'],
},
downloadAndZip: { downloadAndZip: {
commandFn: queue.bindSafe(actions.downloadAndZip, error), commandFn: queue.bindSafe(actions.downloadAndZip, error),
storeContexts: ['servers'], storeContexts: ['servers'],
@@ -1,5 +1,7 @@
import { getDicomWebClientFromConfig } from './utils'; import { getDicomWebClientFromConfig } from './utils';
import { getCommands } from './commandsModule'; import { getCommands } from './commandsModule';
import { version } from '../package.json';
import toolbarModule from './toolbarModule';
/** /**
* Constants * Constants
@@ -21,6 +23,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'dicom-p10-downloader', id: 'dicom-p10-downloader',
version,
/** /**
* LIFECYCLE HOOKS * LIFECYCLE HOOKS
@@ -37,7 +40,11 @@ export default {
* MODULE GETTERS * MODULE GETTERS
*/ */
getCommandsModule() { getCommandsModule({ servicesManager, extensionManager }) {
return getCommands(sharedContext); return getCommands(sharedContext, servicesManager, extensionManager);
},
getToolbarModule() {
return toolbarModule;
}, },
}; };
+20
View File
@@ -0,0 +1,20 @@
const TOOLBAR_BUTTON_TYPES = {
COMMAND: 'command',
};
const definitions = [
{
id: 'Debug Info',
label: 'Debug Info',
icon: 'cog',
//
type: TOOLBAR_BUTTON_TYPES.COMMAND,
commandName: 'openDebugInfoModal',
context: 'VIEWER',
},
];
export default {
definitions,
defaultContext: 'VIEWER',
};
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.6...@ohif/extension-dicom-html@1.2.7) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.5...@ohif/extension-dicom-html@1.2.6) (2020-09-02)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.4...@ohif/extension-dicom-html@1.2.5) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.3...@ohif/extension-dicom-html@1.2.4) (2020-07-13) ## [1.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.3...@ohif/extension-dicom-html@1.2.4) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-html **Note:** Version bump only for package @ohif/extension-dicom-html
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-html", "name": "@ohif/extension-dicom-html",
"version": "1.2.4", "version": "1.2.7",
"description": "OHIF extension for rendering structured reports to HTML", "description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -29,7 +29,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -22,6 +22,12 @@ const OHIFDicomHtmlSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) { getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance(); const instance = series.getFirstInstance();
const {
SeriesDate,
SeriesTime,
SeriesNumber,
} = instance._instance.metadata;
return { return {
plugin: 'html', plugin: 'html',
Modality: 'SR', Modality: 'SR',
@@ -31,6 +37,9 @@ const OHIFDicomHtmlSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(), SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(), SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(), StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDate,
SeriesTime,
SeriesNumber,
authorizationHeaders, authorizationHeaders,
}; };
}, },
+2
View File
@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js'; import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./OHIFDicomHtmlViewport'); return import('./OHIFDicomHtmlViewport');
@@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'html', id: 'html',
version,
getViewportModule() { getViewportModule() {
return OHIFDicomHtmlViewport; return OHIFDicomHtmlViewport;
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.51.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.1...@ohif/extension-dicom-microscopy@0.51.2) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
## [0.51.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.0...@ohif/extension-dicom-microscopy@0.51.1) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
# [0.51.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.9...@ohif/extension-dicom-microscopy@0.51.0) (2020-06-04) # [0.51.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.9...@ohif/extension-dicom-microscopy@0.51.0) (2020-06-04)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-microscopy", "name": "@ohif/extension-dicom-microscopy",
"version": "0.51.0", "version": "0.51.2",
"description": "OHIF extension for Dicom Microscopy", "description": "OHIF extension for Dicom Microscopy",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -12,6 +12,12 @@ const DicomMicroscopySopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient) { getDisplaySetFromSeries(series, study, dicomWebClient) {
const instance = series.getFirstInstance(); const instance = series.getFirstInstance();
const {
ContentDate,
ContentTime,
SeriesNumber,
} = instance._instance.metadata;
// Note: We are passing the dicomweb client into each viewport! // Note: We are passing the dicomweb client into each viewport!
return { return {
@@ -22,6 +28,9 @@ const DicomMicroscopySopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(), SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(), SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(), StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
SeriesTime: ContentTime,
SeriesNumber,
}; };
}, },
}; };
+2
View File
@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js'; import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./DicomMicroscopyViewport'); return import('./DicomMicroscopyViewport');
@@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'microscopy', id: 'microscopy',
version,
getViewportModule() { getViewportModule() {
return DicomMicroscopyViewport; return DicomMicroscopyViewport;
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.4...@ohif/extension-dicom-pdf@1.0.5) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-pdf
## [1.0.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.3...@ohif/extension-dicom-pdf@1.0.4) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-dicom-pdf
## [1.0.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.2...@ohif/extension-dicom-pdf@1.0.3) (2020-04-02) ## [1.0.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.2...@ohif/extension-dicom-pdf@1.0.3) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-pdf **Note:** Version bump only for package @ohif/extension-dicom-pdf
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-pdf", "name": "@ohif/extension-dicom-pdf",
"version": "1.0.3", "version": "1.0.5",
"description": "OHIF extension for Dicom PDF", "description": "OHIF extension for Dicom PDF",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -12,6 +12,12 @@ const OHIFDicomPDFSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) { getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance(); const instance = series.getFirstInstance();
const {
ContentDate,
ContentTime,
SeriesNumber,
} = instance._instance.metadata;
return { return {
plugin: 'pdf', plugin: 'pdf',
Modality: 'DOC', Modality: 'DOC',
@@ -21,6 +27,9 @@ const OHIFDicomPDFSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(), SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(), SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(), StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
SeriesTime: ContentTime,
SeriesNumber,
authorizationHeaders: authorizationHeaders, authorizationHeaders: authorizationHeaders,
}; };
}, },
+2
View File
@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js'; import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./ConnectedOHIFDicomPDFViewer'); return import('./ConnectedOHIFDicomPDFViewer');
@@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'pdf', id: 'pdf',
version,
getViewportModule() { getViewportModule() {
return ConnectedOHIFDicomPDFViewer; return ConnectedOHIFDicomPDFViewer;
}, },
+70
View File
@@ -3,6 +3,76 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.3...@ohif/extension-dicom-rt@0.4.4) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.4.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.2...@ohif/extension-dicom-rt@0.4.3) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.4.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.1...@ohif/extension-dicom-rt@0.4.2) (2020-09-02)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.0...@ohif/extension-dicom-rt@0.4.1) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-dicom-rt
# [0.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.3.0...@ohif/extension-dicom-rt@0.4.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.7...@ohif/extension-dicom-rt@0.3.0) (2020-08-10)
### Features
* 🎸 Add support for POINT and OPEN_PLANAR for RT ([0e87ab3](https://github.com/OHIF/Viewers/commit/0e87ab37c29fcf9af74bbcefca854c6e6b8707bc))
## [0.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.6...@ohif/extension-dicom-rt@0.2.7) (2020-08-10)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.5...@ohif/extension-dicom-rt@0.2.6) (2020-08-05)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.4...@ohif/extension-dicom-rt@0.2.5) (2020-07-13) ## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.4...@ohif/extension-dicom-rt@0.2.5) (2020-07-13)
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-rt", "name": "@ohif/extension-dicom-rt",
"version": "0.2.5", "version": "0.4.4",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.", "description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -30,8 +30,8 @@
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -1,5 +1,6 @@
import { MODULE_TYPES, utils, DICOMWeb } from '@ohif/core'; import { MODULE_TYPES, utils, DICOMWeb } from '@ohif/core';
import loadRTStruct from './loadRTStruct'; import loadRTStruct from './loadRTStruct';
import getSourceDisplaySet from './getSourceDisplaySet';
import id from './id'; import id from './id';
@@ -26,6 +27,7 @@ const OHIFDicomRTStructSopClassHandler = {
const { const {
SeriesDate, SeriesDate,
SeriesTime, SeriesTime,
SeriesNumber,
SeriesDescription, SeriesDescription,
FrameOfReferenceUID, FrameOfReferenceUID,
SOPInstanceUID, SOPInstanceUID,
@@ -52,6 +54,7 @@ const OHIFDicomRTStructSopClassHandler = {
isLoaded: false, isLoaded: false,
SeriesDate, SeriesDate,
SeriesTime, SeriesTime,
SeriesNumber,
SeriesDescription, SeriesDescription,
}; };
@@ -67,6 +70,10 @@ const OHIFDicomRTStructSopClassHandler = {
} }
} }
rtStructDisplaySet.getSourceDisplaySet = function(studies) {
return getSourceDisplaySet(studies, rtStructDisplaySet);
};
rtStructDisplaySet.load = function(referencedDisplaySet, studies) { rtStructDisplaySet.load = function(referencedDisplaySet, studies) {
return loadRTStruct( return loadRTStruct(
rtStructDisplaySet, rtStructDisplaySet,
@@ -12,12 +12,14 @@ import StructureSetItem from '../StructureSetItem/StructureSetItem';
import RTPanelSettings from '../RTSettings/RTSettings'; import RTPanelSettings from '../RTSettings/RTSettings';
import PanelSection from '../PanelSection/PanelSection'; import PanelSection from '../PanelSection/PanelSection';
import LoadingIndicator from '../LoadingIndicator/LoadingIndicator'; import LoadingIndicator from '../LoadingIndicator/LoadingIndicator';
import TOOL_NAMES from '../../utils/toolNames';
const { RTSTRUCT_DISPLAY_TOOL } = TOOL_NAMES;
const { studyMetadataManager } = utils; const { studyMetadataManager } = utils;
const refreshViewport = () => { const refreshViewport = () => {
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
debugger;
if (enabledElement.image) { if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element); cornerstone.updateImage(enabledElement.element);
} }
@@ -40,7 +42,6 @@ const RTPanel = ({
activeIndex, activeIndex,
isOpen, isOpen,
onContourItemClick, onContourItemClick,
noContoursNotification,
activeContexts = [], activeContexts = [],
contexts = {}, contexts = {},
}) => { }) => {
@@ -126,7 +127,7 @@ const RTPanel = ({
}, [isOpen]); }, [isOpen]);
const toContourItem = ( const toContourItem = (
{ ROINumber, ROIName, RTROIObservations, colorArray, visible }, { ROINumber, ROIName, RTROIObservations, colorArray, visible, isSupported },
loadedSet loadedSet
) => { ) => {
let interpretedType = ''; let interpretedType = '';
@@ -139,19 +140,23 @@ const RTPanel = ({
<StructureSetItem <StructureSetItem
key={ROINumber} key={ROINumber}
selected={isSameContour} selected={isSameContour}
isDisabled={!isSupported}
onClick={() => { onClick={() => {
setSelectedContour(isSameContour ? null : ROINumber); setSelectedContour(isSameContour ? null : ROINumber);
if (isCornerstone()) { if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements(); const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element; const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack'); const stackToolState = cornerstoneTools.getToolState(
element,
'stack'
);
if (!toolState) { if (!stackToolState) {
return; return;
} }
const imageIds = toolState.data[0].imageIds; const imageIds = stackToolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct'); const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour( const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
@@ -160,11 +165,17 @@ const RTPanel = ({
imageIds imageIds
); );
if (!imageId) { const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState();
noContoursNotification(); const imageIdSpecificToolState = toolState[imageId];
return; const rtstructData =
} imageIdSpecificToolState[RTSTRUCT_DISPLAY_TOOL];
const specificData = rtstructData.data.find(
rtData => rtData.ROINumber === ROINumber
);
specificData.highlight = true;
const frameIndex = imageIds.indexOf(imageId); const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get( const SOPInstanceUID = cornerstone.metaData.get(
@@ -249,7 +260,7 @@ const RTPanel = ({
key={SeriesInstanceUID} key={SeriesInstanceUID}
title={metadata.StructureSetLabel} title={metadata.StructureSetLabel}
loading={!isLoaded || !loadedSet} loading={!isLoaded || !loadedSet}
visible={isLoaded && loadedSet.visible} visible={isLoaded && loadedSet && loadedSet.visible}
hideVisibleButton={!isLoaded} hideVisibleButton={!isLoaded}
expanded={ expanded={
isLoaded && isLoaded &&
@@ -22,11 +22,6 @@
width: 100%; width: 100%;
} }
.dcmrt-structure-set-item.selected .item-actions {
height: 35px;
visibility: visible;
}
.dcmrt-structure-set-item .item-actions { .dcmrt-structure-set-item .item-actions {
margin-left: -1px; margin-left: -1px;
background-color: var(--ui-gray-darker); background-color: var(--ui-gray-darker);
@@ -47,7 +42,8 @@
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.dcmrt-structure-set-item .item-actions .btnAction:hover, .dcmrt-structure-set-item .item-actions .btnAction:active { .dcmrt-structure-set-item .item-actions .btnAction:hover,
.dcmrt-structure-set-item .item-actions .btnAction:active {
color: var(--text-primary-color); color: var(--text-primary-color);
} }
@@ -86,3 +82,12 @@
.dcmrt-structure-set-item .item-label .eye-icon.--visible { .dcmrt-structure-set-item .item-label .eye-icon.--visible {
color: var(--default-color); color: var(--default-color);
} }
.dcmrt-structure-set-item.isDisabled .item-color-section {
background-color: #e29e4a;
color: #fff;
}
.dcmrt-structure-set-item.isDisabled .item-label {
color: var(--text-disabled-color);
}
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui'; import { TableListItem, Icon, Tooltip, OverlayTrigger } from '@ohif/ui';
import './StructureSetItem.css'; import './StructureSetItem.css';
@@ -20,6 +20,7 @@ ColoredCircle.propTypes = {
const StructureSetItem = ({ const StructureSetItem = ({
index, index,
label, label,
isDisabled,
onClick, onClick,
itemClass, itemClass,
color, color,
@@ -31,22 +32,44 @@ const StructureSetItem = ({
useEffect(() => { useEffect(() => {
setIsVisible(visible); setIsVisible(visible);
}, [visible]) }, [visible]);
return ( let dcmrtClassNames = `dcmrt-structure-set-item`;
<div className={`dcmrt-structure-set-item ${selected && 'selected'}`}>
<TableListItem if (selected) {
key={index} dcmrtClassNames += ' selected';
itemKey={index} }
itemIndex={index}
itemClass={itemClass} if (isDisabled) {
itemMeta={<ColoredCircle color={color} />} dcmrtClassNames += ' isDisabled';
itemMetaClass="item-color-section" }
onItemClick={onClick}
> const warningIcon = (
<div> <span>
<div className="item-label" style={{ marginBottom: 4 }}> <Icon name="exclamation-triangle" />
<span>{label}</span> </span>
);
const tableListItem = (
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={isDisabled ? warningIcon : <ColoredCircle color={color} />}
itemMetaClass="item-color-section"
onItemClick={() => {
if (isDisabled) {
return;
}
onClick();
}}
>
<div>
<div className="item-label" style={{ marginBottom: 4 }}>
<span>{label}</span>
{!isDisabled && (
<Icon <Icon
className={`eye-icon ${isVisible && '--visible'}`} className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'} name={isVisible ? 'eye' : 'eye-closed'}
@@ -54,37 +77,72 @@ const StructureSetItem = ({
height="20px" height="20px"
onClick={event => { onClick={event => {
event.stopPropagation(); event.stopPropagation();
if (isDisabled) {
return;
}
const newVisibility = !isVisible; const newVisibility = !isVisible;
setIsVisible(newVisibility); setIsVisible(newVisibility);
onVisibilityChange(newVisibility); onVisibilityChange(newVisibility);
}} }}
/> />
</div>
{false && <div className="item-info">{'...'}</div>}
{false && (
<div className="item-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)} )}
</div> </div>
</TableListItem>
{false && <div className="item-info">{'...'}</div>}
{false && (
<div className="item-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)}
</div>
</TableListItem>
);
return (
<div className={dcmrtClassNames}>
<React.Fragment>
{isDisabled ? (
<OverlayTrigger
key={index}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Unsupported Region</div>
<div className="warningContent">
Contour type currently unsupported.
</div>
</Tooltip>
}
>
<div>{tableListItem}</div>
</OverlayTrigger>
) : (
<React.Fragment>{tableListItem} </React.Fragment>
)}
</React.Fragment>
</div> </div>
); );
}; };
@@ -99,7 +157,7 @@ StructureSetItem.propTypes = {
StructureSetItem.defaultProps = { StructureSetItem.defaultProps = {
itemClass: '', itemClass: '',
onClick: () => { }, onClick: () => {},
}; };
export default StructureSetItem; export default StructureSetItem;
@@ -0,0 +1,38 @@
export default function getSourceDisplaySet(studies, rtStructDisplaySet) {
const referencedDisplaySet = _getReferencedDisplaySet(
rtStructDisplaySet,
studies
);
rtStructDisplaySet.load(referencedDisplaySet, studies);
return referencedDisplaySet;
}
const _getReferencedDisplaySet = (rtStructDisplaySet, studies) => {
let allDisplaySets = [];
studies.forEach(study => {
allDisplaySets = allDisplaySets.concat(study.displaySets);
});
const otherDisplaySets = allDisplaySets.filter(
ds => ds.displaySetInstanceUID !== rtStructDisplaySet.displaySetInstanceUID
);
const ReferencedSeriesSequence = Array.isArray(
rtStructDisplaySet.metadata.ReferencedSeriesSequence
)
? rtStructDisplaySet.metadata.ReferencedSeriesSequence
: [rtStructDisplaySet.metadata.ReferencedSeriesSequence];
const referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
);
const referencedDisplaySet = otherDisplaySets.find(ds =>
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
);
return referencedDisplaySet;
};
+2 -13
View File
@@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import init from './init.js'; import init from './init.js';
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler'; import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
import id from './id.js'; import id from './id.js';
import RTPanel from './components/RTPanel/RTPanel'; import RTPanel from './components/RTPanel/RTPanel';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id, id,
version,
/** /**
* *
@@ -21,20 +22,9 @@ export default {
init({ servicesManager, configuration }); init({ servicesManager, configuration });
}, },
getPanelModule({ commandsManager, servicesManager, api }) { getPanelModule({ commandsManager, servicesManager, api }) {
const { UINotificationService } = servicesManager.services;
const ExtendedRTPanel = props => { const ExtendedRTPanel = props => {
const { activeContexts } = api.hooks.useAppContext(); const { activeContexts } = api.hooks.useAppContext();
const noContoursNotificationHandler = () => {
UINotificationService.show({
title: 'ROI Contour empty',
message: 'The ROI contour has no structure set data.',
type: 'error',
autoClose: false,
});
};
const contourItemClickHandler = contourData => { const contourItemClickHandler = contourData => {
commandsManager.runCommand('jumpToImage', contourData); commandsManager.runCommand('jumpToImage', contourData);
}; };
@@ -45,7 +35,6 @@ export default {
onContourItemClick={contourItemClickHandler} onContourItemClick={contourItemClickHandler}
activeContexts={activeContexts} activeContexts={activeContexts}
contexts={api.contexts} contexts={api.contexts}
noContoursNotification={noContoursNotificationHandler}
/> />
); );
}; };
+50 -33
View File
@@ -74,26 +74,17 @@ export default async function loadRTStruct(
continue; continue;
} }
_setROIContourMetadata( const isSupported = false;
structureSet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour
);
for (let c = 0; c < ContourSequence.length; c++) { const ContourSequenceArray = _toArray(ContourSequence);
for (let c = 0; c < ContourSequenceArray.length; c++) {
const { const {
ContourImageSequence, ContourImageSequence,
ContourData, ContourData,
NumberOfContourPoints, NumberOfContourPoints,
ContourGeometricType, ContourGeometricType,
} = ContourSequence[c]; } = ContourSequenceArray[c];
if (ContourGeometricType !== 'CLOSED_PLANAR') {
// TODO: Do we want to visualise types other than closed planar?
// We could easily do open planar.
continue;
}
const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID; const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID;
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID); const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
@@ -105,27 +96,47 @@ export default async function loadRTStruct(
const imagePlane = cornerstone.metaData.get('imagePlaneModule', imageId); const imagePlane = cornerstone.metaData.get('imagePlaneModule', imageId);
const points = []; const points = [];
let measurementData;
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) { switch (ContourGeometricType) {
points.push({ case 'CLOSED_PLANAR':
x: ContourData[p], case 'OPEN_PLANAR':
y: ContourData[p + 1], case 'POINT':
z: ContourData[p + 2], isSupported = true;
});
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
points.push({
x: ContourData[p],
y: ContourData[p + 1],
z: ContourData[p + 2],
});
}
transformPointsToImagePlane(points, imagePlane);
measurementData = {
handles: {
points,
},
type: ContourGeometricType,
structureSetSeriesInstanceUid: rtStructDataset.SeriesInstanceUID,
ROINumber: ReferencedROINumber,
};
imageIdSpecificToolData.push(measurementData);
break;
default:
continue;
} }
transformPointsToImagePlane(points, imagePlane);
const measurementData = {
handles: {
points,
},
structureSetSeriesInstanceUid: rtStructDataset.SeriesInstanceUID,
ROINumber: ReferencedROINumber,
};
imageIdSpecificToolData.push(measurementData);
} }
_setROIContourMetadata(
structureSet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour,
isSupported
);
} }
_setToolEnabledIfNotEnabled(rtStructDisplayToolName); _setToolEnabledIfNotEnabled(rtStructDisplayToolName);
@@ -155,7 +166,8 @@ function _setROIContourMetadata(
structureSet, structureSet,
StructureSetROISequence, StructureSetROISequence,
RTROIObservationsSequence, RTROIObservationsSequence,
ROIContour ROIContour,
isSupported
) { ) {
const StructureSetROI = StructureSetROISequence.find( const StructureSetROI = StructureSetROISequence.find(
structureSetROI => structureSetROI =>
@@ -167,6 +179,7 @@ function _setROIContourMetadata(
ROIName: StructureSetROI.ROIName, ROIName: StructureSetROI.ROIName,
ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm, ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm,
ROIDescription: StructureSetROI.ROIDescription, ROIDescription: StructureSetROI.ROIDescription,
isSupported,
visible: true, visible: true,
}; };
@@ -307,3 +320,7 @@ function _getImageIdSopInstanceUidPairsForDisplaySet(
}; };
}); });
} }
function _toArray(objOrArray) {
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
}
@@ -1,9 +1,10 @@
import { importInternal, getToolState, toolColors } from 'cornerstone-tools'; import { importInternal, getToolState, toolColors } from 'cornerstone-tools';
import TOOL_NAMES from '../utils/toolNames'; import TOOL_NAMES from '../utils/toolNames';
import drawCanvasCrosshairs from '../utils/drawCanvasCrosshairs';
// Cornerstone 3rd party dev kit imports // Cornerstone 3rd party dev kit imports
const draw = importInternal('drawing/draw'); const draw = importInternal('drawing/draw');
const drawCircle = importInternal('drawing/drawCircle');
const drawJoinedLines = importInternal('drawing/drawJoinedLines'); const drawJoinedLines = importInternal('drawing/drawJoinedLines');
const getNewContext = importInternal('drawing/getNewContext'); const getNewContext = importInternal('drawing/getNewContext');
const BaseTool = importInternal('base/BaseTool'); const BaseTool = importInternal('base/BaseTool');
@@ -36,11 +37,17 @@ export default class RTStructDisplayTool extends BaseTool {
return; return;
} }
const { lineWidth, opacity } = rtstructModule.configuration; const {
lineWidth,
opacity,
highlightOpacity,
} = rtstructModule.configuration;
// We have tool data for this element - iterate over each one and draw it // We have tool data for this element - iterate over each one and draw it
const context = getNewContext(eventData.canvasContext.canvas); const context = getNewContext(eventData.canvasContext.canvas);
let crossHairCenter;
for (let i = 0; i < toolState.data.length; i++) { for (let i = 0; i < toolState.data.length; i++) {
const data = toolState.data[i]; const data = toolState.data[i];
@@ -72,22 +79,76 @@ export default class RTStructDisplayTool extends BaseTool {
const colorArray = ROIContourData.colorArray; const colorArray = ROIContourData.colorArray;
const color = `rgba(${colorArray[0]},${colorArray[1]},${ const color = `rgba(${colorArray[0]},${colorArray[1]},${
colorArray[2] colorArray[2]
},${opacity})`; },${opacity})`;
lineWidth; let highlight = data.highlight;
const options = { color, lineWidth };
draw(context, context => { if (highlight) {
drawJoinedLines( crossHairCenter = { x: 0, y: 0 };
context,
eventData.element, points.forEach(point => {
points[points.length - 1], crossHairCenter.x += point.x;
points, crossHairCenter.y += point.y;
{ });
color,
lineWidth, crossHairCenter.x /= points.length;
} crossHairCenter.y /= points.length;
);
// TODO: Disabling hightlight for now, it'd be good to bring it back
// when we have a good way of doing this for SEG.
// options.fillStyle = color = `rgba(${colorArray[0]},${colorArray[1]},${
// colorArray[2]
// },${highlightOpacity})`;
// Draw highlight lines.
delete data.highlight; // Don't highlight on next render.
}
switch (data.type) {
case 'CLOSED_PLANAR':
this._renderClosedPlanar(context, eventData.element, points, options);
break;
case 'POINT':
this._renderPoint(context, eventData.element, points, options);
break;
case 'OPEN_PLANAR':
this._renderOpenPlanar(context, eventData.element, points, options);
break;
}
}
if (crossHairCenter) {
drawCanvasCrosshairs(eventData, crossHairCenter, {
color: toolColors.getActiveColor(),
lineWidth: 1,
}); });
} }
} }
_renderClosedPlanar(context, element, points, options) {
draw(context, context => {
drawJoinedLines(
context,
element,
points[points.length - 1],
points,
options
);
});
}
_renderPoint(context, element, points, options) {
draw(context, context => {
drawCircle(context, element, points[0], 3, options);
});
}
_renderOpenPlanar(context, element, points, options) {
draw(context, context => {
drawJoinedLines(context, element, points[0], points, options);
});
}
} }
@@ -13,7 +13,8 @@ function refreshViewport() {
const configuration = { const configuration = {
lineWidth: 3, lineWidth: 3,
opacity: 1.0, opacity: 0.75,
highlightOpacity: 0.5,
}; };
const state = { const state = {
@@ -0,0 +1,56 @@
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
const { importInternal } = cornerstoneTools;
const draw = importInternal('drawing/draw');
const drawLine = importInternal('drawing/drawLine');
const getNewContext = importInternal('drawing/getNewContext');
export default function _drawCanvasCrosshairs(eventData, center, options) {
const context = getNewContext(eventData.canvasContext.canvas);
const { element } = eventData;
const centerCanvas = cornerstone.pixelToCanvas(element, center);
const { clientWidth: width, clientHeight: height } = element;
const offset = 10;
draw(context, context => {
drawLine(
context,
element,
{ x: centerCanvas.x + offset, y: centerCanvas.y },
{ x: width, y: centerCanvas.y },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x - offset, y: centerCanvas.y },
{ x: 0, y: centerCanvas.y },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x, y: centerCanvas.y + offset },
{ x: centerCanvas.x, y: height },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x, y: centerCanvas.y - offset },
{ x: centerCanvas.x, y: 0 },
options,
'canvas'
);
});
}
@@ -3,6 +3,57 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.3...@ohif/extension-dicom-segmentation@0.4.4) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.4.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.2...@ohif/extension-dicom-segmentation@0.4.3) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.4.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.1...@ohif/extension-dicom-segmentation@0.4.2) (2020-09-02)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.0...@ohif/extension-dicom-segmentation@0.4.1) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
# [0.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.4...@ohif/extension-dicom-segmentation@0.4.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
## [0.3.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.3...@ohif/extension-dicom-segmentation@0.3.4) (2020-08-10)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.2...@ohif/extension-dicom-segmentation@0.3.3) (2020-07-13) ## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.2...@ohif/extension-dicom-segmentation@0.3.3) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation **Note:** Version bump only for package @ohif/extension-dicom-segmentation
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-segmentation", "name": "@ohif/extension-dicom-segmentation",
"version": "0.3.3", "version": "0.4.4",
"description": "OHIF extension for viewing segmentations in the 2D MPR view", "description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -30,8 +30,8 @@
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -5,6 +5,10 @@ import cornerstone from 'cornerstone-core';
import moment from 'moment'; import moment from 'moment';
import { utils, log } from '@ohif/core'; import { utils, log } from '@ohif/core';
import { ScrollableArea, TableList, Icon } from '@ohif/ui'; import { ScrollableArea, TableList, Icon } from '@ohif/ui';
import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool';
import setActiveLabelmap from '../../utils/setActiveLabelMap';
import refreshViewports from '../../utils/refreshViewports';
import { import {
BrushColorSelector, BrushColorSelector,
@@ -19,12 +23,6 @@ import SegmentationSettings from '../SegmentationSettings/SegmentationSettings';
const { studyMetadataManager } = utils; const { studyMetadataManager } = utils;
const refreshViewport = () => {
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
};
/** /**
* SegmentationPanel component * SegmentationPanel component
* *
@@ -68,8 +66,7 @@ const SegmentationPanel = ({
*/ */
const [state, setState] = useState({ const [state, setState] = useState({
brushRadius: DEFAULT_BRUSH_RADIUS, brushRadius: DEFAULT_BRUSH_RADIUS,
brushColor: brushColor: 'rgba(221, 85, 85, 1)',
'rgba(221, 85, 85, 1)',
selectedSegment: null, selectedSegment: null,
selectedSegmentation: null, selectedSegmentation: null,
showSegmentationSettings: false, showSegmentationSettings: false,
@@ -78,7 +75,7 @@ const SegmentationPanel = ({
segmentList: [], segmentList: [],
cachedSegmentsProperties: [], cachedSegmentsProperties: [],
isLoading: false, isLoading: false,
isDisabled: true isDisabled: true,
}); });
useEffect(() => { useEffect(() => {
@@ -112,7 +109,10 @@ const SegmentationPanel = ({
* allows us to easily watch the module or the segmentations loading process in any other component * allows us to easily watch the module or the segmentations loading process in any other component
* without subscribing to external events. * without subscribing to external events.
*/ */
document.addEventListener('extensiondicomsegmentationsegloaded', refreshSegmentations); document.addEventListener(
'extensiondicomsegmentationsegloaded',
refreshSegmentations
);
/* /*
* These are specific to each element; * These are specific to each element;
@@ -127,7 +127,10 @@ const SegmentationPanel = ({
); );
return () => { return () => {
document.removeEventListener('extensiondicomsegmentationsegloaded', refreshSegmentations); document.removeEventListener(
'extensiondicomsegmentationsegloaded',
refreshSegmentations
);
cornerstoneTools.store.state.enabledElements.forEach(enabledElement => cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
enabledElement.removeEventListener( enabledElement.removeEventListener(
'cornerstonetoolslabelmapmodified', 'cornerstonetoolslabelmapmodified',
@@ -169,26 +172,29 @@ const SegmentationPanel = ({
selectedSegmentation: brushStackState.activeLabelmapIndex, selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList, labelmapList,
segmentList, segmentList,
isDisabled isDisabled,
})); }));
} else { } else {
setState(state => ({ setState(state => ({
...state, ...state,
labelmapList: [], labelmapList: [],
segmentList: [], segmentList: [],
isDisabled isDisabled,
})); }));
} }
} }
}, [ }, [viewports, activeIndex, state.isLoading]);
viewports,
activeIndex,
state.isLoading
]);
useEffect(() => { useEffect(() => {
refreshSegmentations(); refreshSegmentations();
}, [viewports, activeIndex, isOpen, state.selectedSegmentation, activeContexts, state.isLoading]); }, [
viewports,
activeIndex,
isOpen,
state.selectedSegmentation,
activeContexts,
state.isLoading,
]);
/* Handle open/closed panel behaviour */ /* Handle open/closed panel behaviour */
useEffect(() => { useEffect(() => {
@@ -223,12 +229,10 @@ const SegmentationPanel = ({
title: displayDescription, title: displayDescription,
description: displayDate, description: displayDate,
onClick: async () => { onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap( const activatedLabelmapIndex = await setActiveLabelmap(
activeViewport, activeViewport,
studies, studies,
displaySet, displaySet,
firstImageId,
brushStackState.activeLabelmapIndex,
() => onSelectedSegmentationChange(), () => onSelectedSegmentationChange(),
onDisplaySetLoadFailure onDisplaySetLoadFailure
); );
@@ -331,6 +335,12 @@ const SegmentationPanel = ({
imageId imageId
); );
DICOMSegTempCrosshairsTool.addCrosshair(
element,
imageId,
segmentNumber
);
onSegmentItemClick({ onSegmentItemClick({
StudyInstanceUID, StudyInstanceUID,
SOPInstanceUID, SOPInstanceUID,
@@ -376,9 +386,13 @@ const SegmentationPanel = ({
return !segmentsHidden[segmentIndex]; return !segmentsHidden[segmentIndex];
}; };
const cachedSegmentProperties = state.cachedSegmentsProperties[segmentNumber]; const cachedSegmentProperties =
state.cachedSegmentsProperties[segmentNumber];
let visible = isSegmentVisible(); let visible = isSegmentVisible();
if (cachedSegmentProperties && cachedSegmentProperties.visible !== visible) { if (
cachedSegmentProperties &&
cachedSegmentProperties.visible !== visible
) {
toggleSegmentVisibility(); toggleSegmentVisibility();
} }
@@ -406,8 +420,10 @@ const SegmentationPanel = ({
onSegmentVisibilityChange(segmentNumber, newVisibility); onSegmentVisibilityChange(segmentNumber, newVisibility);
} }
updateCachedSegmentsProperties(segmentNumber, { visible: newVisibility }); updateCachedSegmentsProperties(segmentNumber, {
refreshViewport(); visible: newVisibility,
});
refreshViewports();
}} }}
/> />
); );
@@ -429,10 +445,9 @@ const SegmentationPanel = ({
const segmentsProperties = state.cachedSegmentsProperties; const segmentsProperties = state.cachedSegmentsProperties;
const segmentProperties = state.cachedSegmentsProperties[segmentNumber]; const segmentProperties = state.cachedSegmentsProperties[segmentNumber];
segmentsProperties[segmentNumber] = segmentsProperties[segmentNumber] = segmentProperties
segmentProperties ? ? { ...segmentProperties, ...properties }
{ ...segmentProperties, ...properties } : : properties;
properties;
updateState('cachedSegmentsProperties', segmentsProperties); updateState('cachedSegmentsProperties', segmentsProperties);
}; };
@@ -499,10 +514,13 @@ const SegmentationPanel = ({
configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive; configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive;
configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive; configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive;
onConfigurationChange(newConfiguration); onConfigurationChange(newConfiguration);
refreshViewport(); refreshViewports();
}; };
const disabledConfigurationFields = ['outlineAlpha', 'shouldRenderInactiveLabelmaps']; const disabledConfigurationFields = [
'outlineAlpha',
'shouldRenderInactiveLabelmaps',
];
if (state.showSegmentationSettings) { if (state.showSegmentationSettings) {
return ( return (
<SegmentationSettings <SegmentationSettings
@@ -514,7 +532,10 @@ const SegmentationPanel = ({
); );
} else { } else {
return ( return (
<div className={`dcmseg-segmentation-panel ${state.isDisabled && 'disabled'}`}> <div
className={`dcmseg-segmentation-panel ${state.isDisabled &&
'disabled'}`}
>
<Icon <Icon
className="cog-icon" className="cog-icon"
name="cog" name="cog"
@@ -613,57 +634,6 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => {
return referencedDisplaysets; return referencedDisplaysets;
}; };
/**
*
*
* @param {*} viewportSpecificData
* @param {*} studies
* @param {*} displaySet
* @param {*} firstImageId
* @param {*} activeLabelmapIndex
* @returns
*/
const _setActiveLabelmap = async (
viewportSpecificData,
studies,
displaySet,
firstImageId,
activeLabelmapIndex,
callback = () => { },
onDisplaySetLoadFailure
) => {
if (displaySet.labelmapIndex === activeLabelmapIndex) {
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
return displaySet.labelmapIndex;
}
if (!displaySet.isLoaded) {
// What props does this expect `viewportSpecificData` to have?
// TODO: Should this return the `labelmapIndex`?
const loadPromise = displaySet.load(viewportSpecificData, studies);
loadPromise.catch(error => {
onDisplaySetLoadFailure(error);
// Return old index.
return activeLabelmapIndex;
});
await loadPromise;
}
const { state } = cornerstoneTools.getModule('segmentation');
const brushStackState = state.series[firstImageId];
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex;
refreshViewport();
callback();
return displaySet.labelmapIndex;
};
/** /**
* *
* @param {*} firstImageId * @param {*} firstImageId
@@ -683,7 +653,7 @@ const _setActiveSegment = (firstImageId, segmentIndex, activeSegmentIndex) => {
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
labelmap3D.activeSegmentIndex = segmentIndex; labelmap3D.activeSegmentIndex = segmentIndex;
refreshViewport(); refreshViewports();
return segmentIndex; return segmentIndex;
}; };
@@ -1,5 +1,6 @@
import { MODULE_TYPES, utils } from '@ohif/core'; import { MODULE_TYPES, utils } from '@ohif/core';
import loadSegmentation from './loadSegmentation'; import loadSegmentation from './loadSegmentation';
import getSourceDisplaySet from './getSourceDisplaySet';
// TODO: Should probably use dcmjs for this // TODO: Should probably use dcmjs for this
const SOP_CLASS_UIDS = { const SOP_CLASS_UIDS = {
@@ -27,6 +28,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
const { const {
SeriesDate, SeriesDate,
SeriesTime, SeriesTime,
SeriesNumber,
SeriesDescription, SeriesDescription,
FrameOfReferenceUID, FrameOfReferenceUID,
SOPInstanceUID, SOPInstanceUID,
@@ -51,9 +53,14 @@ export default function getSopClassHandlerModule({ servicesManager }) {
isLoaded: false, isLoaded: false,
SeriesDate, SeriesDate,
SeriesTime, SeriesTime,
SeriesNumber,
SeriesDescription, SeriesDescription,
}; };
segDisplaySet.getSourceDisplaySet = function(studies) {
return getSourceDisplaySet(studies, segDisplaySet);
};
segDisplaySet.load = function(referencedDisplaySet, studies) { segDisplaySet.load = function(referencedDisplaySet, studies) {
return loadSegmentation(segDisplaySet, referencedDisplaySet, studies); return loadSegmentation(segDisplaySet, referencedDisplaySet, studies);
}; };
@@ -0,0 +1,88 @@
import setActiveLabelmap from './utils/setActiveLabelMap';
import { classes } from '@ohif/core';
const { ImageSet } = classes;
export default function getSourceDisplaySet(studies, segDisplaySet) {
const referencedDisplaySet = _getReferencedDisplaySet(segDisplaySet, studies);
setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet);
return referencedDisplaySet;
}
const _getReferencedDisplaySet = (segDisplaySet, studies) => {
let allDisplaySets = [];
studies.forEach(study => {
allDisplaySets = allDisplaySets.concat(study.displaySets);
});
const otherDisplaySets = allDisplaySets.filter(
ds => ds.displaySetInstanceUID !== segDisplaySet.displaySetInstanceUID
);
const { metadata } = segDisplaySet;
let referencedSeriesInstanceUIDs;
if (metadata.ReferencedSeriesSequence) {
const ReferencedSeriesSequence = _toArray(
metadata.ReferencedSeriesSequence
);
referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
);
} else {
const { PerFrameFunctionalGroupsSequence } = metadata;
let SourceImageSequence;
if (metadata.SourceImageSequence) {
SourceImageSequence = metadata.SourceImageSequence;
} else {
const firstFunctionalGroups = _toArray(
PerFrameFunctionalGroupsSequence
)[0];
const { DerivationImageSequence } = firstFunctionalGroups;
SourceImageSequence = DerivationImageSequence;
}
const firstSourceImage = _toArray(SourceImageSequence)[0];
const { ReferencedSOPInstanceUID } = firstSourceImage;
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
otherDisplaySets,
ReferencedSOPInstanceUID
);
}
const referencedDisplaySet = otherDisplaySets.find(ds =>
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
);
return referencedDisplaySet;
};
const _findReferencedSeriesInstanceUIDsFromSOPInstanceUID = (
displaySets,
SOPInstanceUID
) => {
const imageSets = displaySets.filter(ds => ds instanceof ImageSet);
for (let i = 0; i < imageSets.length; i++) {
const { images } = imageSets[i];
for (let j = 0; j < images.length; j++) {
if (images[j].SOPInstanceUID === SOPInstanceUID) {
return [images[j].getData().metadata.SeriesInstanceUID];
}
}
}
};
function _toArray(arrayOrObject) {
return Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];
}
+2 -1
View File
@@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import init from './init.js'; import init from './init.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js'; import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js'; import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'com.ohif.dicom-segmentation', id: 'com.ohif.dicom-segmentation',
version,
/** /**
* *
@@ -1,4 +1,5 @@
import csTools from 'cornerstone-tools'; import csTools from 'cornerstone-tools';
import DICOMSegTempCrosshairsTool from './tools/DICOMSegTempCrosshairsTool';
/** /**
* *
@@ -17,4 +18,6 @@ export default function init({ servicesManager, configuration = {} }) {
alwaysEraseOnClick: true, alwaysEraseOnClick: true,
}, },
}); });
csTools.addTool(DICOMSegTempCrosshairsTool);
} }
@@ -0,0 +1,127 @@
import cornerstoneTools, {
importInternal,
getToolState,
toolColors,
getModule,
globalImageIdSpecificToolStateManager,
} from 'cornerstone-tools';
import cornerstone from 'cornerstone-core';
import drawCanvasCrosshairs from '../utils/drawCanvasCrosshairs';
import TOOL_NAMES from './TOOL_NAMES';
const { DICOM_SEG_TEMP_CROSSHAIRS_TOOL } = TOOL_NAMES;
const { getters } = getModule('segmentation');
// Cornerstone 3rd party dev kit imports
const BaseTool = importInternal('base/BaseTool');
/**
* @class RTStructDisplayTool - Renders RTSTRUCT data in a read only manner (i.e. as an overlay).
* @extends cornerstoneTools.BaseTool
*/
export default class DICOMSegTempCrosshairsTool extends BaseTool {
constructor(props = {}) {
const defaultProps = {
mixins: ['enabledOrDisabledBinaryTool'],
name: DICOM_SEG_TEMP_CROSSHAIRS_TOOL,
};
const initialProps = Object.assign(defaultProps, props);
super(initialProps);
this._rtStructModule = cornerstoneTools.getModule('rtstruct');
}
renderToolData(evt) {
const eventData = evt.detail;
const { element } = eventData;
const toolState = getToolState(evt.currentTarget, this.name);
if (!toolState) {
return;
}
// We have tool data for this element - iterate over each one and draw it
for (let i = 0; i < toolState.data.length; i++) {
const data = toolState.data[i];
const crossHairCenter = data.center;
drawCanvasCrosshairs(eventData, crossHairCenter, {
color: toolColors.getActiveColor(),
lineWidth: 1,
});
// Remove the crosshairs, we only render them for one redraw.
toolState.data.pop();
}
}
}
DICOMSegTempCrosshairsTool.addCrosshair = (element, imageId, segmentNumber) => {
const labelmap3D = getters.labelmap3D(element);
const stackToolState = cornerstoneTools.getToolState(element, 'stack');
const enabledElement = cornerstone.getEnabledElement(element);
const { rows, columns } = enabledElement.image;
if (!stackToolState) {
return;
}
const imageIds = stackToolState.data[0].imageIds;
const imageIdIndex = imageIds.findIndex(imgId => imgId === imageId);
const labelmap2D = labelmap3D.labelmaps2D[imageIdIndex];
const { pixelData } = labelmap2D;
let xCenter = 0;
let yCenter = 0;
let count = 0;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < columns; x++) {
if (pixelData[y * columns + x] === segmentNumber) {
count++;
xCenter += x + 0.5;
yCenter += y + 0.5;
}
}
}
xCenter /= count;
yCenter /= count;
const globalToolState = globalImageIdSpecificToolStateManager.saveToolState();
if (!globalToolState[imageId]) {
globalToolState[imageId] = {};
}
const imageIdSpecificToolState = globalToolState[imageId];
if (!imageIdSpecificToolState[DICOM_SEG_TEMP_CROSSHAIRS_TOOL]) {
imageIdSpecificToolState[DICOM_SEG_TEMP_CROSSHAIRS_TOOL] = { data: [] };
} else if (!imageIdSpecificToolState[DICOM_SEG_TEMP_CROSSHAIRS_TOOL].data) {
imageIdSpecificToolState[DICOM_SEG_TEMP_CROSSHAIRS_TOOL].data = [];
}
const toolSpecificData =
imageIdSpecificToolState[DICOM_SEG_TEMP_CROSSHAIRS_TOOL].data;
toolSpecificData.push({ center: { x: xCenter, y: yCenter }, segmentNumber });
// Enable the tool if not enabled for the element.
const tool = cornerstoneTools.getToolForElement(
element,
DICOM_SEG_TEMP_CROSSHAIRS_TOOL
);
if (tool.mode !== 'enabled') {
// If not already active or passive, set passive so contours render.
cornerstoneTools.setToolEnabled(DICOM_SEG_TEMP_CROSSHAIRS_TOOL);
}
};
@@ -0,0 +1,5 @@
const TOOL_NAMES = {
DICOM_SEG_TEMP_CROSSHAIRS_TOOL: 'DICOMSegTempCrosshairsTool',
};
export default TOOL_NAMES;
@@ -0,0 +1,56 @@
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
const { importInternal } = cornerstoneTools;
const draw = importInternal('drawing/draw');
const drawLine = importInternal('drawing/drawLine');
const getNewContext = importInternal('drawing/getNewContext');
export default function _drawCanvasCrosshairs(eventData, center, options) {
const context = getNewContext(eventData.canvasContext.canvas);
const { element } = eventData;
const centerCanvas = cornerstone.pixelToCanvas(element, center);
const { clientWidth: width, clientHeight: height } = element;
const offset = 10;
draw(context, context => {
drawLine(
context,
element,
{ x: centerCanvas.x + offset, y: centerCanvas.y },
{ x: width, y: centerCanvas.y },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x - offset, y: centerCanvas.y },
{ x: 0, y: centerCanvas.y },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x, y: centerCanvas.y + offset },
{ x: centerCanvas.x, y: height },
options,
'canvas'
);
drawLine(
context,
element,
{ x: centerCanvas.x, y: centerCanvas.y - offset },
{ x: centerCanvas.x, y: 0 },
options,
'canvas'
);
});
}
@@ -0,0 +1,5 @@
export default function refreshViewports() {
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
}
@@ -0,0 +1,67 @@
import { utils, log } from '@ohif/core';
import cornerstoneTools from 'cornerstone-tools';
import refreshViewports from './refreshViewports';
const { studyMetadataManager } = utils;
/**
*
*
* @param {*} viewportSpecificData
* @param {*} studies
* @param {*} displaySet
* @param {*} firstImageId
* @param {*} activeLabelmapIndex
* @returns
*/
export default async function setActiveLabelmap(
referencedDisplaySet,
studies,
displaySet,
callback = () => {},
onDisplaySetLoadFailure = err => {
throw new Error(err.message);
}
) {
const studyMetadata = studyMetadataManager.get(
referencedDisplaySet.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
referencedDisplaySet.displaySetInstanceUID
);
let { state } = cornerstoneTools.getModule('segmentation');
let brushStackState = state.series[firstImageId];
const activeLabelmapIndex = brushStackState
? brushStackState.activeLabelmapIndex
: undefined;
if (displaySet.labelmapIndex === activeLabelmapIndex) {
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
return displaySet.labelmapIndex;
}
if (!displaySet.isLoaded) {
const loadPromise = displaySet.load(referencedDisplaySet, studies);
loadPromise.catch(error => {
onDisplaySetLoadFailure(error);
// Return old index.
return activeLabelmapIndex;
});
await loadPromise;
}
// This might have just been created, so need to use the non-cached value.
state = cornerstoneTools.getModule('segmentation').state;
brushStackState = state.series[firstImageId];
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex;
refreshViewports();
callback();
return displaySet.labelmapIndex;
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.2.0...@ohif/extension-lesion-tracker@0.2.1) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-lesion-tracker
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.1.0...@ohif/extension-lesion-tracker@0.2.0) (2020-02-10) # [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.1.0...@ohif/extension-lesion-tracker@0.2.0) (2020-02-10)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-lesion-tracker", "name": "@ohif/extension-lesion-tracker",
"version": "0.2.0", "version": "0.2.1",
"description": "OHIF extension for Lesion Tracker", "description": "OHIF extension for Lesion Tracker",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
+3 -1
View File
@@ -1,10 +1,12 @@
import MeasurementComparisonTable from './components/MeasurementComparisonTable'; import MeasurementComparisonTable from './components/MeasurementComparisonTable';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'lesion-tracker', id: 'lesion-tracker',
version,
/** /**
* @param {object} params * @param {object} params
@@ -26,7 +28,7 @@ export default {
component: MeasurementComparisonTable, component: MeasurementComparisonTable,
}, },
], ],
defaultContext: ['VIEWER'] defaultContext: ['VIEWER'],
}; };
}, },
+62
View File
@@ -3,6 +3,68 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.7.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.4...@ohif/extension-vtk@1.7.5) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.7.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.3...@ohif/extension-vtk@1.7.4) (2020-09-03)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.2...@ohif/extension-vtk@1.7.3) (2020-09-02)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.1...@ohif/extension-vtk@1.7.2) (2020-08-28)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.0...@ohif/extension-vtk@1.7.1) (2020-08-24)
### Bug Fixes
* 🐛 Fail gracefully on an MPR load error ([#1992](https://github.com/OHIF/Viewers/issues/1992)) ([779a7e0](https://github.com/OHIF/Viewers/commit/779a7e0976aa3a2d2bf1dc8f056950f0545c9fff))
# [1.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.10...@ohif/extension-vtk@1.7.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
## [1.6.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.9...@ohif/extension-vtk@1.6.10) (2020-08-10)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.8...@ohif/extension-vtk@1.6.9) (2020-07-23) ## [1.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.8...@ohif/extension-vtk@1.6.9) (2020-07-23)
**Note:** Version bump only for package @ohif/extension-vtk **Note:** Version bump only for package @ohif/extension-vtk
+6 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-vtk", "name": "@ohif/extension-vtk",
"version": "1.6.9", "version": "1.7.5",
"description": "OHIF extension for VTK.js", "description": "OHIF extension for VTK.js",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -33,9 +33,9 @@
"@ohif/i18n": "^0.50.0", "@ohif/i18n": "^0.50.0",
"@ohif/ui": "^0.50.0", "@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"i18next": "^17.0.3", "i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",
@@ -50,12 +50,12 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"react-vtkjs-viewport": "^0.9.0" "react-vtkjs-viewport": "^0.10.4"
}, },
"devDependencies": { "devDependencies": {
"@ohif/core": "^2.9.13", "@ohif/core": "^2.10.4",
"@ohif/ui": "^1.5.3", "@ohif/ui": "^1.5.3",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"gh-pages": "^2.0.1", "gh-pages": "^2.0.1",
+9 -9
View File
@@ -32,16 +32,16 @@ class LoadingIndicator extends PureComponent {
</div> </div>
</div> </div>
) : ( ) : (
<div className="imageViewerLoadingIndicator loadingIndicator"> <div className="imageViewerLoadingIndicator loadingIndicator">
<div className="indicatorContents"> <div className="indicatorContents">
<p> <p>
{this.props.t('Loading...')} {this.props.t('Loading...')}
<i className="fa fa-spin fa-circle-o-notch fa-fw" /> <i className="fa fa-spin fa-circle-o-notch fa-fw" />
{percComplete} {percComplete}
</p> </p>
</div>
</div> </div>
)} </div>
)}
</React.Fragment> </React.Fragment>
); );
} }
+51 -26
View File
@@ -49,27 +49,30 @@ class OHIFVTKViewport extends Component {
state = { state = {
volumes: null, volumes: null,
paintFilterLabelMapImageData: null, paintFilterLabelMapImageData: null,
paintFilterBackgroundImageData: null paintFilterBackgroundImageData: null,
percentComplete: 0,
isLoaded: false,
}; };
static propTypes = { static propTypes = {
viewportData: PropTypes.shape({ viewportData: PropTypes.shape({
studies: PropTypes.array, studies: PropTypes.array.isRequired,
displaySet: PropTypes.shape({ displaySet: PropTypes.shape({
StudyInstanceUID: PropTypes.string, StudyInstanceUID: PropTypes.string.isRequired,
displaySetInstanceUID: PropTypes.string, displaySetInstanceUID: PropTypes.string.isRequired,
sopClassUIDs: PropTypes.arrayOf(PropTypes.string), sopClassUIDs: PropTypes.arrayOf(PropTypes.string),
SOPInstanceUID: PropTypes.string, SOPInstanceUID: PropTypes.string,
frameIndex: PropTypes.number, frameIndex: PropTypes.number,
}), }),
}), }),
viewportIndex: PropTypes.number, viewportIndex: PropTypes.number.isRequired,
children: PropTypes.node, children: PropTypes.node,
onScroll: PropTypes.func, onScroll: PropTypes.func,
servicesManager: PropTypes.object.isRequired,
}; };
static defaultProps = { static defaultProps = {
onScroll: () => { }, onScroll: () => {},
}; };
static id = 'OHIFVTKViewport'; static id = 'OHIFVTKViewport';
@@ -156,9 +159,11 @@ class OHIFVTKViewport extends Component {
const { activeLabelmapIndex } = brushStackState; const { activeLabelmapIndex } = brushStackState;
const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex]; const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex];
this.segmentsDefaultProperties = labelmap3D.segmentsHidden.map(isHidden => { this.segmentsDefaultProperties = labelmap3D.segmentsHidden.map(
return { visible: !isHidden }; isHidden => {
}); return { visible: !isHidden };
}
);
const vtkLabelmapID = `${firstImageId}_${activeLabelmapIndex}`; const vtkLabelmapID = `${firstImageId}_${activeLabelmapIndex}`;
@@ -349,7 +354,7 @@ class OHIFVTKViewport extends Component {
if ( if (
displaySet.displaySetInstanceUID !== displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID || prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID || displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex displaySet.frameIndex !== prevDisplaySet.frameIndex
) { ) {
@@ -360,34 +365,54 @@ class OHIFVTKViewport extends Component {
loadProgressively(imageDataObject) { loadProgressively(imageDataObject) {
loadImageData(imageDataObject); loadImageData(imageDataObject);
const { isLoading, insertPixelDataPromises } = imageDataObject; const { isLoading, imageIds } = imageDataObject;
const NumberOfFrames = insertPixelDataPromises.length;
if (!isLoading) { if (!isLoading) {
this.setState({ isLoaded: true }); this.setState({ isLoaded: true });
return; return;
} }
insertPixelDataPromises.forEach(promise => { const NumberOfFrames = imageIds.length;
promise.then(numberProcessed => {
const percentComplete = Math.floor(
(numberProcessed * 100) / NumberOfFrames
);
if (percentComplete !== this.state.percentComplete) { const onPixelDataInsertedCallback = numberProcessed => {
this.setState({ const percentComplete = Math.floor(
percentComplete, (numberProcessed * 100) / NumberOfFrames
);
if (percentComplete !== this.state.percentComplete) {
this.setState({
percentComplete,
});
}
};
const onPixelDataInsertedErrorCallback = error => {
const { UINotificationService } = this.props.servicesManager.services;
if (!this.hasError) {
if (this.props.viewportIndex === 0) {
// Only show the notification from one viewport 1 in MPR2D.
UINotificationService.show({
title: 'MPR Load Error',
message: error.message,
type: 'error',
autoClose: false,
}); });
} }
});
});
Promise.all(insertPixelDataPromises).then(() => { this.hasError = true;
}
};
const onAllPixelDataInsertedCallback = () => {
this.setState({ this.setState({
isLoaded: true, isLoaded: true,
}); });
}); };
imageDataObject.onPixelDataInserted(onPixelDataInsertedCallback);
imageDataObject.onAllPixelDataInserted(onAllPixelDataInsertedCallback);
imageDataObject.onPixelDataInsertedError(onPixelDataInsertedErrorCallback);
} }
render() { render() {
@@ -435,7 +460,7 @@ class OHIFVTKViewport extends Component {
segmentsDefaultProperties: this.segmentsDefaultProperties, segmentsDefaultProperties: this.segmentsDefaultProperties,
onNewSegmentationRequested: () => { onNewSegmentationRequested: () => {
this.setStateFromProps(); this.setStateFromProps();
} },
}} }}
onScroll={this.props.onScroll} onScroll={this.props.onScroll}
/> />
+6 -2
View File
@@ -3,6 +3,7 @@ import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import withCommandsManager from './withCommandsManager.js'; import withCommandsManager from './withCommandsManager.js';
import { version } from '../package.json';
// This feels weird // This feels weird
// import loadLocales from './loadLocales'; // import loadLocales from './loadLocales';
@@ -15,9 +16,12 @@ const vtkExtension = {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'vtk', id: 'vtk',
version,
getViewportModule({ commandsManager }) { getViewportModule({ commandsManager, servicesManager }) {
const ExtendedVTKViewport = props => <OHIFVTKViewport {...props} />; const ExtendedVTKViewport = props => (
<OHIFVTKViewport {...props} servicesManager={servicesManager} />
);
return withCommandsManager(ExtendedVTKViewport, commandsManager); return withCommandsManager(ExtendedVTKViewport, commandsManager);
}, },
getToolbarModule() { getToolbarModule() {
+2 -3
View File
@@ -34,9 +34,8 @@
"see-changed": "lerna changed", "see-changed": "lerna changed",
"docs:publish": "chmod +x ./build-and-publish-docs.sh && ./build-and-publish-docs.sh", "docs:publish": "chmod +x ./build-and-publish-docs.sh && ./build-and-publish-docs.sh",
"release": "yarn run lerna:version && yarn run lerna:publish", "release": "yarn run lerna:version && yarn run lerna:publish",
"lerna:cache": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-cache.sh || :", "lerna:cache": "./netlify-lerna-cache.sh",
"lerna:restore": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-restore.sh || :", "lerna:restore": "./netlify-lerna-restore.sh",
"preinstall": "yarn lerna:restore",
"lerna:version": "npx lerna version prerelease --force-publish", "lerna:version": "npx lerna version prerelease --force-publish",
"lerna:publish": "lerna publish from-package --canary --dist-tag canary", "lerna:publish": "lerna publish from-package --canary --dist-tag canary",
"link-list": "npm ls --depth=0 --link=true" "link-list": "npm ls --depth=0 --link=true"
+51
View File
@@ -3,6 +3,57 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.10.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.3...@ohif/core@2.10.4) (2020-09-03)
**Note:** Version bump only for package @ohif/core
## [2.10.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.2...@ohif/core@2.10.3) (2020-09-03)
**Note:** Version bump only for package @ohif/core
## [2.10.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.1...@ohif/core@2.10.2) (2020-09-02)
**Note:** Version bump only for package @ohif/core
## [2.10.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.0...@ohif/core@2.10.1) (2020-08-28)
**Note:** Version bump only for package @ohif/core
# [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.14...@ohif/core@2.10.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
## [2.9.14](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.13...@ohif/core@2.9.14) (2020-08-10)
**Note:** Version bump only for package @ohif/core
## [2.9.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.12...@ohif/core@2.9.13) (2020-07-22) ## [2.9.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.12...@ohif/core@2.9.13) (2020-07-22)
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/core", "name": "@ohif/core",
"version": "2.9.13", "version": "2.10.4",
"description": "Generic business logic for web-based medical imaging applications", "description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team", "author": "OHIF Core Team",
"license": "MIT", "license": "MIT",
@@ -32,14 +32,14 @@
}, },
"peerDependencies": { "peerDependencies": {
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3" "dicom-parser": "^1.8.3"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"immer": "6.0.2", "immer": "6.0.2",
"isomorphic-base64": "^1.0.2", "isomorphic-base64": "^1.0.2",
@@ -54,9 +54,6 @@ export class OHIFStudyMetadataSource extends StudyMetadataSource {
// Get Study display sets // Get Study display sets
const displaySets = studyMetadata.createDisplaySets(); const displaySets = studyMetadata.createDisplaySets();
// Set studyMetadata display sets
studyMetadata.setDisplaySets(displaySets);
OHIFStudyMetadataSource._updateStudyCollections(studyMetadata); OHIFStudyMetadataSource._updateStudyCollections(studyMetadata);
resolve(studyMetadata); resolve(studyMetadata);
}) })
@@ -10,8 +10,8 @@ import { api } from 'dicomweb-client';
// - createStacks // - createStacks
import { isImage } from '../../utils/isImage'; import { isImage } from '../../utils/isImage';
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable'; import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
import isLowPriorityModality from '../../utils/isLowPriorityModality';
import errorHandler from '../../errorHandler'; import errorHandler from '../../errorHandler';
import isLowPriorityModality from '../../utils/isLowPriorityModality';
export class StudyMetadata extends Metadata { export class StudyMetadata extends Metadata {
constructor(data, uid) { constructor(data, uid) {
@@ -139,9 +139,11 @@ export class StudyMetadata extends Metadata {
if (displaySet) { if (displaySet) {
displaySet.sopClassModule = true; displaySet.sopClassModule = true;
displaySet.isDerived if (displaySet.isDerived) {
? this._addDerivedDisplaySet(displaySet) this._addDerivedDisplaySet(displaySet);
: displaySets.push(displaySet); }
displaySets.push(displaySet);
return displaySets; return displaySets;
} }
@@ -306,14 +308,10 @@ export class StudyMetadata extends Metadata {
series series
); );
displaySets.push(...displaySetsForSeries); displaySetsForSeries.forEach(ds => this._insertDisplaySet(ds));
}); });
return sortDisplaySetList(displaySets); return this._displaySets;
}
sortDisplaySets() {
sortDisplaySetList(this._displaySets);
} }
/** /**
@@ -344,25 +342,9 @@ export class StudyMetadata extends Metadata {
this.addDisplaySet(displaySet); this.addDisplaySet(displaySet);
}); });
this.sortDisplaySets();
return true; return true;
} }
/**
* Set display sets
* @param {Array} displaySets Array of display sets (ImageSet[])
*/
setDisplaySets(displaySets) {
if (Array.isArray(displaySets) && displaySets.length > 0) {
// TODO: This is weird, can we just switch it to writable: true?
this._displaySets.splice(0);
displaySets.forEach(displaySet => this.addDisplaySet(displaySet));
this.sortDisplaySets();
}
}
/** /**
* Add a single display set to the list * Add a single display set to the list
* @param {Object} displaySet Display set object * @param {Object} displaySet Display set object
@@ -370,7 +352,7 @@ export class StudyMetadata extends Metadata {
*/ */
addDisplaySet(displaySet) { addDisplaySet(displaySet) {
if (displaySet instanceof ImageSet || displaySet.sopClassModule) { if (displaySet instanceof ImageSet || displaySet.sopClassModule) {
this._displaySets.push(displaySet); this._insertDisplaySet(displaySet);
return true; return true;
} }
return false; return false;
@@ -391,6 +373,96 @@ export class StudyMetadata extends Metadata {
} }
} }
/**
* Insert the displaySet so that the list has an increasing SeriesNumber,
* with the most recent series first for displaySets with the same SeriesNumber.
*
* If the displaySet is low priority, the same logic is applied, but is sorted within a sub list
* At the end of the list, where all low priority data is found.
*/
_insertDisplaySet(displaySet) {
const { SeriesNumber } = displaySet;
const displaySets = this._displaySets;
let insertIndex = displaySets.length;
let firstIndexWithSameSeriesNumber;
// If low priority, start search from next low priority.
if (isLowPriorityModality(displaySet.Modality)) {
let startingIndex;
// Find where the first low priority displaySet is.
for (let i = 0; i < displaySets.length; i++) {
if (isLowPriorityModality(displaySets[i].Modality)) {
startingIndex = i;
break;
}
}
if (!startingIndex) {
startingIndex = displaySets.length;
}
// Find the correct SeriesNumber location to insert within the low priority
// Modality displaySets
for (let i = startingIndex; i < displaySets.length; i++) {
if (
displaySets[i].SeriesNumber === SeriesNumber &&
!firstIndexWithSameSeriesNumber
) {
firstIndexWithSameSeriesNumber = i;
}
if (displaySets[i].SeriesNumber > SeriesNumber) {
insertIndex = i;
break;
}
}
} else {
// Find correct SeriesNumber to insert or where the low priority modalities start.
for (let i = 0; i < displaySets.length; i++) {
if (
displaySets[i].SeriesNumber === SeriesNumber &&
!firstIndexWithSameSeriesNumber
) {
firstIndexWithSameSeriesNumber = i;
}
if (
displaySets[i].SeriesNumber > SeriesNumber ||
isLowPriorityModality(displaySets[i].Modality)
) {
insertIndex = i;
break;
}
}
}
// If we have multiple displaySets with the same series number, find the insert position based on
// SeriesDate and SeriesTime.
if (firstIndexWithSameSeriesNumber !== undefined) {
// If no SeriesDate, is just a placeholder displaySet, just insert anywhere, it will be re-added later.
if (displaySet.SeriesDate) {
const seriesDateTime = `${displaySet.SeriesDate}${displaySet.SeriesTime}`;
for (let i = firstIndexWithSameSeriesNumber; i < insertIndex; i++) {
const displaySetI = displaySets[i];
if (
displaySetI.SeriesDate &&
`${displaySetI.SeriesDate}${displaySetI.SeriesTime}` <
seriesDateTime
) {
insertIndex = i;
break;
}
}
}
}
this._displaySets.splice(insertIndex, 0, displaySet);
this.displaySets = this._displaySets;
}
/** /**
* Search the associated display sets using the supplied callback as criteria. The callback is passed * Search the associated display sets using the supplied callback as criteria. The callback is passed
* two arguments: display set (an ImageSet instance) and index (the integer * two arguments: display set (an ImageSet instance) and index (the integer
@@ -546,49 +618,6 @@ export class StudyMetadata extends Metadata {
return this._series.indexOf(series); return this._series.indexOf(series);
} }
/**
* It sorts the series based on display sets order. Each series must be an instance
* of SeriesMetadata and each display sets must be an instance of ImageSet.
* Useful example of usage:
* Study data provided by backend does not sort series at all and client-side
* needs series sorted by the same criteria used for sorting display sets.
*/
sortSeriesByDisplaySets() {
// Object for mapping display sets' index by SeriesInstanceUID
const displaySetsMapping = {};
// Loop through each display set to create the mapping
this.forEachDisplaySet((displaySet, index) => {
if (!(displaySet instanceof ImageSet)) {
throw new OHIFError(
`StudyMetadata::sortSeriesByDisplaySets display set at index ${index} is not an instance of ImageSet`
);
}
// In case of multiframe studies, just get the first index occurence
if (displaySetsMapping[displaySet.SeriesInstanceUID] === void 0) {
displaySetsMapping[displaySet.SeriesInstanceUID] = index;
}
});
// Clone of actual series
const actualSeries = this.getSeries();
actualSeries.forEach((series, index) => {
if (!(series instanceof SeriesMetadata)) {
throw new OHIFError(
`StudyMetadata::sortSeriesByDisplaySets series at index ${index} is not an instance of SeriesMetadata`
);
}
// Get the new series index
const seriesIndex = displaySetsMapping[series.getSeriesInstanceUID()];
// Update the series object with the new series position
this._series[seriesIndex] = series;
});
}
/** /**
* Compares the current study instance with another one. * Compares the current study instance with another one.
* @param {StudyMetadata} study An instance of the StudyMetadata class. * @param {StudyMetadata} study An instance of the StudyMetadata class.
@@ -863,53 +892,3 @@ function _getDisplaySetFromSopClassModule(
} }
return displaySet; return displaySet;
} }
/**
* Sort series primarily by Modality (i.e., series with references to other
* series like SEG, KO or PR are grouped in the end of the list) and then by
* series number:
*
* --------
* | CT #3 |
* | CT #4 |
* | CT #5 |
* --------
* | SEG #1 |
* | SEG #2 |
* --------
*
* @param {*} a - DisplaySet
* @param {*} b - DisplaySet
*/
function seriesSortingCriteria(a, b) {
const isLowPriorityA = isLowPriorityModality(a.Modality);
const isLowPriorityB = isLowPriorityModality(b.Modality);
if (!isLowPriorityA && isLowPriorityB) {
return -1;
}
if (isLowPriorityA && !isLowPriorityB) {
return 1;
}
return sortBySeriesNumber(a, b);
}
/**
* Sort series by series number. Series with low
* @param {*} a - DisplaySet
* @param {*} b - DisplaySet
*/
function sortBySeriesNumber(a, b) {
const seriesNumberAIsGreaterOrUndefined =
a.SeriesNumber > b.SeriesNumber || (!a.SeriesNumber && b.SeriesNumber);
return seriesNumberAIsGreaterOrUndefined ? 1 : -1;
}
/**
* Sorts a list of display set objects
* @param {Array} list A list of display sets to be sorted
*/
function sortDisplaySetList(list) {
return list.sort(seriesSortingCriteria);
}
@@ -5,6 +5,7 @@ export default class ExtensionManager {
constructor({ commandsManager, servicesManager, api, appConfig = {} }) { constructor({ commandsManager, servicesManager, api, appConfig = {} }) {
this.modules = {}; this.modules = {};
this.registeredExtensionIds = []; this.registeredExtensionIds = [];
this.registeredExtensionVesions = {};
this.moduleTypeNames = Object.values(MODULE_TYPES); this.moduleTypeNames = Object.values(MODULE_TYPES);
// //
this._commandsManager = commandsManager; this._commandsManager = commandsManager;
@@ -51,6 +52,7 @@ export default class ExtensionManager {
} }
let extensionId = extension.id; let extensionId = extension.id;
const version = extension.version;
if (!extensionId) { if (!extensionId) {
extensionId = Math.random() extensionId = Math.random()
@@ -98,6 +100,8 @@ export default class ExtensionManager {
// Track extension registration // Track extension registration
this.registeredExtensionIds.push(extensionId); this.registeredExtensionIds.push(extensionId);
this.registeredExtensionVesions[extensionId] = version;
} }
/** /**
@@ -120,7 +124,8 @@ export default class ExtensionManager {
commandsManager: this._commandsManager, commandsManager: this._commandsManager,
appConfig: this._appConfig, appConfig: this._appConfig,
configuration, configuration,
api: this._api api: this._api,
extensionManager: this,
}); });
if (!extensionModule) { if (!extensionModule) {
@@ -184,6 +184,8 @@ describe('ExtensionManager.js', () => {
commandsManager, commandsManager,
appConfig, appConfig,
configuration: extensionConfiguration, configuration: extensionConfiguration,
api: undefined,
extensionManager,
}); });
} }
}); });
@@ -226,7 +228,7 @@ describe('ExtensionManager.js', () => {
return { return {
definitions: { definitions: {
exampleDefinition: { exampleDefinition: {
commandFn: () => { }, commandFn: () => {},
storeContexts: [], storeContexts: [],
options: {}, options: {},
}, },
@@ -11,12 +11,15 @@ import { retrieveStudyMetadata } from './retrieveStudyMetadata';
* @param {Array} studyInstanceUIDs The UIDs of the Studies to be retrieved * @param {Array} studyInstanceUIDs The UIDs of the Studies to be retrieved
* @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process * @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against * @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
* @param {boolean} [separateSeriesInstanceUIDFilters = false] - If true, split filtered metadata calls into multiple calls,
* as some DICOMWeb implementations only support single filters.
* @returns {Promise} that will be resolved with the metadata or rejected with the error * @returns {Promise} that will be resolved with the metadata or rejected with the error
*/ */
export default function retrieveStudiesMetadata( export default function retrieveStudiesMetadata(
server, server,
studyInstanceUIDs, studyInstanceUIDs,
filters filters,
separateSeriesInstanceUIDFilters = false
) { ) {
// Create an empty array to store the Promises for each metaData retrieval call // Create an empty array to store the Promises for each metaData retrieval call
const promises = []; const promises = [];
@@ -24,7 +27,12 @@ export default function retrieveStudiesMetadata(
// Loop through the array of studyInstanceUIDs // Loop through the array of studyInstanceUIDs
studyInstanceUIDs.forEach(function(StudyInstanceUID) { studyInstanceUIDs.forEach(function(StudyInstanceUID) {
// Send the call and resolve or reject the related promise based on its outcome // Send the call and resolve or reject the related promise based on its outcome
const promise = retrieveStudyMetadata(server, StudyInstanceUID, filters); const promise = retrieveStudyMetadata(
server,
StudyInstanceUID,
filters,
separateSeriesInstanceUIDFilters
);
// Add the current promise to the array of promises // Add the current promise to the array of promises
promises.push(promise); promises.push(promise);
@@ -11,9 +11,16 @@ const StudyMetaDataPromises = new Map();
* @param {string} StudyInstanceUID The UID of the Study to be retrieved * @param {string} StudyInstanceUID The UID of the Study to be retrieved
* @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process * @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against * @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
* @param {boolean} [separateSeriesInstanceUIDFilters = false] - If true, split filtered metadata calls into multiple calls,
* as some DICOMWeb implementations only support single filters.
* @returns {Promise} that will be resolved with the metadata or rejected with the error * @returns {Promise} that will be resolved with the metadata or rejected with the error
*/ */
export function retrieveStudyMetadata(server, StudyInstanceUID, filters) { export function retrieveStudyMetadata(
server,
StudyInstanceUID,
filters,
separateSeriesInstanceUIDFilters = false
) {
// @TODO: Whenever a study metadata request has failed, its related promise will be rejected once and for all // @TODO: Whenever a study metadata request has failed, its related promise will be rejected once and for all
// and further requests for that metadata will always fail. On failure, we probably need to remove the // and further requests for that metadata will always fail. On failure, we probably need to remove the
// corresponding promise from the "StudyMetaDataPromises" map... // corresponding promise from the "StudyMetaDataPromises" map...
@@ -33,11 +40,29 @@ export function retrieveStudyMetadata(server, StudyInstanceUID, filters) {
} }
// Create a promise to handle the data retrieval // Create a promise to handle the data retrieval
const promise = new Promise((resolve, reject) => { let promise;
RetrieveMetadata(server, StudyInstanceUID, filters).then(function(data) {
resolve(data); if (
}, reject); filters &&
}); filters.seriesInstanceUID &&
separateSeriesInstanceUIDFilters
) {
promise = __separateSeriesRequestToAggregatePromiseateSeriesRequestToAggregatePromise(
server,
StudyInstanceUID,
filters
);
} else {
promise = RetrieveMetadata(server, StudyInstanceUID, filters);
/*
promise = new Promise((resolve, reject) => {
RetrieveMetadata(server, StudyInstanceUID, filters).then(function(data) {
resolve(data);
}, reject);
});
*/
}
// Store the promise in cache // Store the promise in cache
StudyMetaDataPromises.set(StudyInstanceUID, promise); StudyMetaDataPromises.set(StudyInstanceUID, promise);
@@ -45,6 +70,49 @@ export function retrieveStudyMetadata(server, StudyInstanceUID, filters) {
return promise; return promise;
} }
/**
* Splits up seriesInstanceUID filters to multiple calls for platforms
* @param {Object} server Object with server configuration parameters
* @param {string} StudyInstanceUID The UID of the Study to be retrieved
* @param {Object} filters - Object containing filters to be applied on retrieve metadata process
*/
function __separateSeriesRequestToAggregatePromiseateSeriesRequestToAggregatePromise(
server,
StudyInstanceUID,
filters
) {
const { seriesInstanceUID } = filters;
const seriesInstanceUIDs = seriesInstanceUID.split(',');
return new Promise((resolve, reject) => {
const promises = [];
seriesInstanceUIDs.forEach(uid => {
const seriesSpecificFilters = Object.assign({}, filters, {
seriesInstanceUID: uid,
});
promises.push(
RetrieveMetadata(server, StudyInstanceUID, seriesSpecificFilters)
);
});
Promise.all(promises).then(results => {
const data = results[0];
let series = [];
results.forEach(result => {
series = [...series, ...result.series];
});
data.series = series;
resolve(data);
}, reject);
});
}
/** /**
* Delete the cached study metadata retrieval promise to ensure that the browser will * Delete the cached study metadata retrieval promise to ensure that the browser will
* re-retrieve the study metadata when it is next requested * re-retrieve the study metadata when it is next requested
@@ -1,4 +1,11 @@
const LOW_PRIORITY_MODALITIES = Object.freeze(['SEG', 'KO', 'PR']); const LOW_PRIORITY_MODALITIES = Object.freeze([
'SEG',
'DOC',
'RTSTRUCT',
'SR',
'KO',
'PR',
]);
export default function isLowPriorityModality(Modality) { export default function isLowPriorityModality(Modality) {
return LOW_PRIORITY_MODALITIES.includes(Modality); return LOW_PRIORITY_MODALITIES.includes(Modality);
@@ -9,7 +9,7 @@ services:
max-size: '10m' max-size: '10m'
ports: ports:
- '389:389' - '389:389'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
volumes: volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro - ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro - ./dcm4che/etc/timezone:/etc/timezone:ro
@@ -25,7 +25,7 @@ services:
max-size: '10m' max-size: '10m'
ports: ports:
- '5432:5432' - '5432:5432'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
volumes: volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro - ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro - ./dcm4che/etc/timezone:/etc/timezone:ro
@@ -44,7 +44,7 @@ services:
- '9990:9990' - '9990:9990'
- '11112:11112' - '11112:11112'
- '2575:2575' - '2575:2575'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
environment: environment:
WILDFLY_CHOWN: /opt/wildfly/standalone /storage WILDFLY_CHOWN: /opt/wildfly/standalone /storage
WILDFLY_WAIT_FOR: ldap:389 db:5432 WILDFLY_WAIT_FOR: ldap:389 db:5432
@@ -73,5 +73,4 @@ services:
restart: always restart: always
networks: networks:
- dcm4che_default - dcm4che_default
networks: dcm4che_default:
networks: dcm4che_default:
+140
View File
@@ -3,6 +3,146 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.5.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.7...@ohif/viewer@4.5.8) (2020-09-10)
**Note:** Version bump only for package @ohif/viewer
## [4.5.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.6...@ohif/viewer@4.5.7) (2020-09-09)
**Note:** Version bump only for package @ohif/viewer
## [4.5.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.5...@ohif/viewer@4.5.6) (2020-09-03)
**Note:** Version bump only for package @ohif/viewer
## [4.5.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.4...@ohif/viewer@4.5.5) (2020-09-03)
**Note:** Version bump only for package @ohif/viewer
## [4.5.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.3...@ohif/viewer@4.5.4) (2020-09-02)
**Note:** Version bump only for package @ohif/viewer
## [4.5.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.2...@ohif/viewer@4.5.3) (2020-08-28)
**Note:** Version bump only for package @ohif/viewer
## [4.5.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.1...@ohif/viewer@4.5.2) (2020-08-24)
**Note:** Version bump only for package @ohif/viewer
## [4.5.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.0...@ohif/viewer@4.5.1) (2020-08-20)
**Note:** Version bump only for package @ohif/viewer
# [4.5.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.4.1...@ohif/viewer@4.5.0) (2020-08-20)
### Features
* 🎸 Filter/promote multiple series instances ([#1533](https://github.com/OHIF/Viewers/issues/1533)) ([5fdace1](https://github.com/OHIF/Viewers/commit/5fdace1432b4b1bd31a60c5db6bd681610ec2b8e))
## [4.4.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.4.0...@ohif/viewer@4.4.1) (2020-08-20)
### Bug Fixes
* Fix incorrect command name in Percy test ([#1999](https://github.com/OHIF/Viewers/issues/1999)) ([ebdcde1](https://github.com/OHIF/Viewers/commit/ebdcde1c4d9c95393cf79cc9994f5d60f6d66fdd))
# [4.4.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.3.1...@ohif/viewer@4.4.0) (2020-08-18)
### Features
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
## [4.3.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.3.0...@ohif/viewer@4.3.1) (2020-08-10)
**Note:** Version bump only for package @ohif/viewer
# [4.3.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.16...@ohif/viewer@4.3.0) (2020-08-10)
### Features
* 🎸 Optional disable measurements panel in app config ([#1912](https://github.com/OHIF/Viewers/issues/1912)) ([3d86b5f](https://github.com/OHIF/Viewers/commit/3d86b5f876d6481bcc57de88a0ba1f974f4ee263)), closes [#1864](https://github.com/OHIF/Viewers/issues/1864)
## [4.2.16](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.15...@ohif/viewer@4.2.16) (2020-08-10)
**Note:** Version bump only for package @ohif/viewer
## [4.2.15](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.14...@ohif/viewer@4.2.15) (2020-08-05)
**Note:** Version bump only for package @ohif/viewer
## [4.2.14](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.13...@ohif/viewer@4.2.14) (2020-08-05)
**Note:** Version bump only for package @ohif/viewer
## [4.2.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.12...@ohif/viewer@4.2.13) (2020-07-23) ## [4.2.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.12...@ohif/viewer@4.2.13) (2020-07-23)
**Note:** Version bump only for package @ohif/viewer **Note:** Version bump only for package @ohif/viewer
@@ -12,6 +12,8 @@ describe('OHIF Microscopy Extension', () => {
}); });
it('drags and drop a SM thumbnail into viewport', () => { it('drags and drop a SM thumbnail into viewport', () => {
// Waiting for series list to load all displaySets (lots of SRs, before defining which dom element to grab.)
cy.wait(3000);
cy.get('[data-cy="thumbnail-list"]') cy.get('[data-cy="thumbnail-list"]')
.contains('SM') .contains('SM')
.drag('.viewport-drop-target'); .drag('.viewport-drop-target');
@@ -19,7 +19,7 @@ describe('Visual Regression - OHIF VTK Extension', () => {
cy.get('[data-cy="2d mpr"]').click(); cy.get('[data-cy="2d mpr"]').click();
//Wait waitVTKLoading Images //Wait waitVTKLoading Images
cy.waitVTKwaitVTKLoading(); cy.waitVTKLoading();
}); });
beforeEach(() => { beforeEach(() => {
+1 -1
View File
@@ -114,7 +114,7 @@ Cypress.Commands.add('waitVTKLoading', () => {
}); });
// Wait for finish loading // Wait for finish loading
cy.get('[data-cy="viewprt-grid"]', { timeout: 30000 }).should($grid => { cy.get('[data-cy="viewprt-grid"]', { timeout: 90000 }).should($grid => {
expect($grid).not.to.contain.text('Loading'); expect($grid).not.to.contain.text('Loading');
}); });
}); });
+13 -13
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/viewer", "name": "@ohif/viewer",
"version": "4.2.13", "version": "4.5.8",
"description": "OHIF Viewer", "description": "OHIF Viewer",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -48,16 +48,16 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@ohif/core": "^2.9.13", "@ohif/core": "^2.10.4",
"@ohif/extension-cornerstone": "^2.8.4", "@ohif/extension-cornerstone": "^2.9.4",
"@ohif/extension-dicom-html": "^1.2.4", "@ohif/extension-debugging": "^0.1.4",
"@ohif/extension-dicom-microscopy": "^0.51.0", "@ohif/extension-dicom-html": "^1.2.7",
"@ohif/extension-dicom-p10-downloader": "^0.1.2", "@ohif/extension-dicom-microscopy": "^0.51.2",
"@ohif/extension-dicom-pdf": "^1.0.3", "@ohif/extension-dicom-pdf": "^1.0.5",
"@ohif/extension-dicom-rt": "^0.2.5", "@ohif/extension-dicom-rt": "^0.4.4",
"@ohif/extension-dicom-segmentation": "^0.3.3", "@ohif/extension-dicom-segmentation": "^0.4.4",
"@ohif/extension-lesion-tracker": "^0.2.0", "@ohif/extension-lesion-tracker": "^0.2.1",
"@ohif/extension-vtk": "^1.6.9", "@ohif/extension-vtk": "^1.7.5",
"@ohif/i18n": "^0.52.8", "@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.5.3", "@ohif/ui": "^1.5.3",
"@tanem/react-nprogress": "^1.1.25", "@tanem/react-nprogress": "^1.1.25",
@@ -65,9 +65,9 @@
"core-js": "^3.2.1", "core-js": "^3.2.1",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.3",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4", "dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
+4
View File
@@ -2,13 +2,17 @@ window.config = function(props) {
var servicesManager = props.servicesManager; var servicesManager = props.servicesManager;
return { return {
disableMeasurementPanel: true,
routerBasename: '/', routerBasename: '/',
enableGoogleCloudAdapter: true, enableGoogleCloudAdapter: true,
splitQueryParameterCalls: true, // Allows the user to split QIDO SeriesInstanceUID filters into multiple calls, if the server does not support multi-valued query parameters.
enableGoogleCloudAdapterUI: false, enableGoogleCloudAdapterUI: false,
showStudyList: true, showStudyList: true,
filterQueryParam: true,
httpErrorHandler: error => { httpErrorHandler: error => {
// This is 429 when rejected from the public idc sandbox too often. // This is 429 when rejected from the public idc sandbox too often.
console.warn(error.status); console.warn(error.status);
// Could use services manager here to bring up a dialog/modal if needed. // Could use services manager here to bring up a dialog/modal if needed.
console.warn('test, navigate to https://ohif.org/'); console.warn('test, navigate to https://ohif.org/');
window.location = 'https://ohif.org/'; window.location = 'https://ohif.org/';
+12 -8
View File
@@ -13,7 +13,7 @@ import {
ModalProvider, ModalProvider,
DialogProvider, DialogProvider,
OHIFModal, OHIFModal,
ErrorBoundary ErrorBoundary,
} from '@ohif/ui'; } from '@ohif/ui';
import { import {
@@ -168,7 +168,7 @@ class App extends Component {
if (this._userManager) { if (this._userManager) {
return ( return (
<ErrorBoundary context='App'> <ErrorBoundary context="App">
<Provider store={store}> <Provider store={store}>
<AppProvider config={this._appConfig}> <AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}> <I18nextProvider i18n={i18n}>
@@ -200,7 +200,7 @@ class App extends Component {
} }
return ( return (
<ErrorBoundary context='App'> <ErrorBoundary context="App">
<Provider store={store}> <Provider store={store}>
<AppProvider config={this._appConfig}> <AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}> <I18nextProvider i18n={i18n}>
@@ -271,17 +271,21 @@ function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
api: { api: {
contexts: CONTEXTS, contexts: CONTEXTS,
hooks: { hooks: {
useAppContext useAppContext,
} },
} },
}); });
const requiredExtensions = [ const requiredExtensions = [
GenericViewerCommands, GenericViewerCommands,
[OHIFCornerstoneExtension, cornerstoneExtensionConfig], [OHIFCornerstoneExtension, cornerstoneExtensionConfig],
/* WARNING: MUST BE REGISTERED _AFTER_ OHIFCornerstoneExtension */
MeasurementsPanel,
]; ];
if (appConfig.disableMeasurementPanel !== true) {
/* WARNING: MUST BE REGISTERED _AFTER_ OHIFCornerstoneExtension */
requiredExtensions.push(MeasurementsPanel);
}
const mergedExtensions = requiredExtensions.concat(extensions); const mergedExtensions = requiredExtensions.concat(extensions);
extensionManager.registerExtensions(mergedExtensions); extensionManager.registerExtensions(mergedExtensions);
} }
@@ -2,6 +2,9 @@ import commandsModule from './commandsModule.js';
export default { export default {
id: 'generic-viewer-commands', id: 'generic-viewer-commands',
get version() {
return window.version;
},
getCommandsModule({ commandsManager }) { getCommandsModule({ commandsManager }) {
return commandsModule({ commandsManager }); return commandsModule({ commandsManager });
}, },
@@ -9,6 +9,9 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'measurements-table', id: 'measurements-table',
get version() {
return window.version;
},
preRegistration({ servicesManager, commandsManager, configuration = {} }) { preRegistration({ servicesManager, commandsManager, configuration = {} }) {
init({ servicesManager, commandsManager, configuration }); init({ servicesManager, commandsManager, configuration });
@@ -80,6 +80,7 @@ const ViewportGrid = function(props) {
// - When updating a panel, ensure that the currently enabled plugin // - When updating a panel, ensure that the currently enabled plugin
// in the viewport is capable of rendering this display set. If not // in the viewport is capable of rendering this display set. If not
// then use the most capable available plugin // then use the most capable available plugin
const pluginName = const pluginName =
!layout.plugin && displaySet && displaySet.plugin !layout.plugin && displaySet && displaySet.plugin
? displaySet.plugin ? displaySet.plugin
@@ -4,6 +4,8 @@ import { StudyBrowser } from '@ohif/ui';
import cloneDeep from 'lodash.clonedeep'; import cloneDeep from 'lodash.clonedeep';
import findDisplaySetByUID from './findDisplaySetByUID'; import findDisplaySetByUID from './findDisplaySetByUID';
const { studyMetadataManager } = OHIF.utils;
const { setActiveViewportSpecificData } = OHIF.redux.actions; const { setActiveViewportSpecificData } = OHIF.redux.actions;
// TODO // TODO
@@ -39,11 +41,27 @@ const mapStateToProps = (state, ownProps) => {
const mapDispatchToProps = (dispatch, ownProps) => { const mapDispatchToProps = (dispatch, ownProps) => {
return { return {
onThumbnailClick: displaySetInstanceUID => { onThumbnailClick: displaySetInstanceUID => {
const displaySet = findDisplaySetByUID( let displaySet = findDisplaySetByUID(
ownProps.studyMetadata, ownProps.studyMetadata,
displaySetInstanceUID displaySetInstanceUID
); );
if (displaySet.isDerived) {
const { Modality } = displaySet;
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata);
if (!displaySet) {
throw new Error(
`Referenced series for ${Modality} dataset not present.`
);
}
if (!displaySet) {
throw new Error('Source data not present');
}
}
dispatch(setActiveViewportSpecificData(displaySet)); dispatch(setActiveViewportSpecificData(displaySet));
}, },
}; };
@@ -19,6 +19,7 @@ import UserManagerContext from '../context/UserManagerContext';
import AppContext from '../context/AppContext'; import AppContext from '../context/AppContext';
import './Viewer.css'; import './Viewer.css';
import { finished } from 'stream';
class Viewer extends Component { class Viewer extends Component {
static propTypes = { static propTypes = {
@@ -189,6 +190,7 @@ class Viewer extends Component {
currentTimepointId, currentTimepointId,
]); ]);
} }
this.setState({ this.setState({
thumbnails: _mapStudiesToThumbnails(studies), thumbnails: _mapStudiesToThumbnails(studies),
}); });
@@ -197,6 +199,7 @@ class Viewer extends Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { studies, isStudyLoaded } = this.props; const { studies, isStudyLoaded } = this.props;
if (studies !== prevProps.studies) { if (studies !== prevProps.studies) {
this.setState({ this.setState({
thumbnails: _mapStudiesToThumbnails(studies), thumbnails: _mapStudiesToThumbnails(studies),
@@ -355,7 +358,6 @@ export default withDialog(Viewer);
* a mapping layer? * a mapping layer?
* *
* TODO[react]: * TODO[react]:
* - Add sorting of display sets
* - Add showStackLoadingProgressBar option * - Add showStackLoadingProgressBar option
* *
* @param {Study[]} studies * @param {Study[]} studies
@@ -369,9 +371,9 @@ const _mapStudiesToThumbnails = function(studies) {
const { const {
displaySetInstanceUID, displaySetInstanceUID,
SeriesDescription, SeriesDescription,
SeriesNumber,
InstanceNumber, InstanceNumber,
numImageFrames, numImageFrames,
SeriesNumber,
} = displaySet; } = displaySet;
let imageId; let imageId;
@@ -395,9 +397,9 @@ const _mapStudiesToThumbnails = function(studies) {
altImageText, altImageText,
displaySetInstanceUID, displaySetInstanceUID,
SeriesDescription, SeriesDescription,
SeriesNumber,
InstanceNumber, InstanceNumber,
numImageFrames, numImageFrames,
SeriesNumber,
}; };
}); });
@@ -77,7 +77,6 @@ class ViewerLocalFileData extends Component {
study.displaySets = study.displaySets =
study.displaySets || study.displaySets ||
studyMetadata.createDisplaySets(sopClassHandlerModules); studyMetadata.createDisplaySets(sopClassHandlerModules);
studyMetadata.setDisplaySets(study.displaySets);
studyMetadata.forEachDisplaySet(displayset => { studyMetadata.forEachDisplaySet(displayset => {
displayset.localFile = true; displayset.localFile = true;
@@ -133,12 +133,23 @@ class ViewerMain extends Component {
StudyInstanceUID, StudyInstanceUID,
displaySetInstanceUID, displaySetInstanceUID,
}) => { }) => {
const displaySet = this.findDisplaySet( let displaySet = this.findDisplaySet(
this.props.studies, this.props.studies,
StudyInstanceUID, StudyInstanceUID,
displaySetInstanceUID displaySetInstanceUID
); );
if (displaySet.isDerived) {
const { Modality } = displaySet;
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
if (!displaySet) {
throw new Error(
`Referenced series for ${Modality} dataset not present.`
);
}
}
this.props.setViewportSpecificData(viewportIndex, displaySet); this.props.setViewportSpecificData(viewportIndex, displaySet);
}; };
@@ -14,23 +14,25 @@ const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata;
const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies; const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies;
const { studyMetadataManager, makeCancelable } = utils; const { studyMetadataManager, makeCancelable } = utils;
const _promoteToFront = (list, value, searchMethod) => { const _promoteToFront = (list, values, searchMethod) => {
let response = [...list]; let listCopy = [...list];
let promoted = false; let response = [];
const index = response.findIndex(searchMethod.bind(undefined, value)); let promotedCount = 0;
if (index > 0) { const arrayValues = values.split(',');
const first = response.splice(index, 1); arrayValues.forEach(value => {
response = [...first, ...response]; const index = listCopy.findIndex(searchMethod.bind(undefined, value));
}
if (index >= 0) { if (index >= 0) {
promoted = true; const [itemToPromote] = listCopy.splice(index, 1);
} response[promotedCount] = itemToPromote;
promotedCount++;
}
});
return { return {
promoted, promoted: promotedCount === arrayValues.length,
data: response, data: [...response, ...listCopy],
}; };
}; };
@@ -45,7 +47,6 @@ const _promoteList = (study, studyMetadata, filters, isFilterStrategy) => {
let promoted = false; let promoted = false;
// Promote only if no filter should be applied // Promote only if no filter should be applied
if (!isFilterStrategy) { if (!isFilterStrategy) {
_sortStudyDisplaySet(study, studyMetadata);
promoted = _promoteStudyDisplaySet(study, studyMetadata, filters); promoted = _promoteStudyDisplaySet(study, studyMetadata, filters);
} }
@@ -87,15 +88,52 @@ const _isQueryParamApplied = (study, filters = {}, isFilterStrategy) => {
const { seriesInstanceUID } = filters; const { seriesInstanceUID } = filters;
let applied = true; let applied = true;
// skip in case no filter or no toast manager // skip in case no filter or no toast manager
if (!seriesInstanceUID) { if (!seriesInstanceUID) {
return applied; return applied;
} }
const seriesInstanceUIDs = seriesInstanceUID.split(',');
let validateFilterApplied = () => {
const sameSize = arrayToInspect.length === seriesInstanceUIDs.length;
if (!sameSize) {
return;
}
return arrayToInspect.every(item =>
seriesInstanceUIDs.some(
seriesInstanceUIDStr => seriesInstanceUIDStr === item.SeriesInstanceUID
)
);
};
let validatePromoteApplied = () => {
let isValid = true;
for (let index = 0; index < seriesInstanceUIDs.length; index++) {
const seriesInstanceUIDStr = seriesInstanceUIDs[index];
const resultSeries = arrayToInspect[index];
if (
!resultSeries ||
resultSeries.SeriesInstanceUID !== seriesInstanceUIDStr
) {
isValid = false;
break;
}
}
return isValid;
};
const { series = [], displaySets = [] } = study; const { series = [], displaySets = [] } = study;
const firstSeries = isFilterStrategy ? series[0] : displaySets[0]; const arrayToInspect = isFilterStrategy ? series : displaySets;
const validateMethod = isFilterStrategy
? validateFilterApplied
: validatePromoteApplied;
if (!firstSeries || firstSeries.SeriesInstanceUID !== seriesInstanceUID) { if (!arrayToInspect) {
applied = false; applied = false;
} else {
applied = validateMethod();
} }
return applied; return applied;
@@ -155,12 +193,6 @@ const _updateStudyDisplaySets = (study, studyMetadata) => {
if (study.derivedDisplaySets) { if (study.derivedDisplaySets) {
studyMetadata._addDerivedDisplaySets(study.derivedDisplaySets); studyMetadata._addDerivedDisplaySets(study.derivedDisplaySets);
} }
studyMetadata.setDisplaySets(study.displaySets);
};
const _sortStudyDisplaySet = (study, studyMetadata) => {
studyMetadata.sortDisplaySets(study.displaySets);
}; };
const _thinStudyData = study => { const _thinStudyData = study => {
@@ -221,7 +253,7 @@ function ViewerRetrieveStudyData({
// Show message in case not promoted neither filtered but should to // Show message in case not promoted neither filtered but should to
_showUserMessage( _showUserMessage(
isQueryParamApplied, isQueryParamApplied,
'Query parameters were not applied. Using original series list for given study.', 'Query parameters were not totally applied. It might be using original series list for given study.',
snackbarContext snackbarContext
); );
@@ -233,7 +265,7 @@ function ViewerRetrieveStudyData({
* Method to process studies. It will update displaySet, studyMetadata, load remaining series, ... * Method to process studies. It will update displaySet, studyMetadata, load remaining series, ...
* @param {Array} studiesData Array of studies retrieved from server * @param {Array} studiesData Array of studies retrieved from server
* @param {Object} [filters] - Object containing filters to be applied * @param {Object} [filters] - Object containing filters to be applied
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against * @param {string} [filters.seriesInstanceUID] - series instance uid to filter results against
*/ */
const processStudies = (studiesData, filters) => { const processStudies = (studiesData, filters) => {
if (Array.isArray(studiesData) && studiesData.length > 0) { if (Array.isArray(studiesData) && studiesData.length > 0) {
@@ -299,7 +331,6 @@ function ViewerRetrieveStudyData({
const filters = {}; const filters = {};
// Use the first, discard others // Use the first, discard others
const seriesInstanceUID = seriesInstanceUIDs && seriesInstanceUIDs[0]; const seriesInstanceUID = seriesInstanceUIDs && seriesInstanceUIDs[0];
const retrieveParams = [server, studyInstanceUIDs]; const retrieveParams = [server, studyInstanceUIDs];
if (seriesInstanceUID) { if (seriesInstanceUID) {
@@ -310,6 +341,13 @@ function ViewerRetrieveStudyData({
} }
} }
if (
appConfig.splitQueryParameterCalls ||
appConfig.enableGoogleCloudAdapter
) {
retrieveParams.push(true); // Seperate SeriesInstanceUID filter calls.
}
cancelableStudiesPromises[studyInstanceUIDs] = makeCancelable( cancelableStudiesPromises[studyInstanceUIDs] = makeCancelable(
retrieveStudiesMetadata(...retrieveParams) retrieveStudiesMetadata(...retrieveParams)
) )
+6 -2
View File
@@ -29,7 +29,9 @@ import OHIFDicomSegmentationExtension from '@ohif/extension-dicom-segmentation';
import OHIFDicomRtExtension from '@ohif/extension-dicom-rt'; import OHIFDicomRtExtension from '@ohif/extension-dicom-rt';
import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy'; import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy';
import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf'; import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf';
import OHIFDicomP10DownloaderExtension from '@ohif/extension-dicom-p10-downloader'; // Add this for Debugging purposes:
//import OHIFDebuggingExtension from '@ohif/extension-debugging';
import { version } from '../package.json';
/* /*
* Default Settings * Default Settings
@@ -38,6 +40,8 @@ let config = {};
if (window) { if (window) {
config = window.config || {}; config = window.config || {};
window.version = version;
} }
const appProps = { const appProps = {
@@ -49,7 +53,7 @@ const appProps = {
OHIFDicomPDFExtension, OHIFDicomPDFExtension,
OHIFDicomSegmentationExtension, OHIFDicomSegmentationExtension,
OHIFDicomRtExtension, OHIFDicomRtExtension,
OHIFDicomP10DownloaderExtension, //OHIFDebuggingExtension,
], ],
}; };
@@ -194,7 +194,6 @@ const _mapStudiesToNewFormat = studies => {
study.displaySets = study.displaySets =
study.displaySets || study.displaySets ||
studyMetadata.createDisplaySets(sopClassHandlerModules); studyMetadata.createDisplaySets(sopClassHandlerModules);
studyMetadata.setDisplaySets(study.displaySets);
studyMetadataManager.add(studyMetadata); studyMetadataManager.add(studyMetadata);
uniqueStudyUIDs.add(study.StudyInstanceUID); uniqueStudyUIDs.add(study.StudyInstanceUID);
+23 -43
View File
@@ -1247,34 +1247,13 @@
pirates "^4.0.0" pirates "^4.0.0"
source-map-support "^0.5.9" source-map-support "^0.5.9"
"@babel/runtime@7.1.2": "@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
dependencies:
regenerator-runtime "^0.12.0"
"@babel/runtime@7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
version "7.5.5" version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
dependencies: dependencies:
regenerator-runtime "^0.13.2" regenerator-runtime "^0.13.2"
"@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": "@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
version "7.6.0" version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
@@ -5971,10 +5950,10 @@ cornerstone-math@^0.1.8:
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5" resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw== integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
cornerstone-tools@4.15.1: cornerstone-tools@^4.20.1:
version "4.15.1" version "4.20.1"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.15.1.tgz#f0b1026da9c7758defc088bb2f1e78426a23d91e" resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.20.1.tgz#90f6c28356622eac037683b19f9175f85d33dff7"
integrity sha512-fJuTUJW/NDSD520jPB++tX//Kq80jPDngChHZrhx2xjj/9dCnexD9kmTX30Z0WUq0a2JEHZ6FlcOX38kSmw1hQ== integrity sha512-SyQsrg3bnGHUvWGZpUq7ia7/gj12S9zQFK2VMhpyAYxK9pWz+PRv8HBi0eEe/fWGAo/Vc62IRkdP3uF9MJlqiQ==
dependencies: dependencies:
"@babel/runtime" "7.1.2" "@babel/runtime" "7.1.2"
cornerstone-math "0.1.7" cornerstone-math "0.1.7"
@@ -6560,10 +6539,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@0.16.0: dcmjs@0.16.3:
version "0.16.0" version "0.16.3"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.0.tgz#714755d006c3a49cf65ef13e627ff7a23151d35a" resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.3.tgz#ef44ccc4988a473fd40e1601122c57f2babded11"
integrity sha512-41nY3Isg5i5FoSd1s67aUgUHi146r+fb8JuiRvDsxvm7MEkkdjIzMYMtxjsrVwKRW1xRH+x34nxbV9+8cc7WQw== integrity sha512-pMQGifMpMW9i9ufCdUoEPHrLWnm6ilnpfLqUc+PPoWr7OnuBhiRVRunbv39oTtFDZOLhQ2Ampm7UrkidWAbqiA==
dependencies: dependencies:
"@babel/polyfill" "^7.8.3" "@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4" "@babel/runtime" "^7.8.4"
@@ -6803,6 +6782,11 @@ detab@2.0.2, detab@^2.0.0:
dependencies: dependencies:
repeat-string "^1.5.4" repeat-string "^1.5.4"
detect-browser@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.1.1.tgz#a800db91d3fd60d0861669f5984f1be9ffbe009c"
integrity sha512-5n2aWI57qC3kZaK4j2zYsG6L1LrxgLptGCNhMQgdKhVn6cSdcq43pp6xHPfTHG3TYM6myF4tIPWiZtfdVDgb9w==
detect-file@^1.0.0: detect-file@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
@@ -15617,13 +15601,14 @@ react-codemirror2@^6.0.0:
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525" resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525"
integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag== integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag==
react-cornerstone-viewport@2.3.9: react-cornerstone-viewport@4.0.2:
version "2.3.9" version "4.0.2"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.9.tgz#f9761da8e536f0a217137c6ca1a983f5882249f9" resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-4.0.2.tgz#49bc8f8464164d8e779138dd9244d5c2524e0418"
integrity sha512-qrhq8CbX/jq6b93cQjV2qC/mhHOvFBpxzxcHlvHzEQZt/rmRMcaYxCqjpNaNbUmmBu61wNkxesUVsggkPTTcqg== integrity sha512-mF/piahoPG5U5TbxVMtc15bXrQZxcaPycbu4i4KD2rZkiWh1Zs24ryY1S+lFlDB/RcOAm0u+luEEt1tOg8F+UQ==
dependencies: dependencies:
classnames "^2.2.6" classnames "^2.2.6"
date-fns "^2.2.1" date-fns "^2.2.1"
lodash.debounce "^4.0.8"
prop-types "^15.7.2" prop-types "^15.7.2"
react-resize-detector "^4.2.1" react-resize-detector "^4.2.1"
@@ -16067,10 +16052,10 @@ react-transition-group@^4.1.1:
loose-envify "^1.4.0" loose-envify "^1.4.0"
prop-types "^15.6.2" prop-types "^15.6.2"
react-vtkjs-viewport@^0.9.0: react-vtkjs-viewport@^0.10.4:
version "0.9.0" version "0.10.4"
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.9.0.tgz#5a7890643e511946f960db6b05142318be2dfe80" resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.10.4.tgz#eb038b0bf5c384bd3b45f2a0a57f52c682607fea"
integrity sha512-kjpNr1n+eW0nU736HH07IsSYQXphxfn/RBvKtpBer9tNsl+FyLH8h+uY8KWdv8kVBHkEoaH6/srxB0JBa8l+jA== integrity sha512-J1WxwHbjlttEWvdkM33DJx8QxHr3J7HR6wFSDVBDKY7McK2lwDAdRN8wSqy2BVNILlyurUUVwg2tK2txiJ+tuw==
dependencies: dependencies:
date-fns "^2.2.1" date-fns "^2.2.1"
gl-matrix "^3.1.0" gl-matrix "^3.1.0"
@@ -16433,11 +16418,6 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
version "0.13.5" version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"