Files
ohif-viewer/extensions/cornerstone/src/hooks/useMeasurementTracking.ts
T
AlirezaandBill Wallace 6dd150d401 fix: ohif tests to run with cornerstone 3d 5.0 (#6043)
* chore(tests): Update multiple screenshot test images for various specs

* feat(screenshot-reviewer): Add screenshot review tool and update package.json scripts

* fix(DICOMSRDisplayTool): Improve actor presence check in viewport

* chore(tests): Update multiple screenshot assets for various specs

* chore(tests): Integrate waitForPaintToSettle and waitForViewportsRendered in multiple specs for improved rendering stability

* chore(tests): Update screenshot assets for SEGHydration and SEGNoHydration specs

* test: update progressive loading screenshots

* jest 30 test fixes for compatibility with pnpm cs3d

* Use correct setDisplaySets instead of setDataId

* fix: Naming change for LegacyVolumeViewport3D

* Update to allow tolerance for contour tests

* update

* fix

* refactor: Replace instanceof checks with utility functions for viewport type validation

* fix: Update createSegmentationForViewport to handle undefined displaySetInstanceUID gracefully

* bun lock

* fix: Install cs3d with pnpm instead of bun

* Update node version for playwright

* Update to v5.0.0 of cs3d

* fix: Build dependency

* audit

* Change to a web await retry assert

* Fix timing related test failures

* fix: Freehand close

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-06-09 20:25:14 -04:00

133 lines
4.4 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react';
import { useSystem } from '@ohif/core/src';
import { useViewportDisplaySets } from './useViewportDisplaySets';
import { isVolumeViewportType } from '../utils/getLegacyViewportType';
/**
* Hook that provides measurement tracking information for a viewport
*
* @param options - The hook options
* @param options.viewportId - The ID of the viewport to track
* @returns An object containing the tracking state, related information, and tracked measurement UIDs
*/
export function useMeasurementTracking({ viewportId }: { viewportId: string }) {
const { servicesManager } = useSystem();
const { cornerstoneViewportService, trackedMeasurementsService, measurementService } =
servicesManager.services;
const { backgroundDisplaySet } = useViewportDisplaySets(viewportId);
// Tracking states
const [isTracked, setIsTracked] = useState(false);
const [isLocked, setIsLocked] = useState(false);
const [trackedMeasurementUIDs, setTrackedMeasurementUIDs] = useState<string[]>([]);
const updateTrackedMeasurements = useCallback(() => {
if (!measurementService || !backgroundDisplaySet?.SeriesInstanceUID || !isTracked) {
setTrackedMeasurementUIDs([]);
return;
}
const seriesInstanceUID = backgroundDisplaySet.SeriesInstanceUID;
const seriesMeasurements = measurementService.getMeasurements(
measurement => measurement.referenceSeriesUID === seriesInstanceUID
);
const uids = seriesMeasurements.map(measurement => measurement.uid);
setTrackedMeasurementUIDs(uids);
}, [measurementService, backgroundDisplaySet, isTracked]);
const updateIsTracked = useCallback(() => {
if (!trackedMeasurementsService || !backgroundDisplaySet?.SeriesInstanceUID) {
setIsTracked(false);
return;
}
const trackedSeries = trackedMeasurementsService.getTrackedSeries();
if (!trackedSeries?.length) {
setIsTracked(false);
return;
}
const viewport = cornerstoneViewportService?.getCornerstoneViewport(viewportId);
const SeriesInstanceUID = backgroundDisplaySet.SeriesInstanceUID;
if (isVolumeViewportType(viewport)) {
const currentImageId = viewport?.getCurrentImageId();
if (!currentImageId) {
setIsTracked(false);
return;
}
}
const seriesIsTracked = trackedSeries.includes(SeriesInstanceUID);
setIsTracked(seriesIsTracked);
}, [viewportId, backgroundDisplaySet, cornerstoneViewportService, trackedMeasurementsService]);
// Update tracked measurements whenever tracking state changes
useEffect(() => {
updateTrackedMeasurements();
}, [isTracked, updateTrackedMeasurements]);
useEffect(() => {
if (!trackedMeasurementsService) {
return;
}
setIsLocked(trackedMeasurementsService.isTrackingEnabled());
updateIsTracked();
const subscriptions = [
trackedMeasurementsService.subscribe(trackedMeasurementsService.EVENTS.TRACKING_ENABLED, () =>
setIsLocked(true)
),
trackedMeasurementsService.subscribe(
trackedMeasurementsService.EVENTS.TRACKING_DISABLED,
() => setIsLocked(false)
),
trackedMeasurementsService.subscribe(
trackedMeasurementsService.EVENTS.TRACKED_SERIES_CHANGED,
() => updateIsTracked()
),
trackedMeasurementsService.subscribe(trackedMeasurementsService.EVENTS.SERIES_ADDED, () =>
updateIsTracked()
),
trackedMeasurementsService.subscribe(trackedMeasurementsService.EVENTS.SERIES_REMOVED, () =>
updateIsTracked()
),
];
// Subscribe to measurement service events to update trackedMeasurementUIDs
if (measurementService) {
[
measurementService.EVENTS.MEASUREMENT_ADDED,
measurementService.EVENTS.RAW_MEASUREMENT_ADDED,
measurementService.EVENTS.MEASUREMENT_UPDATED,
measurementService.EVENTS.MEASUREMENT_REMOVED,
measurementService.EVENTS.MEASUREMENTS_CLEARED,
].forEach(eventType => {
subscriptions.push(
measurementService.subscribe(eventType, () => updateTrackedMeasurements())
);
});
}
return () => {
subscriptions.forEach(sub => sub.unsubscribe());
};
}, [trackedMeasurementsService, measurementService, updateIsTracked, updateTrackedMeasurements]);
return {
isTracked,
isLocked,
seriesInstanceUID: backgroundDisplaySet?.SeriesInstanceUID,
trackedMeasurementUIDs,
};
}
export default useMeasurementTracking;