diff --git a/extensions/cornerstone/src/initMeasurementService.js b/extensions/cornerstone/src/initMeasurementService.js index f97b5007f..cb143c38f 100644 --- a/extensions/cornerstone/src/initMeasurementService.js +++ b/extensions/cornerstone/src/initMeasurementService.js @@ -121,11 +121,36 @@ const connectToolsToMeasurementService = ( const { toolName } = metadata; annotationModifiedEventDetail.uid = annotationUID; - annotationToMeasurement(toolName, annotationModifiedEventDetail); + // Passing true to indicate this is an update and NOT a annotation (start) completion. + annotationToMeasurement(toolName, annotationModifiedEventDetail, true); } catch (error) { console.warn('Failed to update measurement:', error); } } + function selectMeasurement(csToolsEvent) { + try { + const annotationSelectionEventDetail = csToolsEvent.detail; + + const { + added: addedSelectedAnnotationUIDs, + removed: removedSelectedAnnotationUIDs, + } = annotationSelectionEventDetail; + + if (removedSelectedAnnotationUIDs) { + removedSelectedAnnotationUIDs.forEach(annotationUID => + MeasurementService.setMeasurementSelected(annotationUID, false) + ); + } + + if (addedSelectedAnnotationUIDs) { + addedSelectedAnnotationUIDs.forEach(annotationUID => + MeasurementService.setMeasurementSelected(annotationUID, true) + ); + } + } catch (error) { + console.warn('Failed to select and unselect measurements:', error); + } + } /** * When csTools fires a removed event, remove the same measurement @@ -157,13 +182,17 @@ const connectToolsToMeasurementService = ( // on display sets added, check if there are any measurements in measurement service that need to be // put into cornerstone tools + const addedEvt = csToolsEvents.ANNOTATION_ADDED; const completedEvt = csToolsEvents.ANNOTATION_COMPLETED; const updatedEvt = csToolsEvents.ANNOTATION_MODIFIED; const removedEvt = csToolsEvents.ANNOTATION_REMOVED; + const selectionEvt = csToolsEvents.ANNOTATION_SELECTION_CHANGE; + eventTarget.addEventListener(addedEvt, addMeasurement); eventTarget.addEventListener(completedEvt, addMeasurement); eventTarget.addEventListener(updatedEvt, updateMeasurement); eventTarget.addEventListener(removedEvt, removeMeasurement); + eventTarget.addEventListener(selectionEvt, selectMeasurement); return csTools3DVer1MeasurementSource; }; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js index 3a36b7576..ed2ea6c27 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js @@ -86,7 +86,7 @@ function getMappedAnnotations(annotation, DisplaySetService) { const targets = Object.keys(cachedStats); if (!targets.length) { - return; + return []; } const annotations = []; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.js b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.js index 3e3c8b7cd..49025e22e 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.js @@ -85,7 +85,7 @@ function getMappedAnnotations(annotation, DisplaySetService) { const targets = Object.keys(cachedStats); if (!targets.length) { - return; + return []; } const annotations = []; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js index c3def8782..8388c8482 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js @@ -91,7 +91,7 @@ function getMappedAnnotations(annotation, DisplaySetService) { const targets = Object.keys(cachedStats); if (!targets.length) { - return; + return []; } const annotations = []; diff --git a/extensions/default/src/Panels/PanelMeasurementTable.tsx b/extensions/default/src/Panels/PanelMeasurementTable.tsx index d213210d1..1d2b0f6f0 100644 --- a/extensions/default/src/Panels/PanelMeasurementTable.tsx +++ b/extensions/default/src/Panels/PanelMeasurementTable.tsx @@ -255,14 +255,13 @@ function _getMappedMeasurements(MeasurementService) { } function _mapMeasurementToDisplay(measurement, index, types) { - const { displayText, uid, label, type } = measurement; + const { displayText, uid, label, type, selected } = measurement; return { uid, label: label || '(empty)', measurementType: type, displayText: displayText || [], - // TODO: handle one layer down - isActive: false, // activeMeasurementItem === i + 1, + isActive: selected, }; } diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx index 137dffa38..f376aecfb 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx @@ -323,7 +323,7 @@ function _mapMeasurementToDisplay(measurement, types, DisplaySetService) { label: measurement.label || '(empty)', measurementType: measurement.type, displayText: displayText || [], - isActive: false, // activeMeasurementItem === i + 1, + isActive: measurement.selected, }; } diff --git a/platform/core/src/services/MeasurementService/MeasurementService.js b/platform/core/src/services/MeasurementService/MeasurementService.js index a0955e86a..6eba5cb75 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.js +++ b/platform/core/src/services/MeasurementService/MeasurementService.js @@ -26,6 +26,7 @@ import pubSubServiceInterface from '../_shared/pubSubServiceInterface'; * @property {number} area - * @property {Array} points - * @property {MeasurementSource} source - + * @property {boolean} selected - */ /* Measurement schema keys for object validation. */ @@ -56,6 +57,7 @@ const MEASUREMENT_SCHEMA_KEYS = [ 'shortestDiameter', 'longestDiameter', 'cachedStats', + 'selected', ]; const EVENTS = { @@ -184,6 +186,20 @@ class MeasurementService { return measurement; } + setMeasurementSelected(measurementUID, selected) { + const measurement = this.getMeasurement(measurementUID); + if (!measurement) { + return; + } + + measurement.selected = selected; + + this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, { + source: measurement.source, + measurement, + }); + } + /** * Create a new source. * @@ -218,8 +234,17 @@ class MeasurementService { version, }; - source.annotationToMeasurement = (annotationType, annotation) => { - return this.annotationToMeasurement(source, annotationType, annotation); + source.annotationToMeasurement = ( + annotationType, + annotation, + isUpdate = false + ) => { + return this.annotationToMeasurement( + source, + annotationType, + annotation, + isUpdate + ); }; source.remove = (measurementUID, eventDetails) => { @@ -467,9 +492,15 @@ class MeasurementService { * @param {MeasurementSource} source The measurement source instance * @param {string} annotationType The source annotationType * @param {EventDetail} sourceAnnotationDetail for the annotation event + * @param {boolean} isUpdate is this an update or an add/completed instead? * @return {string} A measurement uid */ - annotationToMeasurement(source, annotationType, sourceAnnotationDetail) { + annotationToMeasurement( + source, + annotationType, + sourceAnnotationDetail, + isUpdate = false + ) { if (!this._isValidSource(source)) { throw new Error('Invalid source.'); } @@ -524,19 +555,27 @@ class MeasurementService { }; if (this.measurements[internalUID]) { + // TODO: Ultimately, each annotation should have a selected flag right from the soure. + // For now, it is just added in OHIF here and in setMeasurementSelected. + newMeasurement.selected = this.measurements[internalUID].selected; this.measurements[internalUID] = newMeasurement; - this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, { - source, - measurement: newMeasurement, - notYetUpdatedAtSource: false, - }); + if (isUpdate) { + this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, { + source, + measurement: newMeasurement, + notYetUpdatedAtSource: false, + }); + } else { + log.info('Measurement added.', newMeasurement); + this.measurements[internalUID] = newMeasurement; + this._broadcastEvent(this.EVENTS.MEASUREMENT_ADDED, { + source, + measurement: newMeasurement, + }); + } } else { - log.info('Measurement added.', newMeasurement); + log.info('Measurement started.', newMeasurement); this.measurements[internalUID] = newMeasurement; - this._broadcastEvent(this.EVENTS.MEASUREMENT_ADDED, { - source, - measurement: newMeasurement, - }); } return newMeasurement.uid; @@ -588,16 +627,10 @@ class MeasurementService { } this._addJumpToMeasurement(viewportIndex, measurementUID); - const eventName = this.EVENTS.JUMP_TO_MEASUREMENT; - - const hasListeners = Object.keys(this.listeners).length > 0; - const hasCallbacks = Array.isArray(this.listeners[eventName]); - - if (hasListeners && hasCallbacks) { - this.listeners[eventName].forEach(listener => { - listener.callback({ viewportIndex, measurement }); - }); - } + this._broadcastEvent(this.EVENTS.JUMP_TO_MEASUREMENT, { + viewportIndex, + measurement, + }); } getJumpToMeasurement(viewportIndex) { diff --git a/platform/core/src/services/MeasurementService/MeasurementService.test.js b/platform/core/src/services/MeasurementService/MeasurementService.test.js index 6d6fa6eb8..3ec0a2799 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.test.js +++ b/platform/core/src/services/MeasurementService/MeasurementService.test.js @@ -364,8 +364,9 @@ describe('MeasurementService.js', () => { () => (addCallbackWasCalled = true) ); - /* Add new measurement */ - source.annotationToMeasurement(annotationType, measurement); + /* Add new measurement - two calls needed for the start and the other for the completed*/ + const uid = source.annotationToMeasurement(annotationType, measurement); + source.annotationToMeasurement(annotationType, { uid, ...measurement }); expect(addCallbackWasCalled).toBe(true); }); @@ -392,7 +393,11 @@ describe('MeasurementService.js', () => { const uid = source.annotationToMeasurement(annotationType, measurement); /* Update measurement */ - source.annotationToMeasurement(annotationType, { uid, ...measurement }); + source.annotationToMeasurement( + annotationType, + { uid, ...measurement }, + true + ); expect(updateCallbackWasCalled).toBe(true); }); @@ -418,8 +423,9 @@ describe('MeasurementService.js', () => { /* Unsubscribe */ unsubscribe(); - /* Create measurement */ - source.annotationToMeasurement(annotationType, measurement); + /* Create measurement - two calls needed one to start and one to complete */ + const uid = source.annotationToMeasurement(annotationType, measurement); + source.annotationToMeasurement(annotationType, { uid, ...measurement }); expect(updateCallbackWasCalled).toBe(false); });