Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d57158b59d | ||
|
|
4a7ce1c093 | ||
|
|
333677c40e | ||
|
|
b459b894bd | ||
|
|
c5a4860209 | ||
|
|
4f79bda7c9 | ||
|
|
7d22bb7d5a | ||
|
|
36822cc6ce | ||
|
|
e676f0b5b5 | ||
|
|
57de66a65f | ||
|
|
99022f2bac | ||
|
|
47b1e2837f | ||
|
|
d604ebaffd | ||
|
|
c1a0d3c662 | ||
|
|
16866a47a0 | ||
|
|
77ab0ad9a1 | ||
|
|
1c306dd243 | ||
|
|
404d52fe5c | ||
|
|
d9a50b6b08 | ||
|
|
e41d339f5f | ||
|
|
e374fe2118 | ||
|
|
9feadd3c6d |
No files matched your search
@@ -18,6 +18,9 @@ exemptLabels:
|
||||
- 'Triage :white_flag:'
|
||||
- 'Extension: Discussion'
|
||||
- 'Announcement 🎉'
|
||||
- 'IDC:priority'
|
||||
- 'IDC:candidate'
|
||||
- 'IDC:collaboration'
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: 'Stale :baguette_bread:'
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
|
||||
@@ -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.50.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.7...@ohif/extension-dicom-microscopy@0.50.8) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.50.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.6...@ohif/extension-dicom-microscopy@0.50.7) (2020-03-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "0.50.7",
|
||||
"version": "0.50.8",
|
||||
"description": "OHIF extension for Dicom Microscopy",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -34,11 +34,12 @@ class DicomMicroscopyViewport extends Component {
|
||||
.then(instances => {
|
||||
const promises = [];
|
||||
for (let i = 0; i < instances.length; i++) {
|
||||
const sopInstanceUid = instances[i]['00080018']['Value'][0];
|
||||
const sopInstanceUID = instances[i]['00080018']['Value'][0];
|
||||
|
||||
const retrieveInstanceOptions = {
|
||||
studyInstanceUID: displaySet.StudyInstanceUID,
|
||||
seriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||
sopInstanceUid,
|
||||
sopInstanceUID,
|
||||
};
|
||||
|
||||
const promise = dicomWebClient
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.0...@ohif/extension-dicom-segmentation@0.1.1) (2020-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 0.1.0 (2020-03-13)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-segmentation",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -14,7 +14,7 @@ const OHIFDicomSegSopClassHandler = {
|
||||
id: 'OHIFDicomSegSopClassHandler',
|
||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||
sopClassUIDs,
|
||||
getDisplaySetFromSeries: function(
|
||||
getDisplaySetFromSeries: function (
|
||||
series,
|
||||
study,
|
||||
dicomWebClient,
|
||||
@@ -53,7 +53,7 @@ const OHIFDicomSegSopClassHandler = {
|
||||
SeriesDescription,
|
||||
};
|
||||
|
||||
segDisplaySet.load = function(referencedDisplaySet, studies) {
|
||||
segDisplaySet.load = function (referencedDisplaySet, studies) {
|
||||
return loadSegmentation(
|
||||
segDisplaySet,
|
||||
referencedDisplaySet,
|
||||
|
||||
+81
-84
@@ -39,50 +39,26 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
* TODO: wrap get/set interactions with the cornerstoneTools
|
||||
* store with context to make these kind of things less blurry.
|
||||
*/
|
||||
const segmentationModule = cornerstoneTools.getModule('segmentation');
|
||||
const { configuration } = segmentationModule;
|
||||
const { configuration } = cornerstoneTools.getModule('segmentation');
|
||||
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
|
||||
|
||||
const [brushRadius, setBrushRadius] = useState(DEFAULT_BRUSH_RADIUS);
|
||||
|
||||
/* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */
|
||||
const [brushColor, setBrushColor] = useState('rgba(221, 85, 85, 1)');
|
||||
const [selectedSegment, setSelectedSegment] = useState();
|
||||
const [showSegSettings, setShowSegSettings] = useState(false);
|
||||
const [selectedSegmentation, setSelectedSegmentation] = useState();
|
||||
|
||||
const viewport = viewports[activeIndex];
|
||||
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
} = viewport;
|
||||
|
||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||
const firstImageId = studyMetadata.getFirstImageId(displaySetInstanceUID);
|
||||
|
||||
/* CornerstoneTools */
|
||||
const [brushStackState, setBrushStackState] = useState(
|
||||
segmentationModule.state.series[firstImageId]
|
||||
);
|
||||
const [state, setState] = useState({
|
||||
brushRadius: DEFAULT_BRUSH_RADIUS,
|
||||
brushColor: 'rgba(221, 85, 85, 1)', /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */
|
||||
selectedSegment: null,
|
||||
selectedSegmentation: null,
|
||||
showSegSettings: false,
|
||||
brushStackState: null,
|
||||
labelmapList: [],
|
||||
segmentList: []
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setShowSegSettings(showSegSettings && !isOpen);
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
setBrushStackState(segmentationModule.state.series[firstImageId]);
|
||||
}, [studies, viewports, activeIndex, firstImageId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (brushStackState) {
|
||||
setSelectedSegmentation(brushStackState.activeLabelmapIndex);
|
||||
}
|
||||
|
||||
const labelmapModifiedHandler = event => {
|
||||
log.warn('labelmap modified', event);
|
||||
setBrushStackState(segmentationModule.state.series[firstImageId]);
|
||||
log.warn('Segmentation Panel: labelmap modified', event);
|
||||
const module = cornerstoneTools.getModule('segmentation');
|
||||
const activeViewport = viewports[activeIndex];
|
||||
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
|
||||
updateState('brushStackState', module.state.series[firstImageId]);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -107,27 +83,43 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
};
|
||||
});
|
||||
|
||||
if (!brushStackState) {
|
||||
return null;
|
||||
}
|
||||
useEffect(() => {
|
||||
const module = cornerstoneTools.getModule('segmentation');
|
||||
const activeViewport = viewports[activeIndex];
|
||||
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
|
||||
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
|
||||
const brushStackState = module.state.series[firstImageId];
|
||||
|
||||
const labelmap3D =
|
||||
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
|
||||
if (brushStackState) {
|
||||
const labelmap3D = brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
|
||||
const labelmapList = getLabelmapList(brushStackState, firstImageId, activeViewport);
|
||||
const segmentList = getSegmentList(labelmap3D, firstImageId);
|
||||
setState(state => ({
|
||||
...state,
|
||||
brushStackState,
|
||||
selectedSegmentation: brushStackState.activeLabelmapIndex,
|
||||
labelmapList,
|
||||
segmentList
|
||||
}));
|
||||
} else {
|
||||
setState(state => ({
|
||||
...state,
|
||||
labelmapList: [],
|
||||
segmentList: [],
|
||||
}));
|
||||
}
|
||||
}, [studies, viewports, activeIndex]);
|
||||
|
||||
/*
|
||||
* 2. UseEffect to update state? or to a least trigger a re-render
|
||||
* 4. Toggle visibility of labelmap?
|
||||
* 5. Toggle visibility of seg?
|
||||
*
|
||||
* If the port is cornerstone, just need to call a re-render.
|
||||
* If the port is vtkjs, its a bit more tricky as we now need to create a new
|
||||
*/
|
||||
/* Handle open/closed panel behaviour */
|
||||
useEffect(() => {
|
||||
updateState('showSegSettings', state.showSegSettings && !isOpen);
|
||||
}, [isOpen]);
|
||||
|
||||
const getLabelmapList = () => {
|
||||
const getLabelmapList = (brushStackState, firstImageId, activeViewport) => {
|
||||
/* Get list of SEG labelmaps specific to active viewport (reference series) */
|
||||
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID
|
||||
activeViewport.StudyInstanceUID,
|
||||
activeViewport.SeriesInstanceUID
|
||||
);
|
||||
|
||||
return referencedSegDisplaysets.map((displaySet, index) => {
|
||||
@@ -148,23 +140,19 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
description: displayDate,
|
||||
onClick: async () => {
|
||||
const activatedLabelmapIndex = await _setActiveLabelmap(
|
||||
viewport,
|
||||
activeViewport,
|
||||
studies,
|
||||
displaySet,
|
||||
firstImageId,
|
||||
brushStackState.activeLabelmapIndex
|
||||
);
|
||||
setSelectedSegmentation(activatedLabelmapIndex);
|
||||
updateState('selectedSegmentation', activatedLabelmapIndex);
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const labelmapList = getLabelmapList();
|
||||
|
||||
const segmentList = [];
|
||||
|
||||
if (labelmap3D) {
|
||||
const getSegmentList = (labelmap3D, firstImageId) => {
|
||||
/*
|
||||
* Newly created segments have no `meta`
|
||||
* So we instead build a list of all segment indexes in use
|
||||
@@ -186,10 +174,12 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
}, [])
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
const module = cornerstoneTools.getModule('segmentation');
|
||||
const colorLutTable =
|
||||
segmentationModule.state.colorLutTables[labelmap3D.colorLUTIndex];
|
||||
module.state.colorLutTables[labelmap3D.colorLUTIndex];
|
||||
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
|
||||
|
||||
const segmentList = [];
|
||||
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
|
||||
const segmentIndex = uniqueSegmentIndexes[i];
|
||||
|
||||
@@ -207,14 +197,14 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const sameSegment = selectedSegment === segmentNumber;
|
||||
const sameSegment = state.selectedSegment === segmentNumber;
|
||||
const setCurrentSelectedSegment = () => {
|
||||
_setActiveSegment(
|
||||
firstImageId,
|
||||
segmentNumber,
|
||||
labelmap3D.activeSegmentIndex
|
||||
);
|
||||
setSelectedSegment(sameSegment ? null : segmentNumber);
|
||||
updateState('selectedSegment', sameSegment ? null : segmentNumber);
|
||||
};
|
||||
|
||||
segmentList.push(
|
||||
@@ -229,20 +219,27 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
);
|
||||
}
|
||||
|
||||
return segmentList;
|
||||
|
||||
/*
|
||||
* Let's iterate over segmentIndexes ^ above
|
||||
* If meta has a match, use it to show info
|
||||
* If now, add "no-meta" class
|
||||
* Show default name
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
const updateState = (field, value) => {
|
||||
setState(state => ({ ...state, [field]: value }));
|
||||
};
|
||||
|
||||
const updateBrushSize = evt => {
|
||||
const updatedRadius = Number(evt.target.value);
|
||||
|
||||
if (updatedRadius !== brushRadius) {
|
||||
setBrushRadius(updatedRadius);
|
||||
segmentationModule.setters.radius(updatedRadius);
|
||||
updateState('brushRadius', updatedRadius);
|
||||
const module = cornerstoneTools.getModule('segmentation');
|
||||
module.setters.radius(updatedRadius);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -251,29 +248,30 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
if (labelmap3D.activeSegmentIndex > 1) {
|
||||
labelmap3D.activeSegmentIndex--;
|
||||
}
|
||||
setSelectedSegment(labelmap3D.activeSegmentIndex);
|
||||
updateState('selectedSegment', labelmap3D.activeSegmentIndex);
|
||||
updateActiveSegmentColor();
|
||||
};
|
||||
|
||||
const incrementSegment = event => {
|
||||
event.preventDefault();
|
||||
labelmap3D.activeSegmentIndex++;
|
||||
setSelectedSegment(labelmap3D.activeSegmentIndex);
|
||||
updateState('selectedSegment', labelmap3D.activeSegmentIndex);
|
||||
updateActiveSegmentColor();
|
||||
};
|
||||
|
||||
const updateActiveSegmentColor = () => {
|
||||
const color = getActiveSegmentColor();
|
||||
setBrushColor(color);
|
||||
updateState('brushColor', color);
|
||||
};
|
||||
|
||||
const getActiveSegmentColor = () => {
|
||||
if (!brushStackState) {
|
||||
if (!state.brushStackState) {
|
||||
return 'rgba(255, 255, 255, 1)';
|
||||
}
|
||||
|
||||
const module = cornerstoneTools.getModule('segmentation');
|
||||
const colorLutTable =
|
||||
segmentationModule.state.colorLutTables[labelmap3D.colorLUTIndex];
|
||||
module.state.colorLutTables[labelmap3D.colorLUTIndex];
|
||||
const color = colorLutTable[labelmap3D.activeSegmentIndex];
|
||||
|
||||
return `rgba(${color.join(',')})`;
|
||||
@@ -293,11 +291,11 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
refreshViewport();
|
||||
};
|
||||
|
||||
if (showSegSettings) {
|
||||
if (state.showSegSettings) {
|
||||
return (
|
||||
<SegmentationSettings
|
||||
configuration={configuration}
|
||||
onBack={() => setShowSegSettings(false)}
|
||||
onBack={() => updateState('showSegSettings', false)}
|
||||
onChange={updateConfiguration}
|
||||
/>
|
||||
);
|
||||
@@ -309,13 +307,13 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
name="cog"
|
||||
width="25px"
|
||||
height="25px"
|
||||
onClick={() => setShowSegSettings(true)}
|
||||
onClick={() => updateState('showSegSettings', true)}
|
||||
/>
|
||||
{false && (
|
||||
<form className="selector-form">
|
||||
<BrushColorSelector
|
||||
defaultColor={brushColor}
|
||||
index={labelmap3D.activeSegmentIndex}
|
||||
index={state.selectedSegment}
|
||||
onNext={incrementSegment}
|
||||
onPrev={decrementSegment}
|
||||
/>
|
||||
@@ -331,17 +329,17 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
|
||||
<div className="segmentations">
|
||||
<SegmentationSelect
|
||||
value={
|
||||
labelmapList.find(i => i.value === selectedSegmentation) || null
|
||||
state.labelmapList.find(i => i.value === state.selectedSegmentation) || null
|
||||
}
|
||||
formatOptionLabel={SegmentationItem}
|
||||
options={labelmapList}
|
||||
options={state.labelmapList}
|
||||
/>
|
||||
</div>
|
||||
<ScrollableArea>
|
||||
<TableList
|
||||
customHeader={<SegmentsHeader count={segmentList.length} />}
|
||||
customHeader={<SegmentsHeader count={state.segmentList.length} />}
|
||||
>
|
||||
{segmentList}
|
||||
{state.segmentList}
|
||||
</TableList>
|
||||
</ScrollableArea>
|
||||
</div>
|
||||
@@ -420,7 +418,7 @@ const _setActiveLabelmap = async (
|
||||
) => {
|
||||
if (displaySet.labelmapIndex === activeLabelmapIndex) {
|
||||
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
|
||||
return;
|
||||
return displaySet.labelmapIndex;
|
||||
}
|
||||
|
||||
if (!displaySet.isLoaded) {
|
||||
@@ -431,7 +429,6 @@ const _setActiveLabelmap = async (
|
||||
|
||||
const { state } = cornerstoneTools.getModule('segmentation');
|
||||
const brushStackState = state.series[firstImageId];
|
||||
|
||||
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex;
|
||||
|
||||
refreshViewport();
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [1.3.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.5...@ohif/extension-vtk@1.3.6) (2020-04-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.4...@ohif/extension-vtk@1.3.5) (2020-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.3...@ohif/extension-vtk@1.3.4) (2020-03-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.2...@ohif/extension-vtk@1.3.3) (2020-03-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.1...@ohif/extension-vtk@1.3.2) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.0...@ohif/extension-vtk@1.3.1) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.6",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -53,7 +53,7 @@
|
||||
"react-vtkjs-viewport": "^0.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.6.0",
|
||||
"@ohif/core": "^2.6.5",
|
||||
"@ohif/ui": "^1.4.1",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
|
||||
@@ -3,6 +3,62 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.6.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.4...@ohif/core@2.6.5) (2020-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.3...@ohif/core@2.6.4) (2020-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add support for single entries in SequenceOfUltrasoundRegions. M… ([#1559](https://github.com/OHIF/Viewers/issues/1559)) ([c1a0d3c](https://github.com/OHIF/Viewers/commit/c1a0d3c662d143b62dfbf1c01f6ce394af3756ca))
|
||||
* disable autoFreeze of immer, even in dev mode ([#1560](https://github.com/OHIF/Viewers/issues/1560)) ([d604eba](https://github.com/OHIF/Viewers/commit/d604ebaffd93f688eadd0081e402f27074dd226b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.2...@ohif/core@2.6.3) (2020-03-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Ensure we take into account pixel spacing fields properly ([#1555](https://github.com/OHIF/Viewers/issues/1555)) ([77ab0ad](https://github.com/OHIF/Viewers/commit/77ab0ad9a14a135b5560741fc1600704df08c141))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.1...@ohif/core@2.6.2) (2020-03-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* OverlayPlane module usage for ArrayBuffer, BulkDataURI, and InlineBinary cases, as well as PaletteColor LUTs for ArrayBuffer (i.e. local drag/drop) case ([#1546](https://github.com/OHIF/Viewers/issues/1546)) ([404d52f](https://github.com/OHIF/Viewers/commit/404d52fe5c0442dd13e4d407bb0687d72fa5f32c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.0...@ohif/core@2.6.1) (2020-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* avoid-wasteful-renders ([#1544](https://github.com/OHIF/Viewers/issues/1544)) ([e41d339](https://github.com/OHIF/Viewers/commit/e41d339f5faef6b93700bc860f37f29f32ad5ed6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.6.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.5.3...@ohif/core@2.6.0) (2020-03-13)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.5",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -41,6 +41,7 @@
|
||||
"ajv": "^6.10.0",
|
||||
"dcmjs": "^0.8.3",
|
||||
"dicomweb-client": "^0.5.2",
|
||||
"immer": "6.0.2",
|
||||
"isomorphic-base64": "^1.0.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.merge": "^4.6.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as dcmjs from 'dcmjs';
|
||||
import queryString from 'query-string';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import { getFallbackTagFromInstance } from '../utils/metadataProvider/metadataProviderFallbackTags';
|
||||
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
|
||||
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
|
||||
import fetchOverlayData from '../utils/metadataProvider/fetchOverlayData';
|
||||
|
||||
@@ -58,9 +58,7 @@ class MetadataProvider {
|
||||
|
||||
Object.assign(instance, naturalizedDataset);
|
||||
|
||||
if (options.server) {
|
||||
await this._checkBulkDataAndInlineBinaries(instance, options.server);
|
||||
}
|
||||
await this._checkBulkDataAndInlineBinaries(instance, options.server);
|
||||
|
||||
return instance;
|
||||
}
|
||||
@@ -163,18 +161,6 @@ class MetadataProvider {
|
||||
return instance[naturalizedTagOrWADOImageLoaderTag];
|
||||
}
|
||||
|
||||
if (options.fallback) {
|
||||
// Perhaps the tag has fallbacks?
|
||||
const fallbackTag = getFallbackTagFromInstance(
|
||||
naturalizedTagOrWADOImageLoaderTag,
|
||||
instance
|
||||
);
|
||||
|
||||
if (fallbackTag) {
|
||||
return fallbackTag;
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe its a legacy CornerstoneWADOImageLoader tag then:
|
||||
return this._getCornerstoneWADOImageLoaderTag(
|
||||
naturalizedTagOrWADOImageLoaderTag,
|
||||
@@ -220,10 +206,9 @@ class MetadataProvider {
|
||||
const { ImageOrientationPatient } = instance;
|
||||
|
||||
// Fallback for DX images.
|
||||
const PixelSpacing = getFallbackTagFromInstance(
|
||||
'PixelSpacing',
|
||||
instance
|
||||
);
|
||||
// TODO: We should use the rest of the results of this function
|
||||
// to update the UI somehow
|
||||
const { PixelSpacing } = getPixelSpacingInformation(instance);
|
||||
|
||||
let rowPixelSpacing;
|
||||
let columnPixelSpacing;
|
||||
@@ -344,7 +329,7 @@ class MetadataProvider {
|
||||
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.OVERLAY_PLANE_MODULE:
|
||||
metadata = [];
|
||||
const overlays = [];
|
||||
|
||||
for (
|
||||
let overlayGroup = 0x00;
|
||||
@@ -389,9 +374,13 @@ class MetadataProvider {
|
||||
roiStandardDeviation: instance[ROIStandardDeviationTag],
|
||||
};
|
||||
|
||||
metadata.push(overlay);
|
||||
overlays.push(overlay);
|
||||
}
|
||||
|
||||
metadata = {
|
||||
overlays
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_MODULE:
|
||||
|
||||
@@ -78,7 +78,7 @@ export class StudyMetadata extends Metadata {
|
||||
Object.defineProperty(this, 'studyInstanceUID', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
get: function() {
|
||||
get: function () {
|
||||
return this.getStudyInstanceUID();
|
||||
},
|
||||
});
|
||||
@@ -208,20 +208,21 @@ export class StudyMetadata extends Metadata {
|
||||
/**
|
||||
* Returns a list of derived datasets in the study, filtered by the given filter.
|
||||
* @param {object} filter An object containing search filters
|
||||
* @param {object} filter.modality
|
||||
* @param {object} filter.Modality
|
||||
* @param {object} filter.referencedSeriesInstanceUID
|
||||
* @param {object} filter.referencedFrameOfReferenceUID
|
||||
* @return {Array} filtered derived display sets
|
||||
*/
|
||||
getDerivedDatasets(filter) {
|
||||
const {
|
||||
modality,
|
||||
Modality,
|
||||
referencedSeriesInstanceUID,
|
||||
referencedFrameOfReferenceUID,
|
||||
} = filter;
|
||||
|
||||
let filteredDerivedDisplaySets = this._derivedDisplaySets;
|
||||
|
||||
if (modality) {
|
||||
if (Modality) {
|
||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||
displaySet => displaySet.Modality === Modality
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import merge from 'lodash.merge';
|
||||
import produce, { setAutoFreeze } from 'immer';
|
||||
|
||||
import {
|
||||
CLEAR_VIEWPORT,
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
SET_VIEWPORT_LAYOUT_AND_DATA,
|
||||
} from './../constants/ActionTypes.js';
|
||||
|
||||
setAutoFreeze(false);
|
||||
|
||||
export const DEFAULT_STATE = {
|
||||
numRows: 1,
|
||||
numColumns: 1,
|
||||
@@ -93,12 +95,13 @@ const viewports = (state = DEFAULT_STATE, action) => {
|
||||
* @return {Object} New state.
|
||||
*/
|
||||
case SET_VIEWPORT_ACTIVE: {
|
||||
const activeViewportIndex = getActiveViewportIndex(
|
||||
state.numRows,
|
||||
state.numColumns,
|
||||
action.viewportIndex
|
||||
);
|
||||
return { ...state, activeViewportIndex };
|
||||
return produce(state, draftState => {
|
||||
draftState.activeViewportIndex = getActiveViewportIndex(
|
||||
draftState.numRows,
|
||||
draftState.numColumns,
|
||||
action.viewportIndex
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,21 +166,20 @@ const viewports = (state = DEFAULT_STATE, action) => {
|
||||
* @return {Object} New state.
|
||||
*/
|
||||
case SET_VIEWPORT: {
|
||||
const layout = cloneDeep(state.layout);
|
||||
return produce(state, draftState => {
|
||||
draftState.viewportSpecificData[action.viewportIndex] =
|
||||
draftState.viewportSpecificData[action.viewportIndex] || {};
|
||||
|
||||
let viewportSpecificData = cloneDeep(state.viewportSpecificData);
|
||||
viewportSpecificData[action.viewportIndex] = merge(
|
||||
{},
|
||||
viewportSpecificData[action.viewportIndex],
|
||||
action.viewportSpecificData
|
||||
);
|
||||
Object.keys(action.viewportSpecificData).forEach(key => {
|
||||
draftState.viewportSpecificData[action.viewportIndex][key] =
|
||||
action.viewportSpecificData[key];
|
||||
});
|
||||
|
||||
if (action.viewportSpecificData && action.viewportSpecificData.plugin) {
|
||||
layout.viewports[action.viewportIndex].plugin =
|
||||
action.viewportSpecificData.plugin;
|
||||
}
|
||||
|
||||
return { ...state, layout, viewportSpecificData };
|
||||
if (action.viewportSpecificData && action.viewportSpecificData.plugin) {
|
||||
draftState.layout.viewports[action.viewportIndex].plugin =
|
||||
action.viewportSpecificData.plugin;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../../DICOMWeb';
|
||||
import str2ab from '../str2ab';
|
||||
import unpackOverlay from './unpackOverlay';
|
||||
|
||||
export default async function fetchOverlayData(instance, server) {
|
||||
const OverlayDataPromises = [];
|
||||
@@ -15,11 +17,18 @@ export default async function fetchOverlayData(instance, server) {
|
||||
|
||||
const OverlayDataTag = `${groupStr}3000`;
|
||||
|
||||
if (instance[OverlayDataTag] && instance[OverlayDataTag].BulkDataURI) {
|
||||
if (instance[OverlayDataTag] && instance[OverlayDataTag].InlineBinary) {
|
||||
const inlineBinaryData = atob(instance[OverlayDataTag].InlineBinary);
|
||||
const arraybuffer = str2ab(inlineBinaryData);
|
||||
|
||||
instance[OverlayDataTag] = unpackOverlay(arraybuffer)
|
||||
} else if (instance[OverlayDataTag] && instance[OverlayDataTag].BulkDataURI) {
|
||||
OverlayDataPromises.push(
|
||||
_getOverlayData(instance[OverlayDataTag], server)
|
||||
);
|
||||
OverlayDataTags.push(OverlayDataTag);
|
||||
} else if (instance[OverlayDataTag] && instance[OverlayDataTag] instanceof ArrayBuffer) {
|
||||
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,19 +69,7 @@ async function _getOverlayData(tag, server) {
|
||||
return dicomWeb
|
||||
.retrieveBulkData(options)
|
||||
.then(result => result[0])
|
||||
.then(_unpackOverlay);
|
||||
.then(unpackOverlay);
|
||||
}
|
||||
|
||||
function _unpackOverlay(arrayBuffer) {
|
||||
const bitArray = new Uint8Array(arrayBuffer);
|
||||
const byteArray = new Uint8Array(8 * bitArray.length);
|
||||
|
||||
for (let byteIndex = 0; byteIndex < byteArray.length; byteIndex++) {
|
||||
const bitIndex = byteIndex % 8;
|
||||
const bitByteIndex = Math.floor(byteIndex / 8);
|
||||
byteArray[byteIndex] =
|
||||
1 * ((bitArray[bitByteIndex] & (1 << bitIndex)) >> bitIndex);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../../DICOMWeb';
|
||||
import str2ab from '../str2ab';
|
||||
|
||||
export default async function fetchPaletteColorLookupTableData(
|
||||
instance,
|
||||
@@ -157,31 +158,12 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
|
||||
.then(arrayBufferToPaletteColorLUT);
|
||||
} else if (paletteColorLookupTableData.InlineBinary) {
|
||||
const inlineBinaryData = atob(paletteColorLookupTableData.InlineBinary);
|
||||
const arraybuffer = _str2ab(inlineBinaryData);
|
||||
const arraybuffer = str2ab(inlineBinaryData);
|
||||
|
||||
return new Promise(resolve => {
|
||||
resolve(arrayBufferToPaletteColorLUT(arraybuffer));
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(arrayBufferToPaletteColorLUT(paletteColorLookupTableData));
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'Palette Color LUT was not provided as InlineBinary or BulkDataURI'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert String to ArrayBuffer
|
||||
*
|
||||
* @param {String} str Input String
|
||||
* @return {ArrayBuffer} Output converted ArrayBuffer
|
||||
*/
|
||||
function _str2ab(str) {
|
||||
const strLen = str.length;
|
||||
const bytes = new Uint8Array(strLen);
|
||||
|
||||
for (let i = 0; i < strLen; i++) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import log from '../../log';
|
||||
|
||||
export default function getPixelSpacingInformation(instance) {
|
||||
// See http://gdcm.sourceforge.net/wiki/index.php/Imager_Pixel_Spacing
|
||||
|
||||
// TODO: Add Ultrasound region spacing
|
||||
// TODO: Add manual calibration
|
||||
|
||||
// TODO: Use ENUMS from dcmjs
|
||||
const projectionRadiographSOPClassUIDs = [
|
||||
'1.2.840.10008.5.1.4.1.1.1', // CR Image Storage
|
||||
'1.2.840.10008.5.1.4.1.1.1.1', // Digital X-Ray Image Storage – for Presentation
|
||||
'1.2.840.10008.5.1.4.1.1.1.1.1', // Digital X-Ray Image Storage – for Processing
|
||||
'1.2.840.10008.5.1.4.1.1.1.2', // Digital Mammography X-Ray Image Storage – for Presentation
|
||||
'1.2.840.10008.5.1.4.1.1.1.2.1', // Digital Mammography X-Ray Image Storage – for Processing
|
||||
'1.2.840.10008.5.1.4.1.1.1.3', // Digital Intra – oral X-Ray Image Storage – for Presentation
|
||||
'1.2.840.10008.5.1.4.1.1.1.3.1', // Digital Intra – oral X-Ray Image Storage – for Processing
|
||||
'1.2.840.10008.5.1.4.1.1.12.1', // X-Ray Angiographic Image Storage
|
||||
'1.2.840.10008.5.1.4.1.1.12.1.1', // Enhanced XA Image Storage
|
||||
'1.2.840.10008.5.1.4.1.1.12.2', // X-Ray Radiofluoroscopic Image Storage
|
||||
'1.2.840.10008.5.1.4.1.1.12.2.1', // Enhanced XRF Image Storage
|
||||
'1.2.840.10008.5.1.4.1.1.12.3', // X-Ray Angiographic Bi-plane Image Storage Retired
|
||||
];
|
||||
|
||||
const {
|
||||
PixelSpacing,
|
||||
ImagerPixelSpacing,
|
||||
SOPClassUID,
|
||||
PixelSpacingCalibrationType,
|
||||
PixelSpacingCalibrationDescription,
|
||||
EstimatedRadiographicMagnificationFactor,
|
||||
SequenceOfUltrasoundRegions,
|
||||
} = instance;
|
||||
const isProjection = projectionRadiographSOPClassUIDs.includes(SOPClassUID);
|
||||
|
||||
const TYPES = {
|
||||
NOT_APPLICABLE: 'NOT_APPLICABLE',
|
||||
UNKNOWN: 'UNKNOWN',
|
||||
CALIBRATED: 'CALIBRATED',
|
||||
DETECTOR: 'DETECTOR',
|
||||
};
|
||||
|
||||
if (isProjection && !ImagerPixelSpacing) {
|
||||
// If only Pixel Spacing is present, and this is a projection radiograph,
|
||||
// PixelSpacing should be used, but the user should be informed that
|
||||
// what it means is unknown
|
||||
return {
|
||||
PixelSpacing,
|
||||
type: TYPES.UNKNOWN,
|
||||
isProjection,
|
||||
};
|
||||
} else if (
|
||||
PixelSpacing &&
|
||||
ImagerPixelSpacing &&
|
||||
PixelSpacing === ImagerPixelSpacing
|
||||
) {
|
||||
// If Imager Pixel Spacing and Pixel Spacing are present and they have the same values,
|
||||
// then the user should be informed that the measurements are at the detector plane
|
||||
return {
|
||||
PixelSpacing,
|
||||
type: TYPES.DETECTOR,
|
||||
isProjection,
|
||||
};
|
||||
} else if (
|
||||
PixelSpacing &&
|
||||
ImagerPixelSpacing &&
|
||||
PixelSpacing !== ImagerPixelSpacing
|
||||
) {
|
||||
// If Imager Pixel Spacing and Pixel Spacing are present and they have different values,
|
||||
// then the user should be informed that these are "calibrated"
|
||||
// (in some unknown manner if Pixel Spacing Calibration Type and/or
|
||||
// Pixel Spacing Calibration Description are absent)
|
||||
return {
|
||||
PixelSpacing,
|
||||
type: TYPES.CALIBRATED,
|
||||
isProjection,
|
||||
PixelSpacingCalibrationType,
|
||||
PixelSpacingCalibrationDescription,
|
||||
};
|
||||
} else if (!PixelSpacing && ImagerPixelSpacing) {
|
||||
let CorrectedImagerPixelSpacing = ImagerPixelSpacing;
|
||||
if (EstimatedRadiographicMagnificationFactor) {
|
||||
// Note that in IHE Mammo profile compliant displays, the value of Imager Pixel Spacing is required to be corrected by
|
||||
// Estimated Radiographic Magnification Factor and the user informed of that.
|
||||
// TODO: should this correction be done before all of this logic?
|
||||
CorrectedImagerPixelSpacing = ImagerPixelSpacing.map(
|
||||
pixelSpacing => pixelSpacing / EstimatedRadiographicMagnificationFactor
|
||||
);
|
||||
} else {
|
||||
log.info(
|
||||
'EstimatedRadiographicMagnificationFactor was not present. Unable to correct ImagerPixelSpacing.'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
PixelSpacing: CorrectedImagerPixelSpacing,
|
||||
isProjection,
|
||||
};
|
||||
} else if (
|
||||
SequenceOfUltrasoundRegions &&
|
||||
typeof SequenceOfUltrasoundRegions === 'object'
|
||||
) {
|
||||
const { PhysicalDeltaX, PhysicalDeltaY } = SequenceOfUltrasoundRegions;
|
||||
const USPixelSpacing = [PhysicalDeltaX * 10, PhysicalDeltaY * 10];
|
||||
|
||||
return {
|
||||
PixelSpacing: USPixelSpacing,
|
||||
};
|
||||
} else if (
|
||||
SequenceOfUltrasoundRegions &&
|
||||
Array.isArray(SequenceOfUltrasoundRegions) &&
|
||||
SequenceOfUltrasoundRegions.length > 1
|
||||
) {
|
||||
log.warn(
|
||||
'Sequence of Ultrasound Regions > one entry. This is not yet implemented, all measurements will be shown in pixels.'
|
||||
);
|
||||
} else if (isProjection === false && !ImagerPixelSpacing) {
|
||||
// If only Pixel Spacing is present, and this is not a projection radiograph,
|
||||
// we can stop here
|
||||
return {
|
||||
PixelSpacing,
|
||||
type: TYPES.NOT_APPLICABLE,
|
||||
isProjection,
|
||||
};
|
||||
}
|
||||
|
||||
log.info(
|
||||
'Unknown combination of PixelSpacing and ImagerPixelSpacing identified. Unable to determine spacing.'
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import log from '../../log';
|
||||
|
||||
function getFallbackTagFromInstance(tag, instance) {
|
||||
if (instance[tag]) {
|
||||
return instance[tag];
|
||||
}
|
||||
|
||||
const fallbackTags = fallbackTagsMap[tag];
|
||||
|
||||
if (fallbackTags) {
|
||||
for (let i = 0; i < fallbackTags.length; i++) {
|
||||
const fallbackTag = fallbackTags[i];
|
||||
|
||||
if (instance[fallbackTag]) {
|
||||
log.info(`metadata provider fallback tag ${tag} to ${fallbackTag}`);
|
||||
|
||||
return instance[fallbackTag];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fallbackTagsMap = {
|
||||
PixelSpacing: ['ImagerPixelSpacing'],
|
||||
};
|
||||
|
||||
export { fallbackTagsMap, getFallbackTagFromInstance };
|
||||
@@ -0,0 +1,13 @@
|
||||
export default function unpackOverlay(arrayBuffer) {
|
||||
const bitArray = new Uint8Array(arrayBuffer);
|
||||
const byteArray = new Uint8Array(8 * bitArray.length);
|
||||
|
||||
for (let byteIndex = 0; byteIndex < byteArray.length; byteIndex++) {
|
||||
const bitIndex = byteIndex % 8;
|
||||
const bitByteIndex = Math.floor(byteIndex / 8);
|
||||
byteArray[byteIndex] =
|
||||
1 * ((bitArray[bitByteIndex] & (1 << bitIndex)) >> bitIndex);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Convert String to ArrayBuffer
|
||||
*
|
||||
* @param {String} str Input String
|
||||
* @return {ArrayBuffer} Output converted ArrayBuffer
|
||||
*/
|
||||
export default function str2ab(str) {
|
||||
const strLen = str.length;
|
||||
const bytes = new Uint8Array(strLen);
|
||||
|
||||
for (let i = 0; i < strLen; i++) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
}
|
||||
@@ -3,6 +3,101 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.8.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.12...@ohif/viewer@3.8.13) (2020-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.11...@ohif/viewer@3.8.12) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.10...@ohif/viewer@3.8.11) (2020-03-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#1312](https://github.com/OHIF/Viewers/issues/1312) Cine dialog remains on screen ([#1540](https://github.com/OHIF/Viewers/issues/1540)) ([7d22bb7](https://github.com/OHIF/Viewers/commit/7d22bb7d5a8590cffc169725c93942f758fe13a0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.9...@ohif/viewer@3.8.10) (2020-03-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.8...@ohif/viewer@3.8.9) (2020-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Load measurement in active viewport. ([#1558](https://github.com/OHIF/Viewers/issues/1558)) ([99022f2](https://github.com/OHIF/Viewers/commit/99022f2bac752f3cd1cedb61e222b8d411e158c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.7...@ohif/viewer@3.8.8) (2020-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.6...@ohif/viewer@3.8.7) (2020-03-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.5...@ohif/viewer@3.8.6) (2020-03-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.4...@ohif/viewer@3.8.5) (2020-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* avoid-wasteful-renders ([#1544](https://github.com/OHIF/Viewers/issues/1544)) ([e41d339](https://github.com/OHIF/Viewers/commit/e41d339f5faef6b93700bc860f37f29f32ad5ed6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.3...@ohif/viewer@3.8.4) (2020-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Only permit web workers to be initialized once. ([#1535](https://github.com/OHIF/Viewers/issues/1535)) ([9feadd3](https://github.com/OHIF/Viewers/commit/9feadd3c6d71c1c48f7825d024ccf95d5d82606d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.2...@ohif/viewer@3.8.3) (2020-03-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@@ -7,15 +7,22 @@ describe('OHIF User Preferences', () => {
|
||||
beforeEach(() => {
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
cy.initPreferencesModalAliases();
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
cy.initPreferencesModalAliases();
|
||||
//Check Title
|
||||
cy.get('@preferencesModal').should('contain.text', 'User Preferences');
|
||||
//Check tabs
|
||||
cy.get('@userPreferencesHotkeysTab')
|
||||
.should('have.text', 'Hotkeys')
|
||||
.and('have.class', 'active');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.text', 'General');
|
||||
cy.get('@userPreferencesWindowLevelTab').should(
|
||||
'have.text',
|
||||
'Window Level'
|
||||
);
|
||||
//Check buttons
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
.should('have.text', 'Reset to Defaults');
|
||||
@@ -26,8 +33,7 @@ describe('OHIF User Preferences', () => {
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
@@ -92,16 +98,22 @@ describe('OHIF User Preferences', () => {
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to general tab
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// Save
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon').click({ force: true });
|
||||
}
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
// Header should be in "English (USA)"
|
||||
cy.get('.research-use')
|
||||
@@ -124,30 +136,54 @@ describe('OHIF User Preferences', () => {
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks if W/L Preferences table is being displayed in the Window Level tab', function() {
|
||||
//Navigate to Window Level tab
|
||||
cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
|
||||
//Check table header
|
||||
cy.get('.wlRow.header')
|
||||
.should('contains.text', 'Preset')
|
||||
.and('contains.text', 'Description')
|
||||
.and('contains.text', 'Window')
|
||||
.and('contains.text', 'Level');
|
||||
|
||||
//Check table has more than 1 row (more than header)
|
||||
cy.get('.wlRow')
|
||||
.its('length')
|
||||
.should('be.greaterThan', 1);
|
||||
});
|
||||
|
||||
it('checks if Preferences set in Study List Page will be consistent on Viewer Page', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@userPreferencesHotkeysTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set new hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal('Rotate Right', '{shift}Q');
|
||||
// Save new hotkey
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon').click({ force: true });
|
||||
}
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
// Open User Preferences modal again
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to General tab
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Go to Study Viewer page
|
||||
cy.openStudyInViewer('1.2.840.113619.2.5.1762583153.215519.978957063.78');
|
||||
cy.waitDicomImage();
|
||||
cy.checkStudyRouteInViewer(
|
||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||
);
|
||||
cy.expectMinimumThumbnails(5);
|
||||
cy.initCommonElementsAliases();
|
||||
|
||||
@@ -178,7 +214,9 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
context('Study Viewer Page', function() {
|
||||
before(() => {
|
||||
cy.openStudyInViewer('1.2.840.113619.2.5.1762583153.215519.978957063.78');
|
||||
cy.checkStudyRouteInViewer(
|
||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||
);
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
@@ -186,29 +224,40 @@ describe('OHIF User Preferences', () => {
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
|
||||
cy.resetUserHoktkeyPreferences();
|
||||
cy.resetUserHotkeyPreferences();
|
||||
cy.resetUserGeneralPreferences();
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Close User Preferences Modal (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.OHIFModal__header').length > 0) {
|
||||
cy.get('[data-cy="close-button"]').click({ force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
cy.get('@preferencesModal').should('contain.text', 'User Preferences');
|
||||
cy.get('@userPreferencesHotkeysTab')
|
||||
.should('have.text', 'Hotkeys')
|
||||
.and('have.class', 'active');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.text', 'General');
|
||||
cy.get('@userPreferencesWindowLevelTab').should(
|
||||
'have.text',
|
||||
'Window Level'
|
||||
);
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
.should('have.text', 'Reset to Defaults');
|
||||
cy.get('@cancelBtn').should('have.text', 'Cancel');
|
||||
cy.get('@saveBtn').should('have.text', 'Save');
|
||||
|
||||
cy.get('[data-cy="close-button"]').click(); //close User Preferences modal
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
@@ -232,9 +281,6 @@ describe('OHIF User Preferences', () => {
|
||||
cy.get('[data-cy="dd-item-menu"]')
|
||||
.last()
|
||||
.should('contain.text', 'Preferencias');
|
||||
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks if user can cancel the language selection and application will be in "English (USA)"', function() {
|
||||
@@ -260,13 +306,10 @@ describe('OHIF User Preferences', () => {
|
||||
cy.get('[data-cy="dd-item-menu"]')
|
||||
.last()
|
||||
.should('contain.text', 'Preferences');
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks if user can restore to default the language selection and application will be in "English (USA)', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
@@ -278,12 +321,14 @@ describe('OHIF User Preferences', () => {
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to general tab
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// click on restore button
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
@@ -303,15 +348,11 @@ describe('OHIF User Preferences', () => {
|
||||
cy.get('[data-cy="dd-item-menu"]')
|
||||
.last()
|
||||
.should('contain.text', 'Preferences');
|
||||
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks new hotkeys for "Rotate Right" and "Rotate Left"', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@userPreferencesHotkeysTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set new hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
@@ -324,10 +365,16 @@ describe('OHIF User Preferences', () => {
|
||||
'{shift}{leftarrow}'
|
||||
);
|
||||
|
||||
//Save new hotkeys
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon').click({ force: true });
|
||||
}
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
//Rotate Right with new Hotkey
|
||||
cy.get('body').type('{shift}{rightarrow}');
|
||||
@@ -340,8 +387,8 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
it('checks new hotkeys for "Next" and "Previous" Image on Viewport', function() {
|
||||
// Update hotkeys for 'Next/Previous Viewport'
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@userPreferencesHotkeysTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
'Next Viewport',
|
||||
'{shift}{rightarrow}'
|
||||
@@ -350,9 +397,16 @@ describe('OHIF User Preferences', () => {
|
||||
'Previous Viewport',
|
||||
'{shift}{leftarrow}'
|
||||
);
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon').click({ force: true });
|
||||
}
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
// Set 3 viewports layout
|
||||
cy.setLayout(3, 1);
|
||||
@@ -391,7 +445,7 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
it('checks error message when duplicated hotkeys are inserted', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set duplicated hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal('Rotate Right', '{i}');
|
||||
@@ -404,15 +458,11 @@ describe('OHIF User Preferences', () => {
|
||||
.as('errorMsg')
|
||||
.should('have.text', '"Invert" is already using the "i" shortcut.');
|
||||
});
|
||||
//Cancel hotkeys
|
||||
cy.get('@cancelBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
it('checks error message when invalid hotkey is inserted', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set invalid hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal('Rotate Right', '{ctrl}Z');
|
||||
@@ -425,16 +475,11 @@ describe('OHIF User Preferences', () => {
|
||||
.as('errorMsg')
|
||||
.should('have.text', '"ctrl+z" shortcut combination is not allowed');
|
||||
});
|
||||
|
||||
//Cancel hotkeys
|
||||
cy.get('@cancelBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
it('checks error message when only modifier keys are inserted', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set invalid modifier key: ctrl
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal('Zoom Out', '{ctrl}');
|
||||
@@ -465,16 +510,11 @@ describe('OHIF User Preferences', () => {
|
||||
'have.text',
|
||||
"It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut"
|
||||
);
|
||||
|
||||
//Cancel hotkeys
|
||||
cy.get('@cancelBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
it('checks if user can cancel changes made on User Preferences Hotkeys tab', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set new hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
@@ -482,10 +522,16 @@ describe('OHIF User Preferences', () => {
|
||||
'{ctrl}{shift}S'
|
||||
);
|
||||
|
||||
//Cancel hotkeys
|
||||
cy.get('@cancelBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon').click({ force: true });
|
||||
}
|
||||
//Cancel hotkeys
|
||||
cy.get('@cancelBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
// Open User Preferences modal again
|
||||
cy.openPreferences();
|
||||
@@ -497,12 +543,11 @@ describe('OHIF User Preferences', () => {
|
||||
.find('input')
|
||||
.should('have.value', 'r');
|
||||
});
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
});
|
||||
|
||||
it('checks if user can reset to default values on User Preferences Hotkeys tab', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set new hotkey for 'Rotate Right' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
@@ -510,7 +555,7 @@ describe('OHIF User Preferences', () => {
|
||||
'{ctrl}{shift}S'
|
||||
);
|
||||
|
||||
//Save hotkeys
|
||||
// click on save button
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
@@ -523,9 +568,6 @@ describe('OHIF User Preferences', () => {
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// Open User Preferences modal again
|
||||
cy.openPreferences();
|
||||
|
||||
//Check that hotkey for 'Rotate Right' function was not changed
|
||||
cy.get('.HotkeysPreferences').within(() => {
|
||||
cy.contains('Rotate Right') // label we're looking for
|
||||
@@ -533,7 +575,183 @@ describe('OHIF User Preferences', () => {
|
||||
.find('input')
|
||||
.should('have.value', 'r');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('W/L Preset Preferences', function() {
|
||||
before(() => {
|
||||
cy.checkStudyRouteInViewer(
|
||||
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
|
||||
);
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.initCommonElementsAliases();
|
||||
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
// Navigate to Window Level tab
|
||||
cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
});
|
||||
|
||||
it('checks if W/L Preferences table is being displayed in the Window Level tab', function() {
|
||||
//Check table header
|
||||
cy.get('.wlRow.header')
|
||||
.should('contains.text', 'Preset')
|
||||
.and('contains.text', 'Description')
|
||||
.and('contains.text', 'Window')
|
||||
.and('contains.text', 'Level');
|
||||
|
||||
//Check table has more than 1 row (more than header)
|
||||
cy.get('.wlRow')
|
||||
.its('length')
|
||||
.should('be.greaterThan', 1);
|
||||
});
|
||||
|
||||
// //TODO: Test blocked by issue #1551: https://github.com/OHIF/Viewers/issues/1551
|
||||
// it('checks if user can add a new W/L preset', function() {
|
||||
// let description = ':nth-child(8) > .description > .preferencesInput';
|
||||
// let window = ':nth-child(8) > .window > .preferencesInput';
|
||||
// let level = ':nth-child(8) > .level > .preferencesInput';
|
||||
// let new_window_value = 150;
|
||||
// let new_level_value = -600;
|
||||
// // Check existing preset values
|
||||
// cy.get(description).should('have.value', '');
|
||||
// cy.get(window).should('have.value', '');
|
||||
// cy.get(level).should('have.value', '');
|
||||
|
||||
// // Set new preset value
|
||||
// cy.setWindowLevelPreset(
|
||||
// 7,
|
||||
// 'New Description',
|
||||
// new_window_value,
|
||||
// new_level_value
|
||||
// );
|
||||
// cy.get('@saveBtn').click();
|
||||
|
||||
// // Open User Preferences modal
|
||||
// cy.openPreferences();
|
||||
// // Navigate to Window Level tab
|
||||
// cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
|
||||
// // Check recently added preset values
|
||||
// cy.get(description).should('have.value', 'New Description');
|
||||
// cy.get(window).should('have.value', new_window_value);
|
||||
// cy.get(level).should('have.value', new_level_value);
|
||||
|
||||
// // Close User Preferences modal
|
||||
// cy.get('[data-cy="close-button"]').click();
|
||||
|
||||
// // Check if new hotkey preset is working on viewport
|
||||
// cy.get('body').type('8');
|
||||
// cy.get('@viewportInfoBottomRight').should(
|
||||
// 'contains.text',
|
||||
// 'W: ' + new_window_value + ' L: ' + new_level_value
|
||||
// );
|
||||
// });
|
||||
|
||||
it('checks if user can remove an existing W/L preset', function() {
|
||||
let description = ':nth-child(3) > .description > .preferencesInput';
|
||||
let window = ':nth-child(3) > .window > .preferencesInput';
|
||||
let level = ':nth-child(3) > .level > .preferencesInput';
|
||||
// Check existing preset values
|
||||
cy.get(description)
|
||||
.should('not.have.value', '')
|
||||
.clear();
|
||||
cy.get(window)
|
||||
.should('not.have.value', '')
|
||||
.clear();
|
||||
cy.get(level)
|
||||
.should('not.have.value', '')
|
||||
.clear();
|
||||
|
||||
// Save changes
|
||||
cy.get('@saveBtn').click();
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
// Navigate to Window Level tab
|
||||
cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
|
||||
// Check recently added preset values
|
||||
cy.get(description).should('have.value', '');
|
||||
cy.get(window).should('have.value', '');
|
||||
cy.get(level).should('have.value', '');
|
||||
// Close User Preferences modal
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
});
|
||||
|
||||
// //TODO: Test blocked by issue #1551: https://github.com/OHIF/Viewers/issues/1551
|
||||
// it('checks if user can edit an existing W/L preset', function() {
|
||||
// let description = ':nth-child(2) > .description > .preferencesInput';
|
||||
// let window = ':nth-child(2) > .window > .preferencesInput';
|
||||
// let level = ':nth-child(2) > .level > .preferencesInput';
|
||||
// // Check existing preset values
|
||||
// cy.get(description).should('have.value', 'Soft tissue');
|
||||
// cy.get(window).should('have.value', '550');
|
||||
// cy.get(level).should('have.value', '40');
|
||||
|
||||
// // Set new preset value
|
||||
// cy.setWindowLevelPreset(1, 'Soft tissue New Description', 1220, 333);
|
||||
// cy.get('@saveBtn').click();
|
||||
|
||||
// // Open User Preferences modal
|
||||
// cy.openPreferences();
|
||||
// // Navigate to Window Level tab
|
||||
// cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
|
||||
// // Check recently added preset values
|
||||
// cy.get(description).should('have.value', 'Soft tissue New Description');
|
||||
// cy.get(window).should('have.value', '1220');
|
||||
// cy.get(level).should('have.value', '333');
|
||||
// });
|
||||
|
||||
it('checks if user can change the W/L by triggering different hotkeys with W/L presets', function() {
|
||||
// Close User Preferences modal
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
// Check if hotkey preset is working on viewport
|
||||
cy.get('body').type('3');
|
||||
cy.get('@viewportInfoBottomRight').should(
|
||||
'contains.text',
|
||||
'W: 150 L: 90'
|
||||
);
|
||||
|
||||
// Check if hotkey preset is working on viewport
|
||||
cy.get('body').type('4');
|
||||
cy.get('@viewportInfoBottomRight').should(
|
||||
'contains.text',
|
||||
'W: 2500 L: 480'
|
||||
);
|
||||
});
|
||||
|
||||
it('checks if user can change the W/L by triggering different hotkeys with W/L presets on multiple viewports', function() {
|
||||
// Close User Preferences modal
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
|
||||
// Set 3 viewports layout
|
||||
cy.setLayout(3, 1);
|
||||
cy.waitViewportImageLoading();
|
||||
|
||||
// Check if hotkey preset is working on viewport
|
||||
cy.get('body').type('3');
|
||||
cy.get('@viewportInfoBottomRight').should(
|
||||
'contains.text',
|
||||
'W: 150 L: 90'
|
||||
);
|
||||
|
||||
// Overlay information from 2nd viewport
|
||||
let second_viewport_overlay =
|
||||
'div:nth-child(2) > div > div.viewport-element > div.ViewportOverlay > div.bottom-right.overlay-element > div';
|
||||
|
||||
// Shift active viewport to Viewport #2
|
||||
cy.get('body').type('{rightarrow}');
|
||||
|
||||
// Check if hotkey preset is working on viewport #2
|
||||
cy.get('body').type('4');
|
||||
cy.get(second_viewport_overlay).should('contains.text', 'W: 2500 L: 480');
|
||||
|
||||
// Set 1 viewport layout
|
||||
cy.setLayout(1, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -4,7 +4,7 @@ describe('Visual Regression - OHIF PDF Extension', () => {
|
||||
'1.2.826.0.13854362241694438965858641723883466450351448'
|
||||
);
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});v
|
||||
});
|
||||
|
||||
it('drags and drop a PDF thumbnail into viewport', () => {
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
|
||||
+20
-11
@@ -7,10 +7,11 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
beforeEach(() => {
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
cy.initPreferencesModalAliases();
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@restoreBtn').scrollIntoView();
|
||||
|
||||
// Visual comparison
|
||||
@@ -21,8 +22,7 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
@@ -59,12 +59,14 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
|
||||
cy.resetUserHoktkeyPreferences();
|
||||
cy.resetUserHotkeyPreferences();
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@restoreBtn').scrollIntoView();
|
||||
|
||||
// Visual comparison
|
||||
@@ -74,9 +76,18 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
cy.get('[data-cy="close-button"]').click(); //close User Preferences modal
|
||||
});
|
||||
|
||||
it('checks if W/L Preferences table is being displayed in the Window Level tab', function() {
|
||||
//Navigate to Window Level tab
|
||||
cy.selectPreferencesTab('@userPreferencesWindowLevelTab');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Window Level Presets Tab'
|
||||
);
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
@@ -100,8 +111,7 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
});
|
||||
|
||||
it('checks if user can restore to default the language selection and application will be in English', function() {
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.get('@userPreferencesGeneralTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
@@ -110,7 +120,7 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to general tab
|
||||
cy.changePreferencesTab('@userPreferencesGeneralTab');
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
@@ -136,8 +146,7 @@ describe('Visual Regression - OHIF User Preferences', () => {
|
||||
|
||||
it('checks new hotkeys for "Next" and "Previous" Image on Viewport', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.changePreferencesTab('@userPreferencesHotkeysTab');
|
||||
cy.get('@userPreferencesHotkeysTab').should('have.class', 'active');
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab');
|
||||
|
||||
// Set new hotkey for 'Next Image Viewport' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
|
||||
@@ -84,6 +84,7 @@ export function initPreferencesModalAliases() {
|
||||
cy.get('.OHIFModal').as('preferencesModal');
|
||||
cy.get('[data-cy="hotkeys"]').as('userPreferencesHotkeysTab');
|
||||
cy.get('[data-cy="general"]').as('userPreferencesGeneralTab');
|
||||
cy.get('[data-cy="window-level"]').as('userPreferencesWindowLevelTab');
|
||||
initPreferencesModalFooterBtnAliases();
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ Cypress.Commands.add('waitVTKReformatting', () => {
|
||||
|
||||
Cypress.Commands.add('waitViewportImageLoading', () => {
|
||||
// Wait for finish loading
|
||||
cy.get('[data-cy="viewprt-grid"]', { timeout: 20000 }).should($grid => {
|
||||
cy.get('[data-cy="viewprt-grid"]', { timeout: 30000 }).should($grid => {
|
||||
expect($grid).not.to.contain.text('Load');
|
||||
});
|
||||
});
|
||||
@@ -466,21 +466,54 @@ Cypress.Commands.add('openPreferences', () => {
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('changePreferencesTab', tabAlias => {
|
||||
Cypress.Commands.add('selectPreferencesTab', tabAlias => {
|
||||
cy.initPreferencesModalAliases();
|
||||
cy.get(tabAlias).click();
|
||||
cy.get(tabAlias)
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
initPreferencesModalFooterBtnAliases();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('resetUserHoktkeyPreferences', () => {
|
||||
Cypress.Commands.add('resetUserHotkeyPreferences', () => {
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
|
||||
cy.initPreferencesModalAliases();
|
||||
cy.selectPreferencesTab('@userPreferencesHotkeysTab').then(() => {
|
||||
cy.log('Reset Hotkeys to Default Preferences');
|
||||
cy.get('@restoreBtn').click();
|
||||
});
|
||||
|
||||
cy.log('Reset to Default Preferences');
|
||||
cy.get('@restoreBtn').click();
|
||||
cy.get('@saveBtn').click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon')
|
||||
.first()
|
||||
.click({ force: true });
|
||||
}
|
||||
// Click on Save Button
|
||||
cy.get('@saveBtn').click();
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('resetUserGeneralPreferences', () => {
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab').then(() => {
|
||||
cy.log('Reset Language to Default Preferences');
|
||||
cy.get('@restoreBtn').click();
|
||||
});
|
||||
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon')
|
||||
.first()
|
||||
.click({ force: true });
|
||||
}
|
||||
// Click on Save Button
|
||||
cy.get('@saveBtn').click();
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
@@ -499,6 +532,37 @@ Cypress.Commands.add(
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add(
|
||||
'setWindowLevelPreset',
|
||||
(preset_index, description_value, window_value, level_value) => {
|
||||
let index = parseInt(preset_index) + 1;
|
||||
|
||||
// Set new Description value
|
||||
cy.get(':nth-child(' + index + ') > .description > .preferencesInput')
|
||||
.clear()
|
||||
.type(description_value, {
|
||||
force: true,
|
||||
})
|
||||
.blur();
|
||||
|
||||
// Set new Window value
|
||||
cy.get(':nth-child(' + index + ') > .window > .preferencesInput')
|
||||
.clear()
|
||||
.type(window_value, {
|
||||
force: true,
|
||||
})
|
||||
.blur();
|
||||
|
||||
// Set new Level value
|
||||
cy.get(':nth-child(' + index + ') > .level > .preferencesInput')
|
||||
.clear()
|
||||
.type(level_value, {
|
||||
force: true,
|
||||
})
|
||||
.blur();
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add('openDownloadImageModal', () => {
|
||||
// Click on More button
|
||||
cy.get('[data-cy="more"]')
|
||||
@@ -513,12 +577,8 @@ Cypress.Commands.add('openDownloadImageModal', () => {
|
||||
|
||||
Cypress.Commands.add('setLanguage', (language, save = true) => {
|
||||
cy.openPreferences();
|
||||
|
||||
cy.get('@userPreferencesGeneralTab')
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
initPreferencesModalFooterBtnAliases();
|
||||
cy.initPreferencesModalAliases();
|
||||
cy.selectPreferencesTab('@userPreferencesGeneralTab');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
@@ -526,8 +586,18 @@ Cypress.Commands.add('setLanguage', (language, save = true) => {
|
||||
// Select Language and Save/Cancel
|
||||
cy.get('#language-select').select(language);
|
||||
|
||||
const toClick = save ? '@saveBtn' : '@cancelBtn';
|
||||
cy.get(toClick)
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Close Success Message overlay (if displayed)
|
||||
cy.get('body').then(body => {
|
||||
if (body.find('.sb-closeIcon').length > 0) {
|
||||
cy.get('.sb-closeIcon')
|
||||
.first()
|
||||
.click({ force: true });
|
||||
}
|
||||
|
||||
//Click on Save/Cancel button
|
||||
const toClick = save ? '@saveBtn' : '@cancelBtn';
|
||||
cy.get(toClick)
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "3.8.3",
|
||||
"version": "3.8.13",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -48,14 +48,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^2.6.0",
|
||||
"@ohif/core": "^2.6.5",
|
||||
"@ohif/extension-cornerstone": "^2.5.0",
|
||||
"@ohif/extension-dicom-html": "^1.1.1",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.7",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.8",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.2",
|
||||
"@ohif/extension-dicom-segmentation": "^0.1.0",
|
||||
"@ohif/extension-dicom-segmentation": "^0.1.1",
|
||||
"@ohif/extension-lesion-tracker": "^0.2.0",
|
||||
"@ohif/extension-vtk": "^1.3.1",
|
||||
"@ohif/extension-vtk": "^1.3.6",
|
||||
"@ohif/i18n": "^0.52.7",
|
||||
"@ohif/ui": "^1.4.1",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
|
||||
@@ -103,6 +103,8 @@ export default function jumpToRowItem(
|
||||
displaySet.frameIndex = data.frameIndex;
|
||||
}
|
||||
|
||||
viewportIndex =
|
||||
(viewportIndex + viewportsState.activeViewportIndex) % numViewports;
|
||||
viewportSpecificData.push({
|
||||
viewportIndex,
|
||||
displaySet,
|
||||
|
||||
@@ -2,15 +2,20 @@ import ViewportGrid from './ViewportGrid.js';
|
||||
import { MODULE_TYPES } from '@ohif/core';
|
||||
import { connect } from 'react-redux';
|
||||
import { extensionManager } from './../../App.js';
|
||||
import memoize from 'lodash/memoize';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const getAvailableViewportModules = memoize(viewportModules => {
|
||||
const availableViewportModules = {};
|
||||
const viewportModules = extensionManager.modules[MODULE_TYPES.VIEWPORT];
|
||||
|
||||
viewportModules.forEach(moduleDefinition => {
|
||||
availableViewportModules[moduleDefinition.extensionId] =
|
||||
moduleDefinition.module;
|
||||
});
|
||||
return availableViewportModules;
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const viewportModules = extensionManager.modules[MODULE_TYPES.VIEWPORT];
|
||||
const availableViewportModules = getAvailableViewportModules(viewportModules);
|
||||
|
||||
// TODO: Use something like state.plugins.defaultPlugin[MODULE_TYPES.VIEWPORT]
|
||||
let defaultPlugin;
|
||||
|
||||
@@ -11,7 +11,7 @@ import EmptyViewport from './EmptyViewport.js';
|
||||
|
||||
const { loadAndCacheDerivedDisplaySets } = utils;
|
||||
|
||||
const ViewportGrid = function(props) {
|
||||
const ViewportGrid = function (props) {
|
||||
const {
|
||||
activeViewportIndex,
|
||||
availablePlugins,
|
||||
@@ -37,68 +37,70 @@ const ViewportGrid = function(props) {
|
||||
viewportData.forEach(displaySet => {
|
||||
loadAndCacheDerivedDisplaySets(displaySet, studies);
|
||||
});
|
||||
}, [viewportData]);
|
||||
}, [studies, viewportData]);
|
||||
|
||||
const ViewportPanes = layout.viewports.map((layout, viewportIndex) => {
|
||||
const displaySet = viewportData[viewportIndex];
|
||||
const getViewportPanes = () =>
|
||||
layout.viewports.map((layout, viewportIndex) => {
|
||||
const displaySet = viewportData[viewportIndex];
|
||||
|
||||
if (!displaySet) {
|
||||
return null;
|
||||
}
|
||||
if (!displaySet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = {
|
||||
displaySet,
|
||||
studies,
|
||||
};
|
||||
const data = {
|
||||
displaySet,
|
||||
studies,
|
||||
};
|
||||
|
||||
// JAMES TODO:
|
||||
// JAMES TODO:
|
||||
|
||||
// Use whichever plugin is currently in use in the panel
|
||||
// unless nothing is specified. If nothing is specified
|
||||
// and the display set has a plugin specified, use that.
|
||||
//
|
||||
// TODO: Change this logic to:
|
||||
// - Plugins define how capable they are of displaying a SopClass
|
||||
// - When updating a panel, ensure that the currently enabled plugin
|
||||
// in the viewport is capable of rendering this display set. If not
|
||||
// then use the most capable available plugin
|
||||
const pluginName =
|
||||
!layout.plugin && displaySet && displaySet.plugin
|
||||
? displaySet.plugin
|
||||
: layout.plugin;
|
||||
// Use whichever plugin is currently in use in the panel
|
||||
// unless nothing is specified. If nothing is specified
|
||||
// and the display set has a plugin specified, use that.
|
||||
//
|
||||
// TODO: Change this logic to:
|
||||
// - Plugins define how capable they are of displaying a SopClass
|
||||
// - When updating a panel, ensure that the currently enabled plugin
|
||||
// in the viewport is capable of rendering this display set. If not
|
||||
// then use the most capable available plugin
|
||||
const pluginName =
|
||||
!layout.plugin && displaySet && displaySet.plugin
|
||||
? displaySet.plugin
|
||||
: layout.plugin;
|
||||
|
||||
const ViewportComponent = _getViewportComponent(
|
||||
data, // Why do we pass this as `ViewportData`, when that's not really what it is?
|
||||
viewportIndex,
|
||||
children,
|
||||
availablePlugins,
|
||||
pluginName,
|
||||
defaultPluginName
|
||||
);
|
||||
const ViewportComponent = _getViewportComponent(
|
||||
data, // Why do we pass this as `ViewportData`, when that's not really what it is?
|
||||
viewportIndex,
|
||||
children,
|
||||
availablePlugins,
|
||||
pluginName,
|
||||
defaultPluginName
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewportPane
|
||||
onDrop={({
|
||||
viewportIndex,
|
||||
StudyInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
}) => {
|
||||
setViewportData({
|
||||
viewportIndex,
|
||||
StudyInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
});
|
||||
}}
|
||||
viewportIndex={viewportIndex} // Needed by `setViewportData`
|
||||
className={classNames('viewport-container', {
|
||||
active: activeViewportIndex === viewportIndex,
|
||||
})}
|
||||
key={viewportIndex}
|
||||
>
|
||||
{ViewportComponent}
|
||||
</ViewportPane>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<ViewportPane
|
||||
onDrop={setViewportData}
|
||||
viewportIndex={viewportIndex} // Needed by `setViewportData`
|
||||
className={classNames('viewport-container', {
|
||||
active: activeViewportIndex === viewportIndex,
|
||||
})}
|
||||
key={viewportIndex}
|
||||
>
|
||||
{ViewportComponent}
|
||||
</ViewportPane>
|
||||
);
|
||||
});
|
||||
|
||||
const ViewportPanes = React.useMemo(getViewportPanes, [
|
||||
layout,
|
||||
viewportData,
|
||||
studies,
|
||||
children,
|
||||
availablePlugins,
|
||||
defaultPluginName,
|
||||
setViewportData,
|
||||
activeViewportIndex,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -136,12 +136,32 @@ class ToolbarRow extends Component {
|
||||
}
|
||||
|
||||
if (activeContextsChanged) {
|
||||
this.setState({
|
||||
toolbarButtons: _getVisibleToolbarButtons.call(this),
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
toolbarButtons: _getVisibleToolbarButtons.call(this),
|
||||
},
|
||||
this.closeCineDialogIfNotApplicable
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
closeCineDialogIfNotApplicable = () => {
|
||||
const { dialog } = this.props;
|
||||
let { dialogId, activeButtons, toolbarButtons } = this.state;
|
||||
if (dialogId) {
|
||||
const cineButtonPresent = toolbarButtons.find(
|
||||
button => button.options && button.options.behavior === 'CINE'
|
||||
);
|
||||
if (!cineButtonPresent) {
|
||||
dialog.dismiss({ id: dialogId });
|
||||
activeButtons = activeButtons.filter(
|
||||
button => button.options && button.options.behavior !== 'CINE'
|
||||
);
|
||||
this.setState({ dialogId: null, activeButtons });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const buttonComponents = _getButtonComponents.call(
|
||||
this,
|
||||
|
||||
@@ -4,6 +4,10 @@ import { Component } from 'react';
|
||||
import { ConnectedViewportGrid } from './../components/ViewportGrid/index.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import memoize from 'lodash/memoize';
|
||||
import _values from 'lodash/values';
|
||||
|
||||
var values = memoize(_values);
|
||||
|
||||
class ViewerMain extends Component {
|
||||
static propTypes = {
|
||||
@@ -140,9 +144,7 @@ class ViewerMain extends Component {
|
||||
|
||||
render() {
|
||||
const { viewportSpecificData } = this.props;
|
||||
const viewportData = viewportSpecificData
|
||||
? Object.values(viewportSpecificData)
|
||||
: [];
|
||||
const viewportData = values(viewportSpecificData);
|
||||
|
||||
return (
|
||||
<div className="ViewerMain">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
|
||||
export default function initWebWorkers() {
|
||||
let initialized = false;
|
||||
|
||||
export default function initWebWorkers() {
|
||||
const config = {
|
||||
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
|
||||
startWebWorkersOnDemand: true,
|
||||
@@ -13,5 +15,8 @@ export default function initWebWorkers() {
|
||||
},
|
||||
};
|
||||
|
||||
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
|
||||
if (!initialized) {
|
||||
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
@@ -10,3 +10,14 @@ describe('initWebWorkers', () => {
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('initWebWorkers', () => {
|
||||
it("initializes cornerstoneWADOImageLoader's web workers only once", () => {
|
||||
initWebWorkers();
|
||||
initWebWorkers();
|
||||
|
||||
expect(
|
||||
cornerstoneWADOImageLoader.webWorkerManager.initialize
|
||||
).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -9922,6 +9922,11 @@ immer@1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||
|
||||
immer@6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-6.0.2.tgz#5bc08dc4930c756d0749533a2afbd88c8de0cd19"
|
||||
integrity sha512-56CMvUMZl4kkWJFFUe1TjBgGbyb9ibzpLyHD+RSKSVdytuDXgT/HXO1S+GJVywMVl5neGTdAogoR15eRVEd10Q==
|
||||
|
||||
immutable@>=3.6.0:
|
||||
version "3.8.2"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||
|
||||
Reference in new issue
Block a user