import React, { useEffect, useState, useCallback, useReducer } from 'react'; import PropTypes from 'prop-types'; import { SegmentationTable, Button, Icon } from '@ohif/ui'; import classnames from 'classnames'; import { useTranslation } from 'react-i18next'; import segmentationEditHandler from './segmentationEditHandler'; import ExportReports from './ExportReports'; import ROIThresholdConfiguration, { ROI_STAT, } from './ROIThresholdConfiguration'; const LOWER_THRESHOLD_DEFAULT = 0; const UPPER_THRESHOLD_DEFAULT = 100; const WEIGHT_DEFAULT = 0.41; // a default weight for suv max often used in the literature const DEFAULT_STRATEGY = ROI_STAT; function reducer(state, action) { const { payload } = action; const { strategy, lower, upper, weight } = payload; switch (action.type) { case 'setStrategy': return { ...state, strategy, }; case 'setThreshold': return { ...state, lower: lower ? lower : state.lower, upper: upper ? upper : state.upper, }; case 'setWeight': return { ...state, weight, }; default: return state; } } export default function PanelRoiThresholdSegmentation({ servicesManager, commandsManager, }) { const { SegmentationService } = servicesManager.services; const { t } = useTranslation('PanelSUV'); const [showConfig, setShowConfig] = useState(false); const [labelmapLoading, setLabelmapLoading] = useState(false); const [selectedSegmentationId, setSelectedSegmentationId] = useState(null); const [segmentations, setSegmentations] = useState(() => SegmentationService.getSegmentations() ); const [config, dispatch] = useReducer(reducer, { strategy: DEFAULT_STRATEGY, lower: LOWER_THRESHOLD_DEFAULT, upper: UPPER_THRESHOLD_DEFAULT, weight: WEIGHT_DEFAULT, }); const [tmtvValue, setTmtvValue] = useState(null); const runCommand = useCallback( (commandName, commandOptions = {}) => { return commandsManager.runCommand(commandName, commandOptions); }, [commandsManager] ); const handleTMTVCalculation = useCallback(() => { const tmtv = runCommand('calculateTMTV', { segmentations }); if (tmtv !== undefined) { setTmtvValue(tmtv.toFixed(2)); } }, [segmentations, runCommand]); const handleROIThresholding = useCallback(() => { const labelmap = runCommand('thresholdSegmentationByRectangleROITool', { segmentationId: selectedSegmentationId, config, }); const lesionStats = runCommand('getLesionStats', { labelmap }); const suvPeak = runCommand('calculateSuvPeak', { labelmap }); const lesionGlyoclysisStats = lesionStats.volume * lesionStats.meanValue; // update segDetails with the suv peak for the active segmentation const segmentation = SegmentationService.getSegmentation( selectedSegmentationId ); const cachedStats = { lesionStats, suvPeak, lesionGlyoclysisStats, }; const notYetUpdatedAtSource = true; SegmentationService.addOrUpdateSegmentation( { ...segmentation, ...Object.assign(segmentation.cachedStats, cachedStats), displayText: [`SUV Peak: ${suvPeak.suvPeak.toFixed(2)}`], }, notYetUpdatedAtSource ); handleTMTVCalculation(); }, [selectedSegmentationId, config]); /** * Update UI based on segmentation changes (added, removed, updated) */ useEffect(() => { // ~~ Subscription const added = SegmentationService.EVENTS.SEGMENTATION_ADDED; const updated = SegmentationService.EVENTS.SEGMENTATION_UPDATED; const subscriptions = []; [added, updated].forEach(evt => { const { unsubscribe } = SegmentationService.subscribe(evt, () => { const segmentations = SegmentationService.getSegmentations(); setSegmentations(segmentations); }); subscriptions.push(unsubscribe); }); return () => { subscriptions.forEach(unsub => { unsub(); }); }; }, []); useEffect(() => { const { unsubscribe } = SegmentationService.subscribe( SegmentationService.EVENTS.SEGMENTATION_REMOVED, () => { const segmentations = SegmentationService.getSegmentations(); setSegmentations(segmentations); if (segmentations.length > 0) { setSelectedSegmentationId(segmentations[0].id); handleTMTVCalculation(); } else { setSelectedSegmentationId(null); setTmtvValue(null); } } ); return () => { unsubscribe(); }; }, []); /** * Whenever the segmentations change, update the TMTV calculations */ useEffect(() => { if (!selectedSegmentationId && segmentations.length > 0) { setSelectedSegmentationId(segmentations[0].id); } handleTMTVCalculation(); }, [segmentations, selectedSegmentationId]); return (