diff --git a/extensions/cornerstone/src/OHIFCornerstoneViewport.js b/extensions/cornerstone/src/OHIFCornerstoneViewport.js index 92411423b..04b2565cc 100644 --- a/extensions/cornerstone/src/OHIFCornerstoneViewport.js +++ b/extensions/cornerstone/src/OHIFCornerstoneViewport.js @@ -237,7 +237,7 @@ class OHIFCornerstoneViewport extends Component { } }; - const warningsOverlay = props => { + const overlay = props => { const displaySet = this.props.viewportData.displaySet; let filteredSRLabels; if (displaySet.SRLabels && displaySet.SRLabels.length !== 0) { @@ -264,7 +264,7 @@ class OHIFCornerstoneViewport extends Component { imageIdIndex={currentImageIdIndex} onNewImageDebounced={newImageHandler} onNewImageDebounceTime={300} - viewportOverlayComponent={warningsOverlay} + viewportOverlayComponent={overlay} stackPrefetch={this.props.stackPrefetch} isStackPrefetchEnabled={this.props.isStackPrefetchEnabled} // ~~ Connected (From REDUX) diff --git a/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.css b/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.css index 4ea733160..dca6f30d6 100644 --- a/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.css +++ b/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.css @@ -35,7 +35,7 @@ } .OHIFCornerstoneViewportOverlay .bottom-left3 { bottom: 110px; - left: 100px; + left: -20px; } .OHIFCornerstoneViewportOverlay .bottom-right { bottom: 20px; diff --git a/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.js b/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.js index 590040c5f..72a945625 100644 --- a/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.js +++ b/extensions/cornerstone/src/components/OHIFCornerstoneViewportOverlay.js @@ -18,19 +18,13 @@ import { Tooltip } from '@ohif/ui/src/components/tooltip'; import { OverlayTrigger } from '@ohif/ui/src/components/overlayTrigger'; const Button = styled.button` - background-color: #3f51b5; color: white; - padding: 5px 15px; + padding: 2px 7px; border-radius: 10px; outline: 0; - text-transform: lowercase; - margin: 5px 5px; + text-transform: none; + margin: 2px 2px; cursor: pointer; - box-shadow: 0px 2px 2px lightgray; - transition: ease background-color 250ms; - &:hover { - background-color: #283593; - } &:disabled { cursor: default; opacity: 0.9; @@ -152,34 +146,48 @@ class OHIFCornerstoneViewportOverlay extends PureComponent { const SRLabelsOn = SRLabels && SRLabels.length !== 0 ? true : false; + /**/ + const getSRLabelsContent = SRLabels => { if (Array.isArray(SRLabels)) { const listedSRLabels = SRLabels.map((SRLabel, index) => { + const color = SRLabel.labels.color; return ( - -
Designators
-
- {SRLabel.labels.labelCodingSchemeDesignator + - ' : ' + - SRLabel.labels.valueCodingSchemeDesignator} -
- - } - > -
- -
-
+ SRLabel.labels.visible && ( + +
+ {' '} + Coding scheme designators{' '} +
+
+ {SRLabel.labels.labelCodingSchemeDesignator + + ' : ' + + SRLabel.labels.valueCodingSchemeDesignator} +
+ + } + > +
+ +
+
+ ) ); }); diff --git a/extensions/lesion-tracker/src/components/MeasurementComparisonTable.js b/extensions/lesion-tracker/src/components/MeasurementComparisonTable.js index 8218f2c55..2118895da 100644 --- a/extensions/lesion-tracker/src/components/MeasurementComparisonTable.js +++ b/extensions/lesion-tracker/src/components/MeasurementComparisonTable.js @@ -76,13 +76,13 @@ const additionalFindings = [ const currentCollections = [ { - selectorAction: () => { }, + selectorAction: () => {}, maxMeasurements: 3, groupName: 'Measurements', measurements: measurements, }, { - selectorAction: () => { }, + selectorAction: () => {}, groupName: 'Additional Findings', measurements: additionalFindings, }, @@ -90,13 +90,13 @@ const currentCollections = [ const comparisonColletions = [ { - selectorAction: () => { }, + selectorAction: () => {}, maxMeasurements: 3, groupName: 'Measurements', measurements: measurements, }, { - selectorAction: () => { }, + selectorAction: () => {}, groupName: 'Additional Findings', measurements: additionalFindings, }, @@ -128,7 +128,7 @@ const comparisonTimepoints = [ { key: 'Comparison', date: '15-Jun-18', - } + }, ]; const MeasurementComparisonTable = () => { @@ -138,8 +138,8 @@ const MeasurementComparisonTable = () => { timepoints={comparisonTimepoints} overallWarnings={overallWarnings} measurementCollection={comparisonCollections} - onRelabelClick={() => { }} - onEditDescriptionClick={() => { }} + onRelabelClick={() => {}} + onEditDescriptionClick={() => {}} />
diff --git a/platform/core/src/DICOMSR/SCOORD3D/parseSCOORD3D.js b/platform/core/src/DICOMSR/SCOORD3D/parseSCOORD3D.js index 9d27a304e..ab5a44152 100644 --- a/platform/core/src/DICOMSR/SCOORD3D/parseSCOORD3D.js +++ b/platform/core/src/DICOMSR/SCOORD3D/parseSCOORD3D.js @@ -128,9 +128,9 @@ const checkIfCanAddMeasurementsToDisplaySet = ( const imageIds = images.map(i => i.getImageId()); const SOPInstanceUIDs = images.map(i => i.SOPInstanceUID); imageDisplaySet.SRLabels = []; + const colors = new Map(); measurements.forEach(measurement => { const { coords } = measurement; - coords.forEach((coord, index) => { if (coord.ReferencedSOPSequence !== undefined) { const imageIndex = SOPInstanceUIDs.findIndex( @@ -143,19 +143,42 @@ const checkIfCanAddMeasurementsToDisplaySet = ( const imageMetadata = images[imageIndex].getData().metadata; if (coord.GraphicType === 'TEXT') { + const key = + measurement.labels[index].label + measurement.labels[index].value; + let color = colors.get(key); + if (!color) { + // random dark color + color = + 'hsla(' + Math.floor(Math.random() * 360) + ', 70%, 30%, 1)'; + colors.set(key, color); + } + + measurement.labels[index].color = color; + measurement.isSRText = true; + measurement.labels[index].visible = true; + imageDisplaySet.SRLabels.push({ ReferencedSOPInstanceUID: coord.ReferencedSOPSequence.ReferencedSOPInstanceUID, labels: measurement.labels[index], }); - } - addMeasurement( - measurement, - imageId, - imageMetadata, - imageDisplaySet.displaySetInstanceUID - ); + if (index === 0) { + addMeasurement( + measurement, + imageId, + imageMetadata, + imageDisplaySet.displaySetInstanceUID + ); + } + } else { + addMeasurement( + measurement, + imageId, + imageMetadata, + imageDisplaySet.displaySetInstanceUID + ); + } } } }); diff --git a/platform/core/src/DICOMSR/SCOORD3D/utils/addMeasurement.js b/platform/core/src/DICOMSR/SCOORD3D/utils/addMeasurement.js index 42ad129f8..c8a69ff05 100644 --- a/platform/core/src/DICOMSR/SCOORD3D/utils/addMeasurement.js +++ b/platform/core/src/DICOMSR/SCOORD3D/utils/addMeasurement.js @@ -26,8 +26,10 @@ export default function addMeasurement( const measurementData = { TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier, + TrackingIdentifier: measurement.TrackingIdentifier, renderableData: {}, labels: measurement.labels, + isSRText: measurement.isSRText, }; measurement.coords.forEach(coord => { diff --git a/platform/core/src/measurements/tools/dicomSRDisplayTool.js b/platform/core/src/measurements/tools/dicomSRDisplayTool.js index ffed8efda..77da93a43 100644 --- a/platform/core/src/measurements/tools/dicomSRDisplayTool.js +++ b/platform/core/src/measurements/tools/dicomSRDisplayTool.js @@ -6,7 +6,8 @@ export const dicomSRDisplayTool = { options: { measurementTable: { displayFunction: data => { - return `(SR) ${data.lesionNamingNumber || + return `(SR) ${data.TrackingIdentifier || + data.lesionNamingNumber || data.measurementNumber || data.text || ''}`; diff --git a/platform/ui/src/components/measurementTable/MeasurementTableItem.js b/platform/ui/src/components/measurementTable/MeasurementTableItem.js index 2e7038b17..33c7849c9 100644 --- a/platform/ui/src/components/measurementTable/MeasurementTableItem.js +++ b/platform/ui/src/components/measurementTable/MeasurementTableItem.js @@ -9,6 +9,14 @@ import { TableListItem } from './../tableList/TableListItem.js'; import './MeasurementTableItem.styl'; +const ColoredCircle = ({ color }) => { + return
; +}; + +ColoredCircle.propTypes = { + color: PropTypes.string.isRequired, +}; + class MeasurementTableItem extends Component { static propTypes = { measurementData: PropTypes.object.isRequired, @@ -21,6 +29,14 @@ class MeasurementTableItem extends Component { t: PropTypes.func, }; + constructor(props) { + super(props); + this.state = { + collapsed: true, + visible: true, + }; + } + render() { const { warningTitle = '', @@ -93,27 +109,113 @@ class MeasurementTableItem extends Component { actionButtons.push(deleteButton); } - return ( - -
-
- {this.props.t(this.props.measurementData.label, { - keySeparator: '>', - nsSeparator: '|', + if ( + this.props.measurementData.isSRText === true && + this.props.measurementData.labels && + this.props.measurementData.labels.length > 0 + ) { + return ( + + +
+
+ {this.props.t(this.props.measurementData.label, { + keySeparator: '>', + nsSeparator: '|', + })} +
+
+
+
{this.getDataDisplayText()}
+ { + this.props.measurementData.labels.forEach(label => { + label.visible = !this.state.visible; + }); + + this.setState({ + visible: !this.state.visible, + }); + }} + /> + { + this.setState({ + collapsed: !this.state.collapsed, + }); + }} + /> +
+
+ {this.state.collapsed && + this.props.measurementData.labels.map((SRLabel, index) => { + return ( + } + itemMetaClass="item-color-section" + onItemClick={this.onItemClick} + > +
+
+ {SRLabel.label + ' : ' + SRLabel.value} + { + SRLabel.visible = !SRLabel.visible; + }} + /> +
+
+
+ ); })} +
+ ); + } else { + return ( + +
+
+ {this.props.t(this.props.measurementData.label, { + keySeparator: '>', + nsSeparator: '|', + })} +
+
{this.getDataDisplayText()}
+ {!this.props.measurementData.isReadOnly && ( +
{actionButtons}
+ )}
-
{this.getDataDisplayText()}
- {!this.props.measurementData.isReadOnly && ( -
{actionButtons}
- )} -
- - ); + + ); + } }; onItemClick = event => { diff --git a/platform/ui/src/components/measurementTable/MeasurementTableItem.styl b/platform/ui/src/components/measurementTable/MeasurementTableItem.styl index eb3ba7044..0293418b5 100644 --- a/platform/ui/src/components/measurementTable/MeasurementTableItem.styl +++ b/platform/ui/src/components/measurementTable/MeasurementTableItem.styl @@ -1,4 +1,16 @@ .measurementItem + .item-color-section + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + + .item-color + margin-top: 8px; + border-radius: 100%; + width: 20px; + height: 20px; + .rowActions margin-left: -1px; background-color: var(--ui-gray-darker) @@ -63,3 +75,16 @@ width: 22px; height: 20px; pointer-events: inherit; + +.icons + display: flex; + align-items: center; + border-radius: 5px; + +.icons .eye-icon + margin-left: 100px; + margin-right: -5px; +.icons .angle-double-down +.icons .angle-double-up + margin-left: 15px; + margin-right: -5px; diff --git a/platform/ui/src/components/tableList/TableListItem.js b/platform/ui/src/components/tableList/TableListItem.js index b3b64d6a1..6ddc39516 100644 --- a/platform/ui/src/components/tableList/TableListItem.js +++ b/platform/ui/src/components/tableList/TableListItem.js @@ -12,16 +12,13 @@ export class TableListItem extends Component { itemIndex: PropTypes.number, itemMeta: PropTypes.node, itemMetaClass: PropTypes.string, - itemKey: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - ]), + itemKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), onItemClick: PropTypes.func.isRequired, }; static defaultProps = { itemMeta: null, - itemMetaClass: '' + itemMetaClass: '', }; render() { diff --git a/platform/viewer/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js b/platform/viewer/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js index 13c0068a1..feae52669 100644 --- a/platform/viewer/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js +++ b/platform/viewer/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js @@ -122,6 +122,7 @@ function convertMeasurementsToTableData(toolCollections, timepoints) { Object.keys(toolCollections).forEach(toolId => { const toolMeasurements = toolCollections[toolId]; + const tool = tools.find(tool => tool.id === toolId); const { displayFunction } = tool.options.measurementTable; @@ -131,12 +132,13 @@ function convertMeasurementsToTableData(toolCollections, timepoints) { Object.keys(groupedMeasurements).forEach(groupedMeasurementsIndex => { const measurementNumberList = groupedMeasurements[groupedMeasurementsIndex]; + const measurementData = measurementNumberList[0]; const { measurementNumber, lesionNamingNumber, toolType, - isReadOnly + isReadOnly, } = measurementData; const measurementId = measurementData._id; @@ -150,6 +152,8 @@ function convertMeasurementsToTableData(toolCollections, timepoints) { const tableMeasurement = { itemNumber: lesionNamingNumber, label: getMeasurementText(measurementData), + labels: measurementData.labels, + isSRText: measurementData.isSRText, measurementId, measurementNumber, lesionNamingNumber, diff --git a/platform/viewer/src/connectedComponents/Viewer.js b/platform/viewer/src/connectedComponents/Viewer.js index bdd0e6238..41cfaf5e3 100644 --- a/platform/viewer/src/connectedComponents/Viewer.js +++ b/platform/viewer/src/connectedComponents/Viewer.js @@ -661,10 +661,9 @@ const _mapStudiesToThumbnails = function(studies, activeDisplaySetInstanceUID) { let altImageText; if (displaySet.Modality && displaySet.Modality === 'SEG') { - // TODO: We want to replace this with a thumbnail showing - // the segmentation map on the image, but this is easier - // and better than what we have right now. altImageText = 'SEG'; + } else if (displaySet.Modality && displaySet.Modality === 'SR') { + altImageText = 'SR'; } else if (displaySet.images && displaySet.images.length) { const imageIndex = Math.floor(displaySet.images.length / 2); imageId = displaySet.images[imageIndex].getImageId();