From e545dd5b28c918a42514681aed0c7aca6b441f2c Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Fri, 15 Jan 2021 09:53:32 -0300 Subject: [PATCH 01/10] Add funding acknowledgment of the IDC project --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6b9915ba2..11827e7e3 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,7 @@ window.config = { }; ``` -- Install the viewer: - `window.OHIFViewer.installViewer(window.config);` +- Install the viewer: `window.OHIFViewer.installViewer(window.config);` This exact setup is demonstrated in this [CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our @@ -249,6 +248,9 @@ To acknowledge the OHIF Viewer in an academic publication, please cite > > _Cancer Research_, November 1 2017 (77) (21) e119-e122 DOI: > [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334) +> +> NCI Imaging Data Commons: contract number 19X037Q from Leidos Biomedical +> Research under Task Order HHSN26100071 from NCI. **Note:** If you use or find this repository helpful, please take the time to star this repository on Github. This is an easy way for us to assess adoption From 00b5cccae51a1393f9f9994505b30592ad87a27b Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Fri, 15 Jan 2021 15:19:13 -0300 Subject: [PATCH 02/10] Add error message on microscopy viewport errors --- .../src/DicomMicroscopyViewport.js | 27 ++++++++++++++----- extensions/dicom-microscopy/src/index.js | 18 ++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js index 2290895dc..37b539ea5 100644 --- a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js +++ b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.js @@ -62,11 +62,24 @@ class DicomMicroscopyViewport extends Component { ); const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer; - this.viewer = new microscopyViewer({ - client: dicomWebClient, - metadata, - retrieveRendered: false, - }); + try { + this.viewer = new microscopyViewer({ + client: dicomWebClient, + metadata, + retrieveRendered: false, + }); + } catch (error) { + console.error('[Microscopy Viewer] Failed to load:', error); + const { UINotificationService } = this.props.servicesManager.services; + if (UINotificationService) { + UINotificationService.show({ + title: 'Microscopy Viewport', + message: + 'Failed to load viewport. Please check that you have hardware acceleration enabled.', + type: 'error', + }); + } + } this.viewer.render({ container }); }); @@ -92,8 +105,8 @@ class DicomMicroscopyViewport extends Component { {this.state.error ? (

{JSON.stringify(this.state.error)}

) : ( -
- )} +
+ )}
); } diff --git a/extensions/dicom-microscopy/src/index.js b/extensions/dicom-microscopy/src/index.js index 514f2ed12..2e535a38b 100644 --- a/extensions/dicom-microscopy/src/index.js +++ b/extensions/dicom-microscopy/src/index.js @@ -6,14 +6,6 @@ const Component = React.lazy(() => { return import('./DicomMicroscopyViewport'); }); -const DicomMicroscopyViewport = props => { - return ( - Loading...
}> - - - ); -}; - export default { /** * Only required property. Should be a unique value across all extensions. @@ -21,8 +13,14 @@ export default { id: 'microscopy', version, - getViewportModule() { - return DicomMicroscopyViewport; + getViewportModule({ servicesManager }) { + return props => { + return ( + Loading...}> + + + ); + }; }, getSopClassHandlerModule() { return DicomMicroscopySopClassHandler; From 00fddc1550a9fa98dc92bd85958c9522fc171413 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Sat, 16 Jan 2021 01:19:12 -0300 Subject: [PATCH 03/10] Improve/harmonize segmentations panel and add bulk visibility toggles --- .../components/PanelSection/PanelSection.css | 4 +- .../src/components/RTPanel/RTPanel.css | 9 +- .../src/components/RTPanel/RTPanel.js | 129 +-- .../src/components/SegmentItem/SegmentItem.js | 20 +- .../SegmentationPanel/SegmentationPanel.css | 69 +- .../SegmentationPanel/SegmentationPanel.js | 805 ++++++++++-------- .../src/getOHIFDicomSegSopClassHandler.js | 50 +- .../src/loadSegmentation.js | 8 +- .../src/utils/setActiveLabelMap.js | 18 +- 9 files changed, 646 insertions(+), 466 deletions(-) diff --git a/extensions/dicom-rt/src/components/PanelSection/PanelSection.css b/extensions/dicom-rt/src/components/PanelSection/PanelSection.css index 73380bd3d..b485b5318 100644 --- a/extensions/dicom-rt/src/components/PanelSection/PanelSection.css +++ b/extensions/dicom-rt/src/components/PanelSection/PanelSection.css @@ -39,6 +39,8 @@ border-radius: 5px; } -.icons .angle-double-down, .icons .angle-double-up { +.icons .angle-double-down, +.icons .angle-double-up { margin-left: 15px; + margin-right: -5px; } diff --git a/extensions/dicom-rt/src/components/RTPanel/RTPanel.css b/extensions/dicom-rt/src/components/RTPanel/RTPanel.css index 6855f7c58..8dbdd1cca 100644 --- a/extensions/dicom-rt/src/components/RTPanel/RTPanel.css +++ b/extensions/dicom-rt/src/components/RTPanel/RTPanel.css @@ -9,16 +9,21 @@ .dcmrt-panel .dcmrt-panel-header { display: flex; - flex-direction: row; + flex-direction: column; justify-content: space-between; color: white; } +.dcmrt-panel .dcmrt-panel-header h3 { + margin-top: 0; +} + .dcmrt-panel .dcmrt-panel-header .cog-icon { align-self: flex-end; color: var(--default-color); cursor: pointer; - margin-bottom: 20px; + min-height: 23px; + max-height: 23px; } .dcmrt-panel .dcmrt-panel-header .cog-icon:hover { diff --git a/extensions/dicom-rt/src/components/RTPanel/RTPanel.js b/extensions/dicom-rt/src/components/RTPanel/RTPanel.js index 5d9c9b98b..0b0144260 100644 --- a/extensions/dicom-rt/src/components/RTPanel/RTPanel.js +++ b/extensions/dicom-rt/src/components/RTPanel/RTPanel.js @@ -44,7 +44,7 @@ const RTPanel = ({ activeContexts = [], contexts = {}, activeViewport, - getActiveViewport + getActiveViewport, }) => { const isVTK = () => activeContexts.includes(contexts.VTK); const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE); @@ -55,7 +55,7 @@ const RTPanel = ({ referencedDisplaysets: [], sets: [], selectedSet: null, - isLocked: false + isLocked: false, }; const [state, setState] = useState(DEFAULT_STATE); @@ -84,12 +84,17 @@ const RTPanel = ({ document.addEventListener('extensiondicomrtrtloaded', updateStructureSets); return () => { - document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets); + document.removeEventListener( + 'extensiondicomrtrtloaded', + updateStructureSets + ); }; }, []); useEffect(() => { - const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID); + const studyMetadata = studyMetadataManager.get( + activeViewport.StudyInstanceUID + ); const referencedDisplaysets = studyMetadata.getDerivedDatasets({ referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID, Modality: 'RTSTRUCT', @@ -235,7 +240,7 @@ const RTPanel = ({ return (
-

RT Structure Sets

+ {' '} {!state.isLocked && ( setShowSettings(true)} /> )} +

RT Structure Sets

- {!state.isLocked && !state.referencedDisplaysets.length && } - {state.sets && state.referencedDisplaysets.map(displaySet => { - const { SeriesInstanceUID, metadata, isLoaded } = displaySet; + {!state.isLocked && !state.referencedDisplaysets.length && ( + + )} + {state.sets && + state.referencedDisplaysets.map(displaySet => { + const { SeriesInstanceUID, metadata, isLoaded } = displaySet; - const module = cornerstoneTools.getModule('rtstruct'); - const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( - activeViewport.SeriesInstanceUID - ); + const module = cornerstoneTools.getModule('rtstruct'); + const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( + activeViewport.SeriesInstanceUID + ); - const loadedSet = sets.find( - set => set.SeriesInstanceUID === SeriesInstanceUID - ); + const loadedSet = sets.find( + set => set.SeriesInstanceUID === SeriesInstanceUID + ); - if (!loadedSet) return null; + if (!loadedSet) return null; - return ( - { - const module = cornerstoneTools.getModule('rtstruct'); - - if (newVisibility) { - module.setters.showStructureSet(loadedSet.SeriesInstanceUID); - } else { - module.setters.hideStructureSet(loadedSet.SeriesInstanceUID); + return ( + ({ ...state, sets })); - refreshViewport(); - }} - onExpandChange={async () => { - if (!isLoaded) { - await displaySet.load(activeViewport, studies); + onVisibilityChange={newVisibility => { const module = cornerstoneTools.getModule('rtstruct'); + + if (newVisibility) { + module.setters.showStructureSet(loadedSet.SeriesInstanceUID); + } else { + module.setters.hideStructureSet(loadedSet.SeriesInstanceUID); + } const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( activeViewport.SeriesInstanceUID ); - const selectedSet = sets.find( - set => set.SeriesInstanceUID === SeriesInstanceUID - ); - setState(state => ({ ...state, selectedSet, sets })); - } - }} - > - - - {isLoaded && - loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))} - - - - ); - })} + setState(state => ({ ...state, sets })); + refreshViewport(); + }} + onExpandChange={async () => { + if (!isLoaded) { + await displaySet.load(activeViewport, studies); + const module = cornerstoneTools.getModule('rtstruct'); + const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( + activeViewport.SeriesInstanceUID + ); + const selectedSet = sets.find( + set => set.SeriesInstanceUID === SeriesInstanceUID + ); + setState(state => ({ ...state, selectedSet, sets })); + } + }} + > + + + {isLoaded && + loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))} + + + + ); + })}
); }; diff --git a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js index 84c7860a5..677b10da9 100644 --- a/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js +++ b/extensions/dicom-segmentation/src/components/SegmentItem/SegmentItem.js @@ -24,7 +24,7 @@ const SegmentItem = ({ onClick, itemClass, color, - visible = true, + visible, onVisibilityChange, }) => { const [isVisible, setIsVisible] = useState(visible); @@ -33,6 +33,15 @@ const SegmentItem = ({ setIsVisible(visible); }, [visible]); + const onClickHandler = () => onClick(index); + + const onVisibilityChangeHandler = event => { + event.stopPropagation(); + const newVisibility = !isVisible; + setIsVisible(newVisibility); + onVisibilityChange(newVisibility, index); + }; + return (
} itemMetaClass="segment-color-section" - onItemClick={onClick} + onItemClick={onClickHandler} >
@@ -63,12 +72,7 @@ const SegmentItem = ({ name={isVisible ? 'eye' : 'eye-closed'} width="20px" height="20px" - onClick={event => { - event.stopPropagation(); - const newVisibility = !isVisible; - setIsVisible(newVisibility); - onVisibilityChange(newVisibility); - }} + onClick={onVisibilityChangeHandler} />
{false &&
{'...'}
} diff --git a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.css b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.css index d22eafa19..6f22c392d 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.css +++ b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.css @@ -6,6 +6,7 @@ width: 100%; display: flex; flex-direction: column; + padding: 20px; } .dcmseg-segmentation-panel.disabled { @@ -14,19 +15,19 @@ } .dcmseg-segmentation-panel h3 { - padding-left: 20px; margin-top: 0; } .dcmseg-segmentation-panel .segmentations { - padding: 0 20px 20px 20px; + padding-bottom: 10px; } .dcmseg-segmentation-panel .cog-icon { - margin: 20px 20px 0 0; align-self: flex-end; color: var(--default-color); cursor: pointer; + min-height: 23px; + max-height: 23px; } .dcmseg-segmentation-panel .cog-icon:hover { @@ -48,11 +49,11 @@ height: 150px; } -.dcmseg-segmentation-panel .selector-form>div { +.dcmseg-segmentation-panel .selector-form > div { width: 100%; } -.dcmseg-segmentation-panel .selector-form>div:first-of-type { +.dcmseg-segmentation-panel .selector-form > div:first-of-type { display: flex; flex-direction: row; justify-content: center; @@ -64,3 +65,61 @@ flex: unset; margin-right: 16px; } + +.SegmentsSection { + display: flex; + flex-direction: column; + color: var(--text-primary-color); + font-size: 12px; + margin-bottom: 0px; + height: 100%; +} + +.SegmentsSection .header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + background-color: var(--ui-gray-dark); + border-radius: 5px; + font-weight: bold; + padding: 10px 15px; +} + +.SegmentsSection .count { + font-size: 15px; + color: var(--active-color); + margin-left: 15px; + border-radius: 5px; + background-color: var(--ui-gray-darker); + text-align: center; + min-width: 25px; +} + +.SegmentsSection .header .eye-icon, +.SegmentsSection .header .angle-double-down, +.SegmentsSection .header .angle-double-up { + cursor: pointer; + color: var(--active-color); +} + +.SegmentsSection .header .eye-icon:hover, +.SegmentsSection .header .angle-double-down:hover, +.SegmentsSection .header .angle-double-up:hover { + color: var(--hover-color); +} + +.SegmentsSection .header .eye-icon.expanded { + color: var(--default-color); +} + +.SegmentsSection .header .icons { + display: flex; + align-items: center; + border-radius: 5px; +} + +.SegmentsSection .header .icons .angle-double-down, +.SegmentsSection .header .icons .angle-double-up { + margin-left: 15px; +} diff --git a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js index 1cd292442..92d3551c0 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js +++ b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js @@ -1,8 +1,9 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import cornerstoneTools from 'cornerstone-tools'; import cornerstone from 'cornerstone-core'; import moment from 'moment'; +import classNames from 'classnames'; import { utils, log } from '@ohif/core'; import { ScrollableArea, TableList, Icon } from '@ohif/ui'; import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool'; @@ -67,29 +68,104 @@ const SegmentationPanel = ({ const [state, setState] = useState({ brushRadius: DEFAULT_BRUSH_RADIUS, brushColor: 'rgba(221, 85, 85, 1)', - selectedSegment: null, - selectedSegmentation: null, - showSegmentationSettings: false, - brushStackState: null, - labelmapList: [], + selectedSegment: 0, + selectedSegmentation: 0, + showSettings: false, + labelMapList: [], segmentList: [], - cachedSegmentsProperties: [], + segmentsHidden: [], + segmentNumbers: [], isLoading: false, isDisabled: true, }); + const getActiveViewport = () => viewports[activeIndex]; + + const getFirstImageId = () => { + const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport(); + const studyMetadata = studyMetadataManager.get(StudyInstanceUID); + return studyMetadata.getFirstImageId(displaySetInstanceUID); + }; + + const getActiveLabelMaps3D = () => { + const { labelmaps3D, activeLabelmapIndex } = getBrushStackState(); + return labelmaps3D[activeLabelmapIndex]; + }; + + const getActiveLabelMapIndex = () => { + const { activeLabelmapIndex } = getBrushStackState(); + return activeLabelmapIndex; + }; + + const getActiveSegmentIndex = () => { + const { activeSegmentIndex } = getActiveLabelMaps3D(); + return activeSegmentIndex; + }; + + const getActiveLabelMaps2D = () => { + const { labelmaps2D } = getActiveLabelMaps3D(); + return labelmaps2D; + }; + + const getCurrentDisplaySet = () => { + const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport(); + const studyMetadata = studyMetadataManager.get(StudyInstanceUID); + const allDisplaySets = studyMetadata.getDisplaySets(); + return allDisplaySets.find( + ds => ds.displaySetInstanceUID === displaySetInstanceUID + ); + }; + + const setActiveSegment = segmentIndex => { + const activeSegmentIndex = getActiveSegmentIndex(); + const activeViewport = getActiveViewport(); + + if (segmentIndex === activeSegmentIndex) { + log.info(`${activeSegmentIndex} is already the active segment`); + return; + } + + const labelmap3D = getActiveLabelMaps3D(); + labelmap3D.activeSegmentIndex = segmentIndex; + + /** + * Activates the correct label map if clicked segment + * does not belong to the active labelmap + */ + const { StudyInstanceUID } = activeViewport; + const studyMetadata = studyMetadataManager.get(StudyInstanceUID); + const allDisplaySets = studyMetadata.getDisplaySets(); + let selectedSegmentation; + let newLabelmapIndex = getActiveLabelMapIndex(); + allDisplaySets.forEach(displaySet => { + if (displaySet.labelmapSegments) { + Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => { + if ( + displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex) + ) { + newLabelmapIndex = labelmapIndex; + selectedSegmentation = + displaySet.hasOverlapping === true + ? displaySet.originLabelMapIndex + : labelmapIndex; + } + }); + } + }); + + const brushStackState = getBrushStackState(); + brushStackState.activeLabelmapIndex = newLabelmapIndex; + setState(state => ({ ...state, selectedSegmentation })); + + refreshViewports(); + + return segmentIndex; + }; + useEffect(() => { const labelmapModifiedHandler = event => { log.warn('Segmentation Panel: labelmap modified', event); - const module = cornerstoneTools.getModule('segmentation'); - const activeViewport = viewports[activeIndex]; - const studyMetadata = studyMetadataManager.get( - activeViewport.StudyInstanceUID - ); - const firstImageId = studyMetadata.getFirstImageId( - activeViewport.displaySetInstanceUID - ); - updateState('brushStackState', module.state.series[firstImageId]); + refreshSegmentations(); }; /* @@ -140,50 +216,38 @@ const SegmentationPanel = ({ }; }, [activeIndex, viewports]); - const refreshSegmentations = useCallback(() => { - const module = cornerstoneTools.getModule('segmentation'); - const activeViewport = viewports[activeIndex]; - + const refreshSegmentations = () => { + const activeViewport = getActiveViewport(); const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID; if (!isDisabled) { - const studyMetadata = studyMetadataManager.get( - activeViewport.StudyInstanceUID - ); - const firstImageId = studyMetadata.getFirstImageId( - activeViewport.displaySetInstanceUID - ); - const brushStackState = module.state.series[firstImageId]; + const brushStackState = getBrushStackState(); if (brushStackState) { - const labelmap3D = - brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; - const labelmapList = getLabelmapList( - brushStackState, - firstImageId, - activeViewport, - ); - const segmentList = getSegmentList( - labelmap3D, - firstImageId, - brushStackState, - ); + const labelMapList = getLabelMapList(); + const { + items: segmentList, + numbers: segmentNumbers, + segmentsHidden, + } = getSegmentList(); setState(state => ({ ...state, - brushStackState, - selectedSegmentation: brushStackState.activeLabelmapIndex, - labelmapList, + segmentsHidden, + segmentNumbers, + labelMapList, segmentList, isDisabled, })); } else { setState(state => ({ ...state, - labelmapList: [], + segmentsHidden: [], + segmentNumbers: [], + labelMapList: [], segmentList: [], isDisabled, })); } } - }, [viewports, activeIndex, state.isLoading]); + }; useEffect(() => { refreshSegmentations(); @@ -200,309 +264,277 @@ const SegmentationPanel = ({ useEffect(() => { setState(state => ({ ...state, - showSegmentationSettings: state.showSegmentationSettings && !isOpen, + showSettings: state.showSettings && !isOpen, })); }, [isOpen]); - const getLabelmapList = useCallback( - (brushStackState, firstImageId, activeViewport) => { - /* Get list of SEG labelmaps specific to active viewport (reference series) */ - const referencedSegDisplaysets = _getReferencedSegDisplaysets( - activeViewport.StudyInstanceUID, - activeViewport.SeriesInstanceUID - ); + const getLabelMapList = () => { + const activeViewport = getActiveViewport(); - return referencedSegDisplaysets.map((displaySet, index) => { - const { labelmapIndex, SeriesDate, SeriesTime } = displaySet; + /* Get list of SEG labelmaps specific to active viewport (reference series) */ + const referencedSegDisplaysets = _getReferencedSegDisplaysets( + activeViewport.StudyInstanceUID, + activeViewport.SeriesInstanceUID + ); - /* Map to display representation */ - const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; - const date = moment(dateStr, 'YYYYMMDD:HHmmss'); - const isActiveLabelmap = - labelmapIndex === brushStackState.activeLabelmapIndex; - const displayDate = date.format('ddd, MMM Do YYYY'); - const displayTime = date.format('h:mm:ss a'); - const displayDescription = displaySet.SeriesDescription; + return referencedSegDisplaysets.map((displaySet, index) => { + const { + labelmapIndex, + originLabelMapIndex, + hasOverlapping, + SeriesDate, + SeriesTime, + } = displaySet; - return { - value: labelmapIndex, - title: displayDescription, - description: displayDate, - onClick: async () => { - const activatedLabelmapIndex = await setActiveLabelmap( - activeViewport, - studies, - displaySet, - () => onSelectedSegmentationChange(), - onDisplaySetLoadFailure - ); - updateState('selectedSegmentation', activatedLabelmapIndex); - }, - }; - }); - }, - [studies] - ); + /* Map to display representation */ + const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; + const date = moment(dateStr, 'YYYYMMDD:HHmmss'); + const displayDate = date.format('ddd, MMM Do YYYY'); + const displayDescription = displaySet.SeriesDescription; - const getSegmentList = useCallback( - (labelmap3D, firstImageId, brushStackState) => { - /* - * Newly created segments have no `meta` - * So we instead build a list of all segment indexes in use - * Then find any associated metadata - */ - const uniqueSegmentIndexes = labelmap3D.labelmaps2D - .reduce((acc, labelmap2D) => { - if (labelmap2D) { - const segmentIndexes = labelmap2D.segmentsOnLabelmap; - - for (let i = 0; i < segmentIndexes.length; i++) { - if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) { - acc.push(segmentIndexes[i]); - } - } - } - - return acc; - }, []) - .sort((a, b) => a - b); - - const module = cornerstoneTools.getModule('segmentation'); - const colorLutTable = - 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]; - - const color = colorLutTable[segmentIndex]; - let segmentLabel = '(unlabeled)'; - let segmentNumber = segmentIndex; - - /* Meta */ - if (hasLabelmapMeta) { - const segmentMeta = labelmap3D.metadata.data[segmentIndex]; - - if (segmentMeta) { - segmentNumber = segmentMeta.SegmentNumber; - segmentLabel = segmentMeta.SegmentLabel; - } - } - - const sameSegment = state.selectedSegment === segmentNumber; - const setCurrentSelectedSegment = () => { - const activeViewport = viewports[activeIndex]; - - _setActiveSegment( - firstImageId, - segmentNumber, - labelmap3D.activeSegmentIndex, - activeViewport + return { + value: hasOverlapping === true ? originLabelMapIndex : labelmapIndex, + title: displayDescription, + description: displayDate, + onClick: async () => { + const activatedLabelmapIndex = await setActiveLabelmap( + activeViewport, + studies, + displaySet, + onSelectedSegmentationChange, + onDisplaySetLoadFailure ); - updateState('selectedSegment', sameSegment ? null : segmentNumber); - - const validIndexList = []; - labelmap3D.labelmaps2D.forEach((labelMap2D, index) => { - if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) { - validIndexList.push(index); - } - }); - const avg = array => array.reduce((a, b) => a + b) / array.length; - const average = avg(validIndexList); - const closest = validIndexList.reduce((prev, curr) => { - return Math.abs(curr - average) < Math.abs(prev - average) - ? curr - : prev; - }); - - if (isCornerstone()) { - const enabledElements = cornerstone.getEnabledElements(); - const element = enabledElements[activeIndex].element; - const toolState = cornerstoneTools.getToolState(element, 'stack'); - - if (!toolState) { - return; - } - - const imageIds = toolState.data[0].imageIds; - const imageId = imageIds[closest]; - const frameIndex = imageIds.indexOf(imageId); - - const SOPInstanceUID = cornerstone.metaData.get( - 'SOPInstanceUID', - imageId - ); - const StudyInstanceUID = cornerstone.metaData.get( - 'StudyInstanceUID', - imageId - ); - - DICOMSegTempCrosshairsTool.addCrosshair( - element, - imageId, - segmentNumber - ); - - onSegmentItemClick({ - StudyInstanceUID, - SOPInstanceUID, - frameIndex, - activeViewportIndex: activeIndex, - }); - } - - if (isVTK()) { - const activeViewport = viewports[activeIndex]; - const studyMetadata = studyMetadataManager.get( - activeViewport.StudyInstanceUID - ); - const allDisplaySets = studyMetadata.getDisplaySets(); - const currentDisplaySet = allDisplaySets.find( - displaySet => - displaySet.displaySetInstanceUID === - activeViewport.displaySetInstanceUID - ); - - const frame = labelmap3D.labelmaps2D[closest]; - - onSegmentItemClick({ - studies, - StudyInstanceUID: currentDisplaySet.StudyInstanceUID, - displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID, - SOPClassUID: viewports[activeIndex].sopClassUIDs[0], - SOPInstanceUID: currentDisplaySet.SOPInstanceUID, - segmentNumber, - frameIndex: closest, - frame, - }); - } - }; - - const isSegmentVisible = () => { - return !labelmap3D.segmentsHidden[segmentIndex]; - }; - - const toggleSegmentVisibility = () => { - const segmentsHidden = labelmap3D.segmentsHidden; - segmentsHidden[segmentIndex] = !segmentsHidden[segmentIndex]; - return !segmentsHidden[segmentIndex]; - }; - - const cachedSegmentProperties = - state.cachedSegmentsProperties[segmentNumber]; - let visible = isSegmentVisible(); - if ( - cachedSegmentProperties && - cachedSegmentProperties.visible !== visible - ) { - toggleSegmentVisibility(); - } - - segmentList.push( - setCurrentSelectedSegment()} - label={segmentLabel} - index={segmentNumber} - color={color} - visible={visible} - onVisibilityChange={newVisibility => { - if (isCornerstone()) { - const enabledElements = cornerstone.getEnabledElements(); - const element = enabledElements[activeIndex].element; - module.setters.toggleSegmentVisibility( - element, - segmentNumber, - brushStackState.activeLabelmapIndex - ); - } - - if (isVTK()) { - onSegmentVisibilityChange(segmentNumber, newVisibility); - } - - updateCachedSegmentsProperties(segmentNumber, { - visible: newVisibility, - }); - refreshViewports(); - }} - /> - ); - } - - 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 - */ - }, - [activeIndex, onSegmentItemClick, state.selectedSegment, state.isLoading] - ); - - const updateCachedSegmentsProperties = (segmentNumber, properties) => { - const segmentsProperties = state.cachedSegmentsProperties; - const segmentProperties = state.cachedSegmentsProperties[segmentNumber]; - - segmentsProperties[segmentNumber] = segmentProperties - ? { ...segmentProperties, ...properties } - : properties; - - updateState('cachedSegmentsProperties', segmentsProperties); + setState(state => ({ + ...state, + selectedSegmentation: activatedLabelmapIndex, + })); + }, + }; + }); }; - useEffect(() => { - updateState('cachedSegmentsProperties', []); - }, [activeContexts]); + const setCurrentSelectedSegment = segmentNumber => { + setActiveSegment(segmentNumber); - const updateState = (field, value) => { - setState(state => ({ ...state, [field]: value })); + const sameSegment = state.selectedSegment === segmentNumber; + if (!sameSegment) { + setState(state => ({ ...state, selectedSegment: segmentNumber })); + } + + const validIndexList = []; + getActiveLabelMaps2D().forEach((labelMap2D, index) => { + if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) { + validIndexList.push(index); + } + }); + + const avg = array => array.reduce((a, b) => a + b) / array.length; + const average = avg(validIndexList); + const closest = validIndexList.reduce((prev, curr) => { + return Math.abs(curr - average) < Math.abs(prev - average) ? curr : prev; + }); + + if (isCornerstone()) { + const element = getEnabledElement(); + const toolState = cornerstoneTools.getToolState(element, 'stack'); + + if (!toolState) return; + + const imageIds = toolState.data[0].imageIds; + const imageId = imageIds[closest]; + const frameIndex = imageIds.indexOf(imageId); + + const SOPInstanceUID = cornerstone.metaData.get( + 'SOPInstanceUID', + imageId + ); + const StudyInstanceUID = cornerstone.metaData.get( + 'StudyInstanceUID', + imageId + ); + + DICOMSegTempCrosshairsTool.addCrosshair(element, imageId, segmentNumber); + + onSegmentItemClick({ + StudyInstanceUID, + SOPInstanceUID, + frameIndex, + activeViewportIndex: activeIndex, + }); + } + + if (isVTK()) { + const labelMaps3D = getActiveLabelMaps3D(); + const currentDisplaySet = getCurrentDisplaySet(); + const frame = labelMaps3D.labelmaps2D[closest]; + + onSegmentItemClick({ + studies, + StudyInstanceUID: currentDisplaySet.StudyInstanceUID, + displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID, + SOPClassUID: getActiveViewport().sopClassUIDs[0], + SOPInstanceUID: currentDisplaySet.SOPInstanceUID, + segmentNumber, + frameIndex: closest, + frame, + }); + } + }; + + const getColorLUTTable = () => { + const { state } = cornerstoneTools.getModule('segmentation'); + const { colorLUTIndex } = getActiveLabelMaps3D(); + return state.colorLutTables[colorLUTIndex]; + }; + + const getEnabledElement = () => { + const enabledElements = cornerstone.getEnabledElements(); + return enabledElements[activeIndex].element; + }; + + const onSegmentVisibilityChangeHandler = (isVisible, segmentNumber) => { + const labelmap3D = getActiveLabelMaps3D(); + + if (isCornerstone()) { + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; + } + + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } + + setState(state => ({ + ...state, + segmentsHidden: labelmap3D.segmentsHidden, + })); + + refreshSegmentations(); + refreshViewports(); + }; + + const getSegmentList = () => { + /* + * Newly created segments have no `meta` + * So we instead build a list of all segment indexes in use + * Then find any associated metadata + */ + const uniqueSegmentIndexes = getActiveLabelMaps2D() + .reduce((acc, labelmap2D) => { + if (labelmap2D) { + const segmentIndexes = labelmap2D.segmentsOnLabelmap; + + for (let i = 0; i < segmentIndexes.length; i++) { + if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) { + acc.push(segmentIndexes[i]); + } + } + } + + return acc; + }, []) + .sort((a, b) => a - b); + + const labelmap3D = getActiveLabelMaps3D(); + const colorLutTable = getColorLUTTable(); + const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data; + + const segmentList = []; + const segmentNumbers = []; + for (let i = 0; i < uniqueSegmentIndexes.length; i++) { + const segmentIndex = uniqueSegmentIndexes[i]; + + const color = colorLutTable[segmentIndex]; + let segmentLabel = '(unlabeled)'; + let segmentNumber = segmentIndex; + + /* Meta */ + if (hasLabelmapMeta) { + const segmentMeta = labelmap3D.metadata.data[segmentIndex]; + if (segmentMeta) { + segmentNumber = segmentMeta.SegmentNumber; + segmentLabel = segmentMeta.SegmentLabel; + } + } + + const sameSegment = state.selectedSegment === segmentNumber; + + segmentNumbers.push(segmentNumber); + segmentList.push( + + ); + } + + return { + items: segmentList, + numbers: segmentNumbers, + segmentsHidden: labelmap3D.segmentsHidden, + }; + + /* + * 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 updateBrushSize = evt => { const updatedRadius = Number(evt.target.value); if (updatedRadius !== brushRadius) { - updateState('brushRadius', updatedRadius); + setState(state => ({ ...state, brushRadius: updatedRadius })); const module = cornerstoneTools.getModule('segmentation'); module.setters.radius(updatedRadius); } }; const decrementSegment = event => { + const activeSegmentIndex = getActiveSegmentIndex(); event.preventDefault(); - if (labelmap3D.activeSegmentIndex > 1) { - labelmap3D.activeSegmentIndex--; + if (activeSegmentIndex > 1) { + activeSegmentIndex--; } - updateState('selectedSegment', labelmap3D.activeSegmentIndex); + setState(state => ({ ...state, selectedSegment: activeSegmentIndex })); updateActiveSegmentColor(); }; const incrementSegment = event => { + const activeSegmentIndex = getActiveSegmentIndex(); event.preventDefault(); - labelmap3D.activeSegmentIndex++; - updateState('selectedSegment', labelmap3D.activeSegmentIndex); + activeSegmentIndex++; + setState(state => ({ ...state, selectedSegment: activeSegmentIndex })); updateActiveSegmentColor(); }; const updateActiveSegmentColor = () => { const color = getActiveSegmentColor(); - updateState('brushColor', color); + setState(state => ({ ...state, brushColor: color })); + }; + + const getBrushStackState = () => { + const module = cornerstoneTools.getModule('segmentation'); + const firstImageId = getFirstImageId(); + const brushStackState = module.state.series[firstImageId]; + return brushStackState; }; const getActiveSegmentColor = () => { - if (!state.brushStackState) { + const brushStackState = getBrushStackState(); + if (!brushStackState) { return 'rgba(255, 255, 255, 1)'; } - const module = cornerstoneTools.getModule('segmentation'); - const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex]; + const colorLutTable = getColorLUTTable(); const color = colorLutTable[labelmap3D.activeSegmentIndex]; - return `rgba(${color.join(',')})`; }; @@ -520,65 +552,96 @@ const SegmentationPanel = ({ refreshViewports(); }; + const onVisibilityChangeHandler = isVisible => { + const labelmap3D = getActiveLabelMaps3D(); + + state.segmentNumbers.forEach(segmentNumber => { + if (isCornerstone()) { + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; + } + + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } + }); + + setState(state => ({ + ...state, + segmentsHidden: labelmap3D.segmentsHidden, + })); + + refreshSegmentations(); + refreshViewports(); + }; + const disabledConfigurationFields = [ 'outlineAlpha', 'shouldRenderInactiveLabelmaps', ]; - if (state.showSegmentationSettings) { + + const selectedSegmentationOption = state.labelMapList.find( + i => i.value === state.selectedSegmentation + ); + + if (state.showSettings) { return ( updateState('showSegmentationSettings', false)} + onBack={() => setState(state => ({ ...state, showSettings: false }))} onChange={updateConfiguration} /> ); } else { return (
- updateState('showSegmentationSettings', true)} - /> {false && (
)} + setState(state => ({ ...state, showSettings: true }))} + />

Segmentations

i.value === state.selectedSegmentation - )} + value={selectedSegmentationOption} formatOptionLabel={SegmentationItem} - options={state.labelmapList} + options={state.labelMapList} />
- - } - > - {state.segmentList} - - + isHidden === true).length < + state.segmentNumbers.length + } + onVisibilityChange={onVisibilityChangeHandler} + > + + {state.segmentList} + +
); } @@ -613,7 +676,7 @@ SegmentationPanel.propTypes = { SegmentationPanel.defaultProps = {}; /** - * Returns SEG Displaysets that reference the target series, sorted by dateTime + * Returns SEG DisplaySets that reference the target series, sorted by dateTime * * @param {string} StudyInstanceUID * @param {string} SeriesInstanceUID @@ -637,56 +700,48 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => { return referencedDisplaysets; }; -/** - * - * @param {*} firstImageId - * @param {*} activeSegmentIndex - * @returns - */ -const _setActiveSegment = (firstImageId, segmentIndex, activeSegmentIndex, activeViewport) => { - if (segmentIndex === activeSegmentIndex) { - log.info(`${activeSegmentIndex} is already the active segment`); - return; - } +const SegmentsSection = ({ + count, + children, + isVisible: defaultVisibility, + onVisibilityChange, +}) => { + const [isVisible, setIsVisible] = useState(defaultVisibility); - const { state } = cornerstoneTools.getModule('segmentation'); - const brushStackState = state.series[firstImageId]; + const onVisibilityChangeHandler = () => { + const newVisibility = !isVisible; + setIsVisible(newVisibility); + onVisibilityChange(newVisibility); + }; - const labelmap3D = - brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; - labelmap3D.activeSegmentIndex = segmentIndex; + useEffect(() => { + setIsVisible(defaultVisibility); + }, [defaultVisibility]); - /** - * Activates the correct label map if clicked segment - * does not belong to the active labelmap - */ - const { StudyInstanceUID } = activeViewport; - const studyMetadata = studyMetadataManager.get(StudyInstanceUID); - const allDisplaySets = studyMetadata.getDisplaySets(); - let newLabelmapIndex = brushStackState.activeLabelmapIndex; - allDisplaySets.forEach(displaySet => { - if (displaySet.labelmapSegments) { - Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => { - if (displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex)) { - newLabelmapIndex = labelmapIndex; - } - }); - } - }); - - brushStackState.activeLabelmapIndex = newLabelmapIndex; - - refreshViewports(); - return segmentIndex; -}; - -const SegmentsHeader = ({ count }) => { return ( - -
Segments
-
{count}
-
+
+
+
Segments
+
+ +
{count}
+
+
+ {children} +
); }; +const noop = () => {}; + +SegmentsSection.defaultProps = { + onVisibilityChange: noop, +}; + export default SegmentationPanel; diff --git a/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js b/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js index f21c080f4..4d9435893 100644 --- a/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js +++ b/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js @@ -21,7 +21,7 @@ export default function getSopClassHandlerModule({ servicesManager }) { id: 'OHIFDicomSegSopClassHandler', type: MODULE_TYPES.SOP_CLASS_HANDLER, sopClassUIDs, - getDisplaySetFromSeries: function ( + getDisplaySetFromSeries: function( series, study, dicomWebClient, @@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) { referencedDisplaySetUID: null, // Assigned when loaded. labelmapIndex: null, // Assigned when loaded. isLoaded: false, + hasOverlapping: false, SeriesDate, SeriesTime, SeriesNumber, @@ -62,11 +63,11 @@ export default function getSopClassHandlerModule({ servicesManager }) { metadata, }; - segDisplaySet.getSourceDisplaySet = function (studies) { + segDisplaySet.getSourceDisplaySet = function(studies) { return getSourceDisplaySet(studies, segDisplaySet); }; - segDisplaySet.load = async function (referencedDisplaySet, studies) { + segDisplaySet.load = async function(referencedDisplaySet, studies) { segDisplaySet.isLoaded = true; const { StudyInstanceUID } = referencedDisplaySet; const segArrayBuffer = await DicomLoaderService.findDicomDataPromise( @@ -81,7 +82,7 @@ export default function getSopClassHandlerModule({ servicesManager }) { StudyInstanceUID, referencedDisplaySet.SeriesInstanceUID ); - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { let results; try { results = _parseSeg(segArrayBuffer, imageIds); @@ -90,18 +91,47 @@ export default function getSopClassHandlerModule({ servicesManager }) { segDisplaySet.loadError = true; reject(error); } - const { labelmapBufferArray, segMetadata, segmentsOnFrame, segmentsOnFrameArray } = results; + const { + labelmapBufferArray, + segMetadata, + segmentsOnFrame, + segmentsOnFrameArray, + } = results; + let labelmapIndex; if (labelmapBufferArray.length > 1) { + let labelmapIndexes = []; for (let i = 0; i < labelmapBufferArray.length; ++i) { - loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[i], segMetadata, segmentsOnFrame, segmentsOnFrameArray[i]); + labelmapIndexes.push( + await loadSegmentation( + imageIds, + segDisplaySet, + labelmapBufferArray[i], + segMetadata, + segmentsOnFrame, + segmentsOnFrameArray[i] + ) + ); } + /** + * Since overlapping segmentations have virtual labelmaps, + * originLabelMapIndex is used in the panel to select the correct dropdown value. + */ + segDisplaySet.hasOverlapping = true; + segDisplaySet.originLabelMapIndex = labelmapIndexes[0]; + labelmapIndex = labelmapIndexes[0]; + console.warn('Overlapping segmentations!'); } else { - loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[0], segMetadata, segmentsOnFrame, []); + labelmapIndex = await loadSegmentation( + imageIds, + segDisplaySet, + labelmapBufferArray[0], + segMetadata, + segmentsOnFrame, + [] + ); } - segDisplaySet.labelmapIndex = 0; - - resolve(); + resolve(labelmapIndex); }); }; diff --git a/extensions/dicom-segmentation/src/loadSegmentation.js b/extensions/dicom-segmentation/src/loadSegmentation.js index beb4f80ba..a655c0687 100644 --- a/extensions/dicom-segmentation/src/loadSegmentation.js +++ b/extensions/dicom-segmentation/src/loadSegmentation.js @@ -34,7 +34,11 @@ export default async function loadSegmentation( * Cache each labelmap segments. * This data is used to determine the active label map when a given segment is activated/clicked. */ - segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length ? Array.from(new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))) : []; + segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length + ? Array.from( + new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b))) + ) + : []; segDisplaySet.labelmapIndex = labelmapIndex; /* @@ -57,6 +61,8 @@ export default async function loadSegmentation( console.log('Segmentation loaded.'); const event = new CustomEvent('extensiondicomsegmentationsegloaded'); document.dispatchEvent(event); + + return labelmapIndex; } function _getNextLabelmapIndex(firstImageId) { diff --git a/extensions/dicom-segmentation/src/utils/setActiveLabelMap.js b/extensions/dicom-segmentation/src/utils/setActiveLabelMap.js index ec5535b3a..8a08aad10 100644 --- a/extensions/dicom-segmentation/src/utils/setActiveLabelMap.js +++ b/extensions/dicom-segmentation/src/utils/setActiveLabelMap.js @@ -37,9 +37,14 @@ export default async function setActiveLabelmap( ? brushStackState.activeLabelmapIndex : undefined; - if (displaySet.labelmapIndex === activeLabelmapIndex) { + let labelmapIndex = + displaySet.hasOverlapping === true + ? displaySet.originLabelMapIndex + : displaySet.labelmapIndex; + + if (labelmapIndex === activeLabelmapIndex) { log.warn(`${activeLabelmapIndex} is already the active labelmap`); - return displaySet.labelmapIndex; + return labelmapIndex; } if (!displaySet.isLoaded) { @@ -55,13 +60,18 @@ export default async function setActiveLabelmap( await loadPromise; } + labelmapIndex = + displaySet.hasOverlapping === true + ? displaySet.originLabelMapIndex + : displaySet.labelmapIndex; + // This might have just been created, so need to use the non-cached value. state = cornerstoneTools.getModule('segmentation').state; brushStackState = state.series[firstImageId]; - brushStackState.activeLabelmapIndex = displaySet.labelmapIndex; + brushStackState.activeLabelmapIndex = labelmapIndex; refreshViewports(); callback(); - return displaySet.labelmapIndex; + return labelmapIndex; } From 437c01511e6341f718207c2854bff3d02226616e Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Tue, 19 Jan 2021 14:10:40 -0300 Subject: [PATCH 04/10] Toggle visibility for multiple labelmaps (overlapping) --- .../SegmentationPanel/SegmentationPanel.js | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js index 92d3551c0..00e193291 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js +++ b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js @@ -393,20 +393,30 @@ const SegmentationPanel = ({ }; const onSegmentVisibilityChangeHandler = (isVisible, segmentNumber) => { - const labelmap3D = getActiveLabelMaps3D(); + /** Get all labelmaps with this segmentNumber (overlapping segments) */ + const { labelmaps3D } = getBrushStackState(); + const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => { + return labelmaps2D.some(({ segmentsOnLabelmap }) => + segmentsOnLabelmap.includes(segmentNumber) + ); + }); - if (isCornerstone()) { - labelmap3D.segmentsHidden[segmentNumber] = !isVisible; - } + let segmentsHidden = []; + possibleLabelMaps3D.forEach(labelmap3D => { + if (isCornerstone()) { + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; + } - if (isVTK()) { - onSegmentVisibilityChange(segmentNumber, isVisible); - } + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } - setState(state => ({ - ...state, - segmentsHidden: labelmap3D.segmentsHidden, - })); + segmentsHidden = [ + ...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]), + ]; + }); + + setState(state => ({ ...state, segmentsHidden })); refreshSegmentations(); refreshViewports(); @@ -553,22 +563,32 @@ const SegmentationPanel = ({ }; const onVisibilityChangeHandler = isVisible => { - const labelmap3D = getActiveLabelMaps3D(); - + let segmentsHidden = []; state.segmentNumbers.forEach(segmentNumber => { - if (isCornerstone()) { - labelmap3D.segmentsHidden[segmentNumber] = !isVisible; - } + /** Get all labelmaps with this segmentNumber (overlapping segments) */ + const { labelmaps3D } = getBrushStackState(); + const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => { + return labelmaps2D.some(({ segmentsOnLabelmap }) => + segmentsOnLabelmap.includes(segmentNumber) + ); + }); - if (isVTK()) { - onSegmentVisibilityChange(segmentNumber, isVisible); - } + possibleLabelMaps3D.forEach(labelmap3D => { + if (isCornerstone()) { + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; + } + + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } + + segmentsHidden = [ + ...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]), + ]; + }); }); - setState(state => ({ - ...state, - segmentsHidden: labelmap3D.segmentsHidden, - })); + setState(state => ({ ...state, segmentsHidden })); refreshSegmentations(); refreshViewports(); From 449e8f25f6d038c8ad0146b710eeed0231a93ab0 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Tue, 19 Jan 2021 14:22:52 -0300 Subject: [PATCH 05/10] Toggle visibility for cornerstone by default --- .../SegmentationPanel/SegmentationPanel.js | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js index 00e193291..8bd61e3b7 100644 --- a/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js +++ b/extensions/dicom-segmentation/src/components/SegmentationPanel/SegmentationPanel.js @@ -403,13 +403,7 @@ const SegmentationPanel = ({ let segmentsHidden = []; possibleLabelMaps3D.forEach(labelmap3D => { - if (isCornerstone()) { - labelmap3D.segmentsHidden[segmentNumber] = !isVisible; - } - - if (isVTK()) { - onSegmentVisibilityChange(segmentNumber, isVisible); - } + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; segmentsHidden = [ ...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]), @@ -420,6 +414,10 @@ const SegmentationPanel = ({ refreshSegmentations(); refreshViewports(); + + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } }; const getSegmentList = () => { @@ -565,6 +563,10 @@ const SegmentationPanel = ({ const onVisibilityChangeHandler = isVisible => { let segmentsHidden = []; state.segmentNumbers.forEach(segmentNumber => { + if (isVTK()) { + onSegmentVisibilityChange(segmentNumber, isVisible); + } + /** Get all labelmaps with this segmentNumber (overlapping segments) */ const { labelmaps3D } = getBrushStackState(); const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => { @@ -574,14 +576,7 @@ const SegmentationPanel = ({ }); possibleLabelMaps3D.forEach(labelmap3D => { - if (isCornerstone()) { - labelmap3D.segmentsHidden[segmentNumber] = !isVisible; - } - - if (isVTK()) { - onSegmentVisibilityChange(segmentNumber, isVisible); - } - + labelmap3D.segmentsHidden[segmentNumber] = !isVisible; segmentsHidden = [ ...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]), ]; From f07f21117d2766fec8bdf817d7dca3b41117092c Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 20 Jan 2021 18:05:20 -0300 Subject: [PATCH 06/10] Notify user of buffer errors from vtk / add possibility to add actions to notifications --- extensions/vtk/src/OHIFVTKViewport.js | 123 +++++++++++------- extensions/vtk/src/index.js | 10 +- .../services/UINotificationService/index.js | 5 +- .../ui/src/components/snackbar/Snackbar.css | 39 +++++- .../src/components/snackbar/SnackbarItem.js | 7 +- .../src/contextProviders/SnackbarProvider.js | 1 + 6 files changed, 132 insertions(+), 53 deletions(-) diff --git a/extensions/vtk/src/OHIFVTKViewport.js b/extensions/vtk/src/OHIFVTKViewport.js index 19e557143..8dccda2f3 100644 --- a/extensions/vtk/src/OHIFVTKViewport.js +++ b/extensions/vtk/src/OHIFVTKViewport.js @@ -72,7 +72,7 @@ class OHIFVTKViewport extends Component { }; static defaultProps = { - onScroll: () => { }, + onScroll: () => {}, }; static id = 'OHIFVTKViewport'; @@ -138,6 +138,8 @@ class OHIFVTKViewport extends Component { SOPInstanceUID, frameIndex ) => { + const { UINotificationService } = this.props.servicesManager.services; + const stack = OHIFVTKViewport.getCornerstoneStack( studies, StudyInstanceUID, @@ -159,11 +161,14 @@ class OHIFVTKViewport extends Component { const { activeLabelmapIndex } = brushStackState; const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex]; - if (brushStackState.labelmaps3D.length > 1 && this.props.viewportIndex === 0) { - const { UINotificationService } = this.props.servicesManager.services; + if ( + brushStackState.labelmaps3D.length > 1 && + this.props.viewportIndex === 0 + ) { UINotificationService.show({ title: 'Overlapping Segmentation Found', - message: 'Overlapping segmentations cannot be displayed when in MPR mode', + message: + 'Overlapping segmentations cannot be displayed when in MPR mode', type: 'info', }); } @@ -305,52 +310,80 @@ class OHIFVTKViewport extends Component { seriesDescription: displaySet.seriesDescription, }; - const { - imageDataObject, - labelmapDataObject, - labelmapColorLUT, - } = this.getViewportData( - studies, - StudyInstanceUID, - displaySetInstanceUID, - SOPInstanceUID, - frameIndex - ); + try { + const { + imageDataObject, + labelmapDataObject, + labelmapColorLUT, + } = this.getViewportData( + studies, + StudyInstanceUID, + displaySetInstanceUID, + SOPInstanceUID, + frameIndex + ); - this.imageDataObject = imageDataObject; + this.imageDataObject = imageDataObject; - /* TODO: Not currently used until we have drawing tools in vtkjs. - if (!labelmap) { - labelmap = createLabelMapImageData(data); - } */ + /* TODO: Not currently used until we have drawing tools in vtkjs. + if (!labelmap) { + labelmap = createLabelMapImageData(data); + } */ - const volumeActor = this.getOrCreateVolume( - imageDataObject, - displaySetInstanceUID - ); + const volumeActor = this.getOrCreateVolume( + imageDataObject, + displaySetInstanceUID + ); - this.setState( - { - percentComplete: 0, - dataDetails, - }, - () => { - this.loadProgressively(imageDataObject); + this.setState( + { + percentComplete: 0, + dataDetails, + }, + () => { + this.loadProgressively(imageDataObject); - // TODO: There must be a better way to do this. - // We do this so that if all the data is available the react-vtkjs-viewport - // Will render _something_ before the volumes are set and the volume - // Construction that happens in react-vtkjs-viewport locks up the CPU. - setTimeout(() => { - this.setState({ - volumes: [volumeActor], - paintFilterLabelMapImageData: labelmapDataObject, - paintFilterBackgroundImageData: imageDataObject.vtkImageData, - labelmapColorLUT, - }); - }, 200); + // TODO: There must be a better way to do this. + // We do this so that if all the data is available the react-vtkjs-viewport + // Will render _something_ before the volumes are set and the volume + // Construction that happens in react-vtkjs-viewport locks up the CPU. + setTimeout(() => { + this.setState({ + volumes: [volumeActor], + paintFilterLabelMapImageData: labelmapDataObject, + paintFilterBackgroundImageData: imageDataObject.vtkImageData, + labelmapColorLUT, + }); + }, 200); + } + ); + } catch (error) { + const errorTitle = 'Failed to load 2D MPR'; + console.error(errorTitle, error); + const { UINotificationService } = this.props.servicesManager.services; + if (this.props.viewportIndex === 0) { + const message = error.message.includes('buffer') + ? 'Buffer allocation limit exceeded' + : error.message; + console.error(errorTitle, error); + UINotificationService.show({ + title: errorTitle, + message, + type: 'error', + autoClose: false, + /* action: { + label: 'Download', + onClick: () => { + const listOfUIDs = [window.location.href.split('/').pop()]; + this.props.commandsManager.runCommand('downloadAndZip', { + listOfUIDs, + }); + }, + }, */ + }); } - ); + this.setState({ isLoaded: true }); + } } componentDidMount() { @@ -363,7 +396,7 @@ class OHIFVTKViewport extends Component { if ( displaySet.displaySetInstanceUID !== - prevDisplaySet.displaySetInstanceUID || + prevDisplaySet.displaySetInstanceUID || displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID || displaySet.frameIndex !== prevDisplaySet.frameIndex ) { diff --git a/extensions/vtk/src/index.js b/extensions/vtk/src/index.js index dde0d6ce0..18e69c22c 100644 --- a/extensions/vtk/src/index.js +++ b/extensions/vtk/src/index.js @@ -9,7 +9,9 @@ import { version } from '../package.json'; // import loadLocales from './loadLocales'; const OHIFVTKViewport = asyncComponent(() => - retryImport(() => import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js')) + retryImport(() => + import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js') + ) ); const vtkExtension = { @@ -21,7 +23,11 @@ const vtkExtension = { getViewportModule({ commandsManager, servicesManager }) { const ExtendedVTKViewport = props => ( - + ); return withCommandsManager(ExtendedVTKViewport, commandsManager); }, diff --git a/platform/core/src/services/UINotificationService/index.js b/platform/core/src/services/UINotificationService/index.js index 0cdb8ea95..42e4b0512 100644 --- a/platform/core/src/services/UINotificationService/index.js +++ b/platform/core/src/services/UINotificationService/index.js @@ -8,6 +8,7 @@ * @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight" * @property {string} [type="info"] - "info" | "error" | "warning" | "success" * @property {boolean} [autoClose=true] + * @property {object} [action=null] */ const name = 'UINotificationService'; @@ -34,7 +35,7 @@ const serviceImplementation = { * Create and show a new UI notification; returns the * ID of the created notification. * - * @param {Notification} notification { title, message, duration, position, type, autoClose} + * @param {Notification} notification { title, message, duration, position, type, autoClose, action} * @returns {number} id */ function _show({ @@ -44,6 +45,7 @@ function _show({ position = 'bottomRight', type = 'info', autoClose = true, + action = null, }) { return serviceImplementation._show({ title, @@ -52,6 +54,7 @@ function _show({ position, type, autoClose, + action, }); } diff --git a/platform/ui/src/components/snackbar/Snackbar.css b/platform/ui/src/components/snackbar/Snackbar.css index bb99cefe6..8d7399165 100644 --- a/platform/ui/src/components/snackbar/Snackbar.css +++ b/platform/ui/src/components/snackbar/Snackbar.css @@ -156,19 +156,50 @@ transition: all 300ms ease; } -.sb-success { +.sb-item .sb-action, +.sb-error .sb-action, +.sb-warning .sb-action, +.sb-info .sb-action, +.sb-success .sb-action { + margin-top: 10px; + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: none; + color: white; +} + +.sb-success, +.sb-success .sb-action { background-color: var(--snackbar-success); } -.sb-error { +.sb-error, +.sb-error .sb-action { background-color: var(--snackbar-error); } -.sb-warning { +.sb-warning, +.sb-warning .sb-action { background-color: var(--snackbar-warning); } -.sb-info { +.sb-info, +.sb-info .sb-action { background-color: var(--snackbar-info); } diff --git a/platform/ui/src/components/snackbar/SnackbarItem.js b/platform/ui/src/components/snackbar/SnackbarItem.js index 40c43697e..f3d7243eb 100644 --- a/platform/ui/src/components/snackbar/SnackbarItem.js +++ b/platform/ui/src/components/snackbar/SnackbarItem.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; const SnackbarItem = ({ options, onClose }) => { const handleClose = () => { @@ -24,6 +24,11 @@ const SnackbarItem = ({ options, onClose }) => { {options.title &&
{options.title}
} {options.message &&
{options.message}
} + {options.action && ( + + )}
); }; diff --git a/platform/ui/src/contextProviders/SnackbarProvider.js b/platform/ui/src/contextProviders/SnackbarProvider.js index 4b4c6bcbd..86709fedc 100644 --- a/platform/ui/src/contextProviders/SnackbarProvider.js +++ b/platform/ui/src/contextProviders/SnackbarProvider.js @@ -22,6 +22,7 @@ const SnackbarProvider = ({ children, service }) => { autoClose: true, position: 'bottomRight', type: SnackbarTypes.INFO, + action: null, }; const [count, setCount] = useState(1); From 8038009afd6d9596f7055152da0f487b01bfbfe9 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 20 Jan 2021 20:59:03 -0300 Subject: [PATCH 07/10] Add exit mpr action --- extensions/vtk/src/OHIFVTKViewport.js | 17 ++++++++--------- .../ui/src/components/snackbar/SnackbarItem.js | 6 +++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/extensions/vtk/src/OHIFVTKViewport.js b/extensions/vtk/src/OHIFVTKViewport.js index 8dccda2f3..bac284a4e 100644 --- a/extensions/vtk/src/OHIFVTKViewport.js +++ b/extensions/vtk/src/OHIFVTKViewport.js @@ -363,7 +363,7 @@ class OHIFVTKViewport extends Component { const { UINotificationService } = this.props.servicesManager.services; if (this.props.viewportIndex === 0) { const message = error.message.includes('buffer') - ? 'Buffer allocation limit exceeded' + ? 'Dataset is too big to display in MPR' : error.message; console.error(errorTitle, error); UINotificationService.show({ @@ -371,15 +371,14 @@ class OHIFVTKViewport extends Component { message, type: 'error', autoClose: false, - /* action: { - label: 'Download', - onClick: () => { - const listOfUIDs = [window.location.href.split('/').pop()]; - this.props.commandsManager.runCommand('downloadAndZip', { - listOfUIDs, - }); + action: { + label: 'Exit 2D MPR', + onClick: ({ close }) => { + // context: 'ACTIVE_VIEWPORT::VTK', + close(); + this.props.commandsManager.runCommand('setCornerstoneLayout'); }, - }, */ + }, }); } this.setState({ isLoaded: true }); diff --git a/platform/ui/src/components/snackbar/SnackbarItem.js b/platform/ui/src/components/snackbar/SnackbarItem.js index f3d7243eb..2e1d62ccf 100644 --- a/platform/ui/src/components/snackbar/SnackbarItem.js +++ b/platform/ui/src/components/snackbar/SnackbarItem.js @@ -5,6 +5,10 @@ const SnackbarItem = ({ options, onClose }) => { onClose(options.id); }; + const handleClick = () => { + options.action.onClick({ ...options, close: handleClose }); + }; + useEffect(() => { if (options.autoClose) { setTimeout(() => { @@ -25,7 +29,7 @@ const SnackbarItem = ({ options, onClose }) => { {options.title &&
{options.title}
} {options.message &&
{options.message}
} {options.action && ( - )} From 587de94cea9d8d375c7475bdbaa77ca8ddd016e4 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 21 Jan 2021 12:35:41 +0000 Subject: [PATCH 08/10] chore(release): publish [skip ci] - @ohif/extension-vtk@1.10.2 - @ohif/core@2.12.1 - @ohif/ui@1.8.1 - @ohif/viewer@4.8.2 --- extensions/vtk/CHANGELOG.md | 8 ++++++++ extensions/vtk/package.json | 6 +++--- platform/core/CHANGELOG.md | 8 ++++++++ platform/core/package.json | 2 +- platform/ui/CHANGELOG.md | 8 ++++++++ platform/ui/package.json | 2 +- platform/viewer/CHANGELOG.md | 8 ++++++++ platform/viewer/package.json | 8 ++++---- 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/extensions/vtk/CHANGELOG.md b/extensions/vtk/CHANGELOG.md index a6ee3b762..f905f7e3f 100644 --- a/extensions/vtk/CHANGELOG.md +++ b/extensions/vtk/CHANGELOG.md @@ -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.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.1...@ohif/extension-vtk@1.10.2) (2021-01-21) + +**Note:** Version bump only for package @ohif/extension-vtk + + + + + ## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.0...@ohif/extension-vtk@1.10.1) (2020-12-10) diff --git a/extensions/vtk/package.json b/extensions/vtk/package.json index 1b1836659..1b3c8b35a 100644 --- a/extensions/vtk/package.json +++ b/extensions/vtk/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-vtk", - "version": "1.10.1", + "version": "1.10.2", "description": "OHIF extension for VTK.js", "author": "OHIF", "license": "MIT", @@ -53,8 +53,8 @@ "react-vtkjs-viewport": "^0.14.2" }, "devDependencies": { - "@ohif/core": "^2.12.0", - "@ohif/ui": "^1.8.0", + "@ohif/core": "^2.12.1", + "@ohif/ui": "^1.8.1", "cornerstone-tools": "^4.20.1", "cornerstone-wado-image-loader": "^3.1.0", "dicom-parser": "^1.8.3", diff --git a/platform/core/CHANGELOG.md b/platform/core/CHANGELOG.md index afd6f5d5e..bf41447f6 100644 --- a/platform/core/CHANGELOG.md +++ b/platform/core/CHANGELOG.md @@ -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. +## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.0...@ohif/core@2.12.1) (2021-01-21) + +**Note:** Version bump only for package @ohif/core + + + + + # [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10) diff --git a/platform/core/package.json b/platform/core/package.json index 71ad0313a..ba48fd021 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/core", - "version": "2.12.0", + "version": "2.12.1", "description": "Generic business logic for web-based medical imaging applications", "author": "OHIF Core Team", "license": "MIT", diff --git a/platform/ui/CHANGELOG.md b/platform/ui/CHANGELOG.md index 964bae730..5dc90a2dd 100644 --- a/platform/ui/CHANGELOG.md +++ b/platform/ui/CHANGELOG.md @@ -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.8.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.0...@ohif/ui@1.8.1) (2021-01-21) + +**Note:** Version bump only for package @ohif/ui + + + + + # [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.7.1...@ohif/ui@1.8.0) (2020-12-10) diff --git a/platform/ui/package.json b/platform/ui/package.json index cb97e11d3..3fe076c90 100644 --- a/platform/ui/package.json +++ b/platform/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/ui", - "version": "1.8.0", + "version": "1.8.1", "description": "A set of React components for Medical Imaging Viewers", "author": "OHIF Contributors", "license": "MIT", diff --git a/platform/viewer/CHANGELOG.md b/platform/viewer/CHANGELOG.md index d99d6d4c8..9af74c046 100644 --- a/platform/viewer/CHANGELOG.md +++ b/platform/viewer/CHANGELOG.md @@ -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. +## [4.8.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.1...@ohif/viewer@4.8.2) (2021-01-21) + +**Note:** Version bump only for package @ohif/viewer + + + + + ## [4.8.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.0...@ohif/viewer@4.8.1) (2020-12-10) **Note:** Version bump only for package @ohif/viewer diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 7defee060..9c106cbb3 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/viewer", - "version": "4.8.1", + "version": "4.8.2", "description": "OHIF Viewer", "author": "OHIF Contributors", "license": "MIT", @@ -49,7 +49,7 @@ }, "dependencies": { "@babel/runtime": "^7.5.5", - "@ohif/core": "^2.12.0", + "@ohif/core": "^2.12.1", "@ohif/extension-cornerstone": "^2.10.0", "@ohif/extension-debugging": "^0.1.6", "@ohif/extension-dicom-html": "^1.3.0", @@ -58,9 +58,9 @@ "@ohif/extension-dicom-rt": "^0.6.1", "@ohif/extension-dicom-segmentation": "^0.5.0", "@ohif/extension-lesion-tracker": "^0.2.1", - "@ohif/extension-vtk": "^1.10.1", + "@ohif/extension-vtk": "^1.10.2", "@ohif/i18n": "^0.53.0", - "@ohif/ui": "^1.8.0", + "@ohif/ui": "^1.8.1", "@tanem/react-nprogress": "^1.1.25", "classnames": "^2.2.6", "core-js": "^3.2.1", From e0e5f6000f91d1d179bdee38eff4a896e8ef1092 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 21 Jan 2021 13:36:40 +0000 Subject: [PATCH 09/10] chore(release): publish [skip ci] - @ohif/extension-dicom-rt@0.6.2 - @ohif/extension-dicom-segmentation@0.5.1 - @ohif/viewer@4.8.3 --- extensions/dicom-rt/CHANGELOG.md | 8 ++++++++ extensions/dicom-rt/package.json | 2 +- extensions/dicom-segmentation/CHANGELOG.md | 8 ++++++++ extensions/dicom-segmentation/package.json | 2 +- platform/viewer/CHANGELOG.md | 8 ++++++++ platform/viewer/package.json | 6 +++--- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/extensions/dicom-rt/CHANGELOG.md b/extensions/dicom-rt/CHANGELOG.md index 3c73e26b9..00d146b07 100644 --- a/extensions/dicom-rt/CHANGELOG.md +++ b/extensions/dicom-rt/CHANGELOG.md @@ -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.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.1...@ohif/extension-dicom-rt@0.6.2) (2021-01-21) + +**Note:** Version bump only for package @ohif/extension-dicom-rt + + + + + ## [0.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.0...@ohif/extension-dicom-rt@0.6.1) (2020-12-10) diff --git a/extensions/dicom-rt/package.json b/extensions/dicom-rt/package.json index f9830fa42..29db30d25 100644 --- a/extensions/dicom-rt/package.json +++ b/extensions/dicom-rt/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-rt", - "version": "0.6.1", + "version": "0.6.2", "description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.", "author": "OHIF", "license": "MIT", diff --git a/extensions/dicom-segmentation/CHANGELOG.md b/extensions/dicom-segmentation/CHANGELOG.md index ab0c96b03..b53e88db0 100644 --- a/extensions/dicom-segmentation/CHANGELOG.md +++ b/extensions/dicom-segmentation/CHANGELOG.md @@ -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.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.0...@ohif/extension-dicom-segmentation@0.5.1) (2021-01-21) + +**Note:** Version bump only for package @ohif/extension-dicom-segmentation + + + + + # [0.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.7...@ohif/extension-dicom-segmentation@0.5.0) (2020-12-10) diff --git a/extensions/dicom-segmentation/package.json b/extensions/dicom-segmentation/package.json index 5c2c2cf4c..90b50e85a 100644 --- a/extensions/dicom-segmentation/package.json +++ b/extensions/dicom-segmentation/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-segmentation", - "version": "0.5.0", + "version": "0.5.1", "description": "OHIF extension for viewing segmentations in the 2D MPR view", "author": "OHIF", "license": "MIT", diff --git a/platform/viewer/CHANGELOG.md b/platform/viewer/CHANGELOG.md index 9af74c046..0b874ba14 100644 --- a/platform/viewer/CHANGELOG.md +++ b/platform/viewer/CHANGELOG.md @@ -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. +## [4.8.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.2...@ohif/viewer@4.8.3) (2021-01-21) + +**Note:** Version bump only for package @ohif/viewer + + + + + ## [4.8.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.1...@ohif/viewer@4.8.2) (2021-01-21) **Note:** Version bump only for package @ohif/viewer diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 9c106cbb3..c118bc7e8 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/viewer", - "version": "4.8.2", + "version": "4.8.3", "description": "OHIF Viewer", "author": "OHIF Contributors", "license": "MIT", @@ -55,8 +55,8 @@ "@ohif/extension-dicom-html": "^1.3.0", "@ohif/extension-dicom-microscopy": "^0.51.3", "@ohif/extension-dicom-pdf": "^1.0.6", - "@ohif/extension-dicom-rt": "^0.6.1", - "@ohif/extension-dicom-segmentation": "^0.5.0", + "@ohif/extension-dicom-rt": "^0.6.2", + "@ohif/extension-dicom-segmentation": "^0.5.1", "@ohif/extension-lesion-tracker": "^0.2.1", "@ohif/extension-vtk": "^1.10.2", "@ohif/i18n": "^0.53.0", From 4c7b2b4ae0b8ff5a335677b42c3a95e2f476fa9b Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 21 Jan 2021 14:59:06 +0000 Subject: [PATCH 10/10] chore(release): publish [skip ci] - @ohif/extension-dicom-microscopy@0.51.4 - @ohif/viewer@4.8.4 --- extensions/dicom-microscopy/CHANGELOG.md | 8 ++++++++ extensions/dicom-microscopy/package.json | 2 +- platform/viewer/CHANGELOG.md | 8 ++++++++ platform/viewer/package.json | 4 ++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/extensions/dicom-microscopy/CHANGELOG.md b/extensions/dicom-microscopy/CHANGELOG.md index 250d60e35..8a5545395 100644 --- a/extensions/dicom-microscopy/CHANGELOG.md +++ b/extensions/dicom-microscopy/CHANGELOG.md @@ -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.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.3...@ohif/extension-dicom-microscopy@0.51.4) (2021-01-21) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + ## [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 diff --git a/extensions/dicom-microscopy/package.json b/extensions/dicom-microscopy/package.json index bcc44ca03..4259145f3 100644 --- a/extensions/dicom-microscopy/package.json +++ b/extensions/dicom-microscopy/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-microscopy", - "version": "0.51.3", + "version": "0.51.4", "description": "OHIF extension for Dicom Microscopy", "author": "OHIF", "license": "MIT", diff --git a/platform/viewer/CHANGELOG.md b/platform/viewer/CHANGELOG.md index 0b874ba14..797462e0b 100644 --- a/platform/viewer/CHANGELOG.md +++ b/platform/viewer/CHANGELOG.md @@ -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. +## [4.8.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.3...@ohif/viewer@4.8.4) (2021-01-21) + +**Note:** Version bump only for package @ohif/viewer + + + + + ## [4.8.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.2...@ohif/viewer@4.8.3) (2021-01-21) **Note:** Version bump only for package @ohif/viewer diff --git a/platform/viewer/package.json b/platform/viewer/package.json index c118bc7e8..4a7a6221a 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/viewer", - "version": "4.8.3", + "version": "4.8.4", "description": "OHIF Viewer", "author": "OHIF Contributors", "license": "MIT", @@ -53,7 +53,7 @@ "@ohif/extension-cornerstone": "^2.10.0", "@ohif/extension-debugging": "^0.1.6", "@ohif/extension-dicom-html": "^1.3.0", - "@ohif/extension-dicom-microscopy": "^0.51.3", + "@ohif/extension-dicom-microscopy": "^0.51.4", "@ohif/extension-dicom-pdf": "^1.0.6", "@ohif/extension-dicom-rt": "^0.6.2", "@ohif/extension-dicom-segmentation": "^0.5.1",