Compare commits

...
Author SHA1 Message Date
ohif-bot fab122dd8f chore(release): publish [skip ci]
- @ohif/extension-debugging@0.1.5
 - @ohif/extension-vtk@1.7.7
 - @ohif/ui@1.5.5
 - @ohif/viewer@4.5.10
2020-09-17 18:31:52 +00:00
James Petts 42803b0ae8 [IDC-2006] - optional mailTo for debugging extension. (#2027)
* WIP debug dialog

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

* Fix unit tests

* WIP

* WIP

* Finish mailTo
2020-09-17 20:28:08 +02:00
ohif-bot 5a78da8ff6 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.2.8
 - @ohif/extension-dicom-microscopy@0.51.3
 - @ohif/extension-dicom-pdf@1.0.6
 - @ohif/extension-dicom-rt@0.4.5
 - @ohif/extension-dicom-segmentation@0.4.5
 - @ohif/extension-dicom-tag-browser@0.0.2
 - @ohif/extension-vtk@1.7.6
 - @ohif/core@2.10.5
 - @ohif/ui@1.5.4
 - @ohif/viewer@4.5.9
2020-09-10 10:17:12 +00:00
James Petts 0ef88e88eb [IDC-1129] - DICOM Tag Viewer extension (#2022)
* WIP render top level tags.

* Add drop down to select series.

* Fix errors with type 2 sequences.

* WIP swap instance.

* Fix formatting and make fullscreen.

* Remove debuggers.

* Finish formatting.

* Fix error with double SeriesNumber deconstruction.

* Address reviewer comments.
2020-09-10 12:13:24 +02:00
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
64 changed files with 1350 additions and 67 deletions

No files matched your search

+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.4...@ohif/extension-debugging@0.1.5) (2020-09-17)
**Note:** Version bump only for package @ohif/extension-debugging
## [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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-debugging",
"version": "0.1.3",
"version": "0.1.5",
"description": "OHIF extension for debugging.",
"author": "OHIF",
"license": "MIT",
@@ -2,3 +2,8 @@
padding: 10px 0 10px;
color: var(--active-color);
}
.debug-report-modal-container {
display: flex;
flex-direction: column;
}
+95 -11
View File
@@ -1,22 +1,108 @@
import React from 'react';
import { detect } from 'detect-browser';
import './DebugReportModal.css';
import { ToolbarButton } from '@ohif/ui';
const DubugReportModal = ({
viewports,
studies,
servers,
extensionManager,
mailTo,
}) => {
const mailToFunction = () => {
const StudyInstanceUID = Object.keys(studies.studyData)[0];
const subject = encodeURI(`Issue with Study: ${StudyInstanceUID}`);
let body = `Enter the description of your problem here: \n\n\n`;
body += `============= SESSION INFO =============\n\n`;
// App version
body += '== App ==\n';
body += `version\t${window.version}\n\n`;
// Extensions Versions
body += '== Extensions Versions ==\n';
const { registeredExtensionVesions } = extensionManager;
Object.keys(registeredExtensionVesions).forEach(extensionId => {
const version = registeredExtensionVesions[extensionId];
body += `${extensionId}\t${version}\n`;
});
body += '\n';
// Browser Info
const browser = detect();
const { name, os, type, version } = browser;
body += '== Browser Info ==\n';
body += `name\t ${name}\n`;
body += `os\t ${os}\n`;
body += `type\t ${type}\n`;
body += `version\t ${version}\n\n`;
// Study URL
body += '== URL ==\n';
body += `URL\t ${window.location.href}\n\n`;
// Layout
const { numRows, numColumns, viewportSpecificData } = viewports;
body += '== Viewport Layout ==\n';
body += `Rows\t${numRows}\n`;
body += `Columns\t${numColumns}\n\n`;
body += '== Viewports ==\n';
Object.keys(viewportSpecificData).forEach(viewportIndex => {
const vsd = viewportSpecificData[viewportIndex];
const [row, column] = _viewportIndexToViewportPosition(
viewportIndex,
numColumns
);
body += `[${row},${column}]\t${vsd.SeriesInstanceUID}\n`;
});
// TODO Text dump of rest of stuff.
body = encodeURI(body);
window.location.href = `mailto:${mailTo}?subject=${subject}&body=${body}`;
};
return (
<div>
<table>
{getAppVersion()}
{getExtensionVersions(extensionManager)}
{getBrowserInfo()}
{getCurrentStudyUrl()}
{getLayout(viewports)}
</table>
<div className="debug-report-modal-container">
{mailTo ? (
<div>
<ToolbarButton
label={'Send Bug Report'}
onClick={mailToFunction}
icon={'envelope-square'}
isActive={false}
/>
</div>
) : null}
<div>
<table>
{getAppVersion()}
{getExtensionVersions(extensionManager)}
{getBrowserInfo()}
{getCurrentStudyUrl()}
{getLayout(viewports)}
</table>
</div>
</div>
);
};
@@ -39,7 +125,7 @@ const getCurrentStudyUrl = () => {
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">App</th>
<th className="debugReportModalHeader">URL</th>
</tr>
<tr>
<td>URL</td>
@@ -131,8 +217,6 @@ const getBrowserInfo = () => {
const getSeriesInstanceUIDsPerRow = viewports => {
const { viewportSpecificData, numColumns } = viewports;
debugger;
// NOTE viewportSpecificData is actually an object with numerical keys.
return Object.keys(viewportSpecificData).map(viewportIndex => {
const vsd = viewportSpecificData[viewportIndex];
@@ -11,6 +11,8 @@ import _downloadAndZip, { downloadInstances } from './downloadAndZip';
import DebugReportModal from './DebugReportModal';
import React from 'react';
import state from './state';
const {
utils: { Queue },
} = OHIF;
@@ -99,6 +101,7 @@ export function getCommands(context, servicesManager, extensionManager) {
studies={studies}
servers={servers}
extensionManager={extensionManager}
mailTo={state.mailTo}
/>
);
};
+6 -1
View File
@@ -2,6 +2,7 @@ import { getDicomWebClientFromConfig } from './utils';
import { getCommands } from './commandsModule';
import { version } from '../package.json';
import toolbarModule from './toolbarModule';
import state from './state';
/**
* Constants
@@ -29,11 +30,15 @@ export default {
* LIFECYCLE HOOKS
*/
preRegistration({ appConfig }) {
preRegistration({ appConfig, configuration }) {
const dicomWebClient = getDicomWebClientFromConfig(appConfig);
if (dicomWebClient) {
sharedContext.dicomWebClient = dicomWebClient;
}
if (configuration && configuration.mailTo) {
state.mailTo = configuration.mailTo;
}
},
/**
+3
View File
@@ -0,0 +1,3 @@
const state = { mailTo: undefined };
export default state;
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.7...@ohif/extension-dicom-html@1.2.8) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.2.7",
"version": "1.2.8",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -22,11 +22,13 @@ const OHIFDicomHtmlSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance();
const metadata = instance.getData().metadata;
const {
SeriesDescription,
SeriesNumber,
SeriesDate,
SeriesTime,
SeriesNumber,
} = instance._instance.metadata;
} = metadata;
return {
plugin: 'html',
@@ -37,6 +39,8 @@ const OHIFDicomHtmlSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDescription,
metadata,
SeriesDate,
SeriesTime,
SeriesNumber,
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.51.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.2...@ohif/extension-dicom-microscopy@0.51.3) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
## [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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-microscopy",
"version": "0.51.2",
"version": "0.51.3",
"description": "OHIF extension for Dicom Microscopy",
"author": "OHIF",
"license": "MIT",
@@ -12,11 +12,13 @@ const DicomMicroscopySopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient) {
const instance = series.getFirstInstance();
const metadata = instance.getData().metadata;
const {
SeriesDescription,
SeriesNumber,
ContentDate,
ContentTime,
SeriesNumber,
} = instance._instance.metadata;
} = metadata;
// Note: We are passing the dicomweb client into each viewport!
@@ -28,9 +30,11 @@ const DicomMicroscopySopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDescription,
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
SeriesTime: ContentTime,
SeriesNumber,
metadata,
};
},
};
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.5...@ohif/extension-dicom-pdf@1.0.6) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-pdf
## [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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-pdf",
"version": "1.0.5",
"version": "1.0.6",
"description": "OHIF extension for Dicom PDF",
"author": "OHIF",
"license": "MIT",
@@ -12,11 +12,13 @@ const OHIFDicomPDFSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance();
const metadata = instance.getData().metadata;
const {
ContentDate,
ContentTime,
SeriesDescription,
SeriesNumber,
} = instance._instance.metadata;
} = metadata;
return {
plugin: 'pdf',
@@ -27,9 +29,11 @@ const OHIFDicomPDFSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
SeriesDescription,
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
SeriesTime: ContentTime,
SeriesNumber,
metadata,
authorizationHeaders: authorizationHeaders,
};
},
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.4...@ohif/extension-dicom-rt@0.4.5) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.4.3",
"version": "0.4.5",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -56,6 +56,7 @@ const OHIFDicomRTStructSopClassHandler = {
SeriesTime,
SeriesNumber,
SeriesDescription,
metadata,
};
if (!metadata.ReferencedSeriesSequence) {
@@ -12,6 +12,9 @@ import StructureSetItem from '../StructureSetItem/StructureSetItem';
import RTPanelSettings from '../RTSettings/RTSettings';
import PanelSection from '../PanelSection/PanelSection';
import LoadingIndicator from '../LoadingIndicator/LoadingIndicator';
import TOOL_NAMES from '../../utils/toolNames';
const { RTSTRUCT_DISPLAY_TOOL } = TOOL_NAMES;
const { studyMetadataManager } = utils;
@@ -144,13 +147,16 @@ const RTPanel = ({
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
const stackToolState = cornerstoneTools.getToolState(
element,
'stack'
);
if (!toolState) {
if (!stackToolState) {
return;
}
const imageIds = toolState.data[0].imageIds;
const imageIds = stackToolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
@@ -159,6 +165,18 @@ const RTPanel = ({
imageIds
);
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState();
const imageIdSpecificToolState = toolState[imageId];
const rtstructData =
imageIdSpecificToolState[RTSTRUCT_DISPLAY_TOOL];
const specificData = rtstructData.data.find(
rtData => rtData.ROINumber === ROINumber
);
specificData.highlight = true;
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
@@ -1,6 +1,6 @@
import { importInternal, getToolState, toolColors } from 'cornerstone-tools';
import TOOL_NAMES from '../utils/toolNames';
import drawCanvasCrosshairs from '../utils/drawCanvasCrosshairs';
// Cornerstone 3rd party dev kit imports
const draw = importInternal('drawing/draw');
@@ -37,11 +37,17 @@ export default class RTStructDisplayTool extends BaseTool {
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
const context = getNewContext(eventData.canvasContext.canvas);
let crossHairCenter;
for (let i = 0; i < toolState.data.length; i++) {
const data = toolState.data[i];
@@ -75,27 +81,51 @@ export default class RTStructDisplayTool extends BaseTool {
colorArray[2]
},${opacity})`;
let highlight = data.highlight;
const options = { color, lineWidth };
if (highlight) {
crossHairCenter = { x: 0, y: 0 };
points.forEach(point => {
crossHairCenter.x += point.x;
crossHairCenter.y += point.y;
});
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, {
color,
lineWidth,
});
this._renderClosedPlanar(context, eventData.element, points, options);
break;
case 'POINT':
this._renderPoint(context, eventData.element, points, {
color,
lineWidth,
});
this._renderPoint(context, eventData.element, points, options);
break;
case 'OPEN_PLANAR':
this._renderOpenPlanar(context, eventData.element, points, {
color,
lineWidth,
});
this._renderOpenPlanar(context, eventData.element, points, options);
break;
}
}
if (crossHairCenter) {
drawCanvasCrosshairs(eventData, crossHairCenter, {
color: toolColors.getActiveColor(),
lineWidth: 1,
});
}
}
_renderClosedPlanar(context, element, points, options) {
@@ -13,7 +13,8 @@ function refreshViewport() {
const configuration = {
lineWidth: 3,
opacity: 1.0,
opacity: 0.75,
highlightOpacity: 0.5,
};
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,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.4...@ohif/extension-dicom-segmentation@0.4.5) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.4.3",
"version": "0.4.5",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -5,6 +5,7 @@ import cornerstone from 'cornerstone-core';
import moment from 'moment';
import { utils, log } from '@ohif/core';
import { ScrollableArea, TableList, Icon } from '@ohif/ui';
import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool';
import setActiveLabelmap from '../../utils/setActiveLabelMap';
import refreshViewports from '../../utils/refreshViewports';
@@ -334,6 +335,12 @@ const SegmentationPanel = ({
imageId
);
DICOMSegTempCrosshairsTool.addCrosshair(
element,
imageId,
segmentNumber
);
onSegmentItemClick({
StudyInstanceUID,
SOPInstanceUID,
@@ -28,12 +28,12 @@ export default function getSopClassHandlerModule({ servicesManager }) {
const {
SeriesDate,
SeriesTime,
SeriesNumber,
SeriesDescription,
FrameOfReferenceUID,
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
SeriesNumber,
} = metadata;
const segDisplaySet = {
@@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
SeriesTime,
SeriesNumber,
SeriesDescription,
metadata,
};
segDisplaySet.getSourceDisplaySet = function(studies) {
@@ -1,4 +1,5 @@
import csTools from 'cornerstone-tools';
import DICOMSegTempCrosshairsTool from './tools/DICOMSegTempCrosshairsTool';
/**
*
@@ -17,4 +18,6 @@ export default function init({ servicesManager, configuration = {} }) {
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,8 @@
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
module.exports = (env, argv) => {
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
};
@@ -0,0 +1,38 @@
const merge = require('webpack-merge');
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
const pkg = require('./../package.json');
const ROOT_DIR = path.join(__dirname, './..');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
module.exports = (env, argv) => {
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
return merge(commonConfig, {
devtool: 'source-map',
stats: {
colors: true,
hash: true,
timings: true,
assets: true,
chunks: false,
chunkModules: false,
modules: false,
children: false,
warnings: true,
},
optimization: {
minimize: true,
sideEffects: true,
},
output: {
path: ROOT_DIR,
library: 'OHIFExtDicomP10Downloader',
libraryTarget: 'umd',
libraryExport: 'default',
filename: pkg.main,
},
});
};
@@ -0,0 +1,8 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 0.0.2 (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Open Health Imaging Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1
View File
@@ -0,0 +1 @@
# @ohif/extension-dicom-tag-browser
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@ohif/extension-dicom-tag-browser",
"version": "0.0.2",
"description": "OHIF extension for checking DICOM headers.",
"author": "OHIF",
"license": "MIT",
"repository": "OHIF/Viewers",
"main": "dist/index.umd.js",
"module": "src/index.js",
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=10",
"npm": ">=6",
"yarn": ">=1.16.0"
},
"files": [
"dist",
"README.md"
],
"scripts": {
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package": "yarn run build",
"prepublishOnly": "yarn run build",
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "^2.6.0",
"dcmjs": "0.16.0",
"react": "^16.8.6"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"dicomweb-client": "^0.6.0",
"moment": "2.24.0",
"react-select": "^3.0.8"
}
}
@@ -0,0 +1,58 @@
import React from 'react';
import Select from 'react-select';
const DicomBrowserSelect = ({ value, formatOptionLabel, options }) => (
<Select
value={value}
formatOptionLabel={formatOptionLabel}
styles={dicomBrowserSelectStyles}
options={options}
/>
);
const computedstyle = getComputedStyle(document.body);
const uiGrayDarker = computedstyle.getPropertyValue('--ui-gray-darker');
const activeColor = computedstyle.getPropertyValue('--active-color');
const defaultColor = computedstyle.getPropertyValue('--default-color');
const uiGrayDark = computedstyle.getPropertyValue('--ui-gray-dark');
const dicomBrowserSelectStyles = {
singleValue: (base, state) => ({
...base,
width: '100%',
}),
control: (base, state) => ({
...base,
cursor: 'pointer',
background: uiGrayDarker,
borderRadius: state.isFocused ? '5px 5px 5px 5px' : 5,
borderColor: state.isFocused ? activeColor : defaultColor,
minHeight: '50px',
'&:hover': {
borderColor: activeColor,
},
}),
menu: base => ({
...base,
borderRadius: 5,
background: uiGrayDarker,
}),
option: (base, state) => ({
...base,
cursor: 'pointer',
'&:first-of-type': {
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
},
'&:last-of-type': {
borderBottomLeftRadius: 5,
borderBottomRightRadius: 5,
},
background: state.isSelected ? uiGrayDark : uiGrayDarker,
'&:hover': {
background: uiGrayDark,
},
}),
};
export default DicomBrowserSelect;
@@ -0,0 +1,25 @@
.dcmseg-segmentation-item {
display: flex;
justify-content: start;
margin: 0;
}
.dcmseg-segmentation-item .segmentation-meta {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: hidden;
}
.dcmseg-segmentation-item .segmentation-meta-title {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: white;
max-width: calc(100% - 30px);
}
.dcmseg-segmentation-item .segmentation-meta-description {
font-size: 12px;
color: var(--text-secondary-color);
}
@@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import './DicomBrowserSelectItem.css';
const DicomBrowserSelectItem = ({ onClick, title, description }) => {
return (
<li className="dcmseg-segmentation-item" onClick={onClick}>
<div className="segmentation-meta">
<div className="segmentation-meta-title">{title}</div>
<div className="segmentation-meta-description">{description}</div>
</div>
</li>
);
};
DicomBrowserSelectItem.propTypes = {
onClick: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
};
DicomBrowserSelectItem.defaultProps = {
description: '',
};
export default DicomBrowserSelectItem;
@@ -0,0 +1,27 @@
.dicom-tag-browser-table {
margin-right: auto;
margin-left: auto;
}
.dicom-tag-browser-table td {
padding-left: 10px;
padding-right: 10px;
color: var(--table-text-primary-color);
border-top: 1px solid #ddd;
white-space: nowrap;
}
.dicom-tag-browser-table td.dicom-tag-browser-table-center {
text-align: center;
}
.dicom-tag-browser-table th {
padding-left: 10px;
padding-right: 10px;
text-align: center;
color: var(--active-color);
}
.dicom-tag-browser-table th.dicom-tag-browser-table-left {
text-align: left;
}
@@ -0,0 +1,228 @@
import React, { useState } from 'react';
import { classes } from '@ohif/core';
import dcmjs from 'dcmjs';
import DicomBrowserSelect from './DicomBrowserSelect';
import moment from 'moment';
import './DicomTagBrowser.css';
import DicomBrowserSelectItem from './DicomBrowserSelectItem';
const { ImageSet } = classes;
const { DicomMetaDictionary } = dcmjs.data;
const { nameMap } = DicomMetaDictionary;
const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
const [
activeDisplaySetInstanceUID,
setActiveDisplaySetInstanceUID,
] = useState(displaySetInstanceUID);
const [activeInstance, setActiveInstance] = useState(0);
const activeDisplaySet = displaySets.find(
ds => ds.displaySetInstanceUID === activeDisplaySetInstanceUID
);
const displaySetList = displaySets.map(displaySet => {
const {
displaySetInstanceUID,
SeriesDate,
SeriesTime,
SeriesNumber,
SeriesDescription,
Modality,
} = displaySet;
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const displayDate = date.format('ddd, MMM Do YYYY');
return {
value: displaySetInstanceUID,
title: `${SeriesNumber} (${Modality}): ${SeriesDescription}`,
description: displayDate,
onClick: () => {
setActiveDisplaySetInstanceUID(displaySetInstanceUID);
setActiveInstance(0);
},
};
});
let metadata;
const isImageStack = activeDisplaySet instanceof ImageSet;
let selectedInstanceValue;
let instanceList;
if (isImageStack) {
const { images } = activeDisplaySet;
const image = images[activeInstance];
instanceList = images.map((image, index) => {
const metadata = image.getData().metadata;
const { InstanceNumber } = metadata;
return {
value: index,
title: `Instance Number: ${InstanceNumber}`,
description: '',
onClick: () => {
setActiveInstance(index);
},
};
});
selectedInstanceValue = instanceList[activeInstance];
metadata = image.getData().metadata;
} else {
metadata = activeDisplaySet.metadata;
}
const selectedDisplaySetValue = displaySetList.find(
ds => ds.value === activeDisplaySetInstanceUID
);
return (
<div>
<DicomBrowserSelect
value={selectedDisplaySetValue}
formatOptionLabel={DicomBrowserSelectItem}
options={displaySetList}
/>
{isImageStack ? (
<DicomBrowserSelect
value={selectedInstanceValue}
formatOptionLabel={DicomBrowserSelectItem}
options={instanceList}
/>
) : null}
<DicomTagTable instanceMetadata={metadata}></DicomTagTable>
</div>
);
};
function DicomTagTable({ instanceMetadata }) {
const rows = getRows(instanceMetadata);
return (
<div>
<table className="dicom-tag-browser-table">
<tr>
<th className="dicom-tag-browser-table-left">Tag</th>
<th className="dicom-tag-browser-table-left">Value Representation</th>
<th className="dicom-tag-browser-table-left">Keyword</th>
<th className="dicom-tag-browser-table-left">Value</th>
</tr>
{rows.map(row => (
<tr>
<td>{row[0]}</td>
<td>{row[1]}</td>
<td>{row[2]}</td>
<td>{row[3]}</td>
</tr>
))}
</table>
</div>
);
}
function getRows(metadata, depth = 0) {
// Tag, Type, Value, Keyword
const keywords = Object.keys(metadata);
let tagIndent = '';
for (let i = 0; i < depth; i++) {
tagIndent += '>';
}
const rows = [];
for (let i = 0; i < keywords.length; i++) {
let keyword = keywords[i];
if (keyword === '_vrMap') {
continue;
}
const tagInfo = nameMap[keyword];
let value = metadata[keyword];
if (tagInfo && tagInfo.vr === 'SQ') {
const sequenceAsArray = toArray(value);
// Push line defining the sequence
rows.push([`${tagIndent}${tagInfo.tag}`, tagInfo.vr, keyword, '']);
if (value === null) {
// Type 2 Sequence
continue;
}
sequenceAsArray.forEach(item => {
const sequenceRows = getRows(item, depth + 1);
sequenceRows.forEach(row => {
rows.push(row);
});
});
continue;
}
if (Array.isArray(value)) {
value = value.join('\\');
}
if (typeof value === 'number') {
value = value.toString();
}
if (typeof value !== 'string') {
if (value === null) {
value = ' ';
} else {
if (typeof value === 'object') {
if (value.InlineBinary) {
value = 'Inline Binary';
} else if (value.BulkDataURI) {
value = `Bulk Data URI`; //: ${value.BulkDataURI}`;
} else if (value.Alphabetic) {
value = value.Alphabetic;
} else {
console.error('Unrecognised Value for element:');
console.error(value);
value = ' ';
}
} else {
console.error('Unrecognised Value for element:');
console.error(value);
value = ' ';
}
}
}
// Remove retired tags
keyword = keyword.replace('RETIRED_', '');
if (tagInfo) {
rows.push([`${tagIndent}${tagInfo.tag}`, tagInfo.vr, keyword, value]);
} else {
// Private tag
const tag = `(${keyword.substring(0, 4)},${keyword.substring(4, 8)})`;
rows.push([`${tagIndent}${tag}`, '', 'Private Tag', value]);
}
}
return rows;
}
function toArray(objectOrArray) {
return Array.isArray(objectOrArray) ? objectOrArray : [objectOrArray];
}
export default DicomTagBrowser;
@@ -0,0 +1,52 @@
import { utils } from '@ohif/core';
import React from 'react';
import DicomTagBrowser from './components/DicomTagBrowser';
const { studyMetadataManager } = utils;
export default function getCommandsModule(servicesManager) {
const actions = {
openDICOMTagViewer({ viewports }) {
const { activeViewportIndex, viewportSpecificData } = viewports;
const activeViewportSpecificData =
viewportSpecificData[activeViewportIndex];
const {
StudyInstanceUID,
displaySetInstanceUID,
} = activeViewportSpecificData;
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
const displaySets = studyMetadata.getDisplaySets();
const { UIModalService } = servicesManager.services;
const WrappedDicomTagBrowser = function() {
return (
<DicomTagBrowser
displaySets={displaySets}
displaySetInstanceUID={displaySetInstanceUID}
/>
);
};
UIModalService.show({
content: WrappedDicomTagBrowser,
title: `DICOM Tag Browser`,
fullscreen: true,
});
},
};
const definitions = {
openDICOMTagViewer: {
commandFn: actions.openDICOMTagViewer,
storeContexts: ['servers', 'viewports'],
},
};
return {
actions,
definitions,
};
}
+26
View File
@@ -0,0 +1,26 @@
import getCommandsModule from './getCommandsModule';
import toolbarModule from './toolbarModule';
/**
* Constants
*/
/**
* Extension
*/
export default {
/**
* Only required property. Should be a unique value across all extensions.
*/
id: 'dicom-tag-browser',
/**
* MODULE GETTERS
*/
getCommandsModule({ servicesManager }) {
return getCommandsModule(servicesManager);
},
getToolbarModule() {
return toolbarModule;
},
};
@@ -0,0 +1,20 @@
const TOOLBAR_BUTTON_TYPES = {
COMMAND: 'command',
};
const definitions = [
{
id: 'TagBrowser',
label: 'Tag Browser',
icon: 'list',
//
type: TOOLBAR_BUTTON_TYPES.COMMAND,
commandName: 'openDICOMTagViewer',
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.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.7.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.6...@ohif/extension-vtk@1.7.7) (2020-09-17)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.7.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.5...@ohif/extension-vtk@1.7.6) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-vtk
## [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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.7.4",
"version": "1.7.7",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -53,8 +53,8 @@
"react-vtkjs-viewport": "^0.10.4"
},
"devDependencies": {
"@ohif/core": "^2.10.3",
"@ohif/ui": "^1.5.3",
"@ohif/core": "^2.10.5",
"@ohif/ui": "^1.5.5",
"cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.10.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.4...@ohif/core@2.10.5) (2020-09-10)
**Note:** Version bump only for package @ohif/core
## [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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.10.3",
"version": "2.10.5",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -38,6 +38,7 @@ function _show({
onClose = null,
closeButton = true,
title = null,
fullscreen = false,
customClassName = null,
}) {
return serviceImplementation._show({
@@ -48,6 +49,7 @@ function _show({
onClose,
closeButton,
title,
fullscreen,
customClassName,
});
}
@@ -11,12 +11,15 @@ import { retrieveStudyMetadata } from './retrieveStudyMetadata';
* @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 {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
*/
export default function retrieveStudiesMetadata(
server,
studyInstanceUIDs,
filters
filters,
separateSeriesInstanceUIDFilters = false
) {
// Create an empty array to store the Promises for each metaData retrieval call
const promises = [];
@@ -24,7 +27,12 @@ export default function retrieveStudiesMetadata(
// Loop through the array of studyInstanceUIDs
studyInstanceUIDs.forEach(function(StudyInstanceUID) {
// 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
promises.push(promise);
@@ -11,9 +11,16 @@ const StudyMetaDataPromises = new Map();
* @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 {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
*/
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
// and further requests for that metadata will always fail. On failure, we probably need to remove the
// corresponding promise from the "StudyMetaDataPromises" map...
@@ -33,11 +40,29 @@ export function retrieveStudyMetadata(server, StudyInstanceUID, filters) {
}
// Create a promise to handle the data retrieval
const promise = new Promise((resolve, reject) => {
RetrieveMetadata(server, StudyInstanceUID, filters).then(function(data) {
resolve(data);
}, reject);
});
let promise;
if (
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
StudyMetaDataPromises.set(StudyInstanceUID, promise);
@@ -45,6 +70,49 @@ export function retrieveStudyMetadata(server, StudyInstanceUID, filters) {
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
* re-retrieve the study metadata when it is next requested
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.5](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.4...@ohif/ui@1.5.5) (2020-09-17)
**Note:** Version bump only for package @ohif/ui
## [1.5.4](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.3...@ohif/ui@1.5.4) (2020-09-10)
**Note:** Version bump only for package @ohif/ui
## [1.5.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.2...@ohif/ui@1.5.3) (2020-07-23)
**Note:** Version bump only for package @ohif/ui
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "1.5.3",
"version": "1.5.5",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
@@ -22,6 +22,7 @@ const OHIFModal = ({
closeButton,
shouldCloseOnEsc,
isOpen,
fullscreen,
title,
onClose,
children,
@@ -41,9 +42,13 @@ const OHIFModal = ({
);
};
const classes = fullscreen
? classNames('OHIFModal', className, 'OHIFModal-fullscreen')
: classNames('OHIFModal', className);
return (
<Modal
className={classNames('OHIFModal', className)}
className={classes}
data-cy="modal"
shouldCloseOnEsc={shouldCloseOnEsc}
isOpen={isOpen}
@@ -18,9 +18,17 @@
@media (min-width: 768px)
width: 600px
&.OHIFModal-fullscreen
width: 95%
@media (min-width: 992px)
width: 900px
&.OHIFModal-fullscreen
width: 95%
&__content
padding: 20px
max-height: 90vh;
@@ -36,6 +36,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
closeButton: true,
title: null,
customClassName: '',
fullscreen: false,
};
const [options, setOptions] = useState(DEFAULT_OPTIONS);
@@ -78,6 +79,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
title,
customClassName,
shouldCloseOnEsc,
fullscreen,
closeButton,
} = options;
@@ -89,6 +91,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
shouldCloseOnEsc={shouldCloseOnEsc}
isOpen={isOpen}
title={title}
fullscreen={fullscreen}
closeButton={closeButton}
onClose={() => {
if (onClose) {
+2
View File
@@ -87,6 +87,7 @@ import user from './icons/user.svg';
import youtube from './icons/youtube.svg';
import eye from './icons/eye.svg';
import eyeClosed from './icons/eye-closed.svg';
import envelopeSquare from './icons/envelope-square.svg';
const ICONS = {
eye,
@@ -176,6 +177,7 @@ const ICONS = {
lung,
liver,
save: saveRegular,
'envelope-square': envelopeSquare,
};
/**
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
aria-labelledby="envelope-square"
width="1em"
height="1em"
fill="currentColor"
>
<path d="M400 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM178.117 262.104C87.429 196.287 88.353 196.121 64 177.167V152c0-13.255 10.745-24 24-24h272c13.255 0 24 10.745 24 24v25.167c-24.371 18.969-23.434 19.124-114.117 84.938-10.5 7.655-31.392 26.12-45.883 25.894-14.503.218-35.367-18.227-45.883-25.895zM384 217.775V360c0 13.255-10.745 24-24 24H88c-13.255 0-24-10.745-24-24V217.775c13.958 10.794 33.329 25.236 95.303 70.214 14.162 10.341 37.975 32.145 64.694 32.01 26.887.134 51.037-22.041 64.72-32.025 61.958-44.965 81.325-59.406 95.283-70.199z"/>
</svg>

After

Width:  |  Height:  |  Size: 811 B

+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.5.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.9...@ohif/viewer@4.5.10) (2020-09-17)
**Note:** Version bump only for package @ohif/viewer
## [4.5.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.8...@ohif/viewer@4.5.9) (2020-09-10)
**Note:** Version bump only for package @ohif/viewer
## [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
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "4.5.5",
"version": "4.5.10",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -48,18 +48,18 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.10.3",
"@ohif/core": "^2.10.5",
"@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-debugging": "^0.1.5",
"@ohif/extension-dicom-html": "^1.2.8",
"@ohif/extension-dicom-microscopy": "^0.51.3",
"@ohif/extension-dicom-pdf": "^1.0.6",
"@ohif/extension-dicom-rt": "^0.4.5",
"@ohif/extension-dicom-segmentation": "^0.4.5",
"@ohif/extension-lesion-tracker": "^0.2.1",
"@ohif/extension-vtk": "^1.7.4",
"@ohif/extension-vtk": "^1.7.7",
"@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.5.3",
"@ohif/ui": "^1.5.5",
"@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6",
"core-js": "^3.2.1",
+3
View File
@@ -5,11 +5,14 @@ window.config = function(props) {
disableMeasurementPanel: true,
routerBasename: '/',
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,
showStudyList: true,
filterQueryParam: true,
httpErrorHandler: error => {
// This is 429 when rejected from the public idc sandbox too often.
console.warn(error.status);
// Could use services manager here to bring up a dialog/modal if needed.
console.warn('test, navigate to https://ohif.org/');
window.location = 'https://ohif.org/';
@@ -88,6 +88,7 @@ const _isQueryParamApplied = (study, filters = {}, isFilterStrategy) => {
const { seriesInstanceUID } = filters;
let applied = true;
// skip in case no filter or no toast manager
if (!seriesInstanceUID) {
return applied;
}
@@ -340,6 +341,13 @@ function ViewerRetrieveStudyData({
}
}
if (
appConfig.splitQueryParameterCalls ||
appConfig.enableGoogleCloudAdapter
) {
retrieveParams.push(true); // Seperate SeriesInstanceUID filter calls.
}
cancelableStudiesPromises[studyInstanceUIDs] = makeCancelable(
retrieveStudiesMetadata(...retrieveParams)
)
+3 -1
View File
@@ -29,6 +29,7 @@ import OHIFDicomSegmentationExtension from '@ohif/extension-dicom-segmentation';
import OHIFDicomRtExtension from '@ohif/extension-dicom-rt';
import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy';
import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf';
import OHIFDicomTagBrowserExtension from '@ohif/extension-dicom-tag-browser';
// Add this for Debugging purposes:
//import OHIFDebuggingExtension from '@ohif/extension-debugging';
import { version } from '../package.json';
@@ -53,7 +54,8 @@ const appProps = {
OHIFDicomPDFExtension,
OHIFDicomSegmentationExtension,
OHIFDicomRtExtension,
//OHIFDebuggingExtension,
//[OHIFDebuggingExtension, { mailTo: 'support@canceridc.dev' }],
//OHIFDicomTagBrowserExtension,
],
};