import React, { useCallback, useEffect, useState } from 'react'; import { useRunCommand, useSystem } from '@ohif/core'; import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone'; import { Button, cn, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, Tabs, TabsList, TabsTrigger, } from '@ohif/ui-next'; import { Icons } from '@ohif/ui-next'; import { contourSegmentation } from '@cornerstonejs/tools/utilities'; import { useTranslation } from 'react-i18next'; import { Segment } from '@cornerstonejs/tools/types'; const { LogicalOperation } = contourSegmentation; const options = [ { value: 'merge', logicalOperation: LogicalOperation.Union, label: 'Merge', icon: 'actions-combine-merge', helperIcon: 'helper-combine-merge', }, { value: 'intersect', logicalOperation: LogicalOperation.Intersect, label: 'Intersect', icon: 'actions-combine-intersect', helperIcon: 'helper-combine-intersect', }, { value: 'subtract', logicalOperation: LogicalOperation.Subtract, label: 'Subtract', icon: 'actions-combine-subtract', helperIcon: 'helper-combine-subtract', }, ]; // Shared component for segment selection function SegmentSelector({ label, value, onValueChange, segments, placeholder = 'Select a segment', }: { label: string; value: string; onValueChange: (value: string) => void; segments: Segment[]; placeholder?: string; }) { const { t } = useTranslation('SegmentationPanel'); return (
{label}
); } function LogicalContourOperationOptions() { const { servicesManager } = useSystem(); const { segmentationService } = servicesManager.services; const { t } = useTranslation('SegmentationPanel'); const { segmentationsWithRepresentations } = useActiveViewportSegmentationRepresentations(); const activeRepresentation = segmentationsWithRepresentations?.find( ({ representation }) => representation?.active ); const segments = activeRepresentation ? Object.values(activeRepresentation.segmentation.segments) : []; // Calculate the next available segment index const nextSegmentIndex = activeRepresentation ? segmentationService.getNextAvailableSegmentIndex( activeRepresentation.segmentation.segmentationId ) : 1; const activeSegment = segments.find(segment => segment.active); const activeSegmentIndex = activeSegment?.segmentIndex || 0; const [operation, setOperation] = useState(options[0]); const [segmentA, setSegmentA] = useState(activeSegmentIndex?.toString() || ''); const [segmentB, setSegmentB] = useState(''); const [createNewSegment, setCreateNewSegment] = useState(false); const [newSegmentName, setNewSegmentName] = useState(''); useEffect(() => { setSegmentA(activeSegmentIndex?.toString() || null); }, [activeSegmentIndex]); useEffect(() => { setNewSegmentName(`Segment ${nextSegmentIndex}`); }, [nextSegmentIndex]); const runCommand = useRunCommand(); const applyLogicalContourOperation = useCallback(() => { let resultSegmentIndex = segmentA; if (createNewSegment) { resultSegmentIndex = nextSegmentIndex.toString(); runCommand('addSegment', { segmentationId: activeRepresentation.segmentation.segmentationId, config: { label: newSegmentName, segmentIndex: nextSegmentIndex, }, }); } runCommand('applyLogicalContourOperation', { segmentAInfo: { segmentationId: activeRepresentation.segmentation.segmentationId, segmentIndex: parseInt(segmentA), }, segmentBInfo: { segmentationId: activeRepresentation.segmentation.segmentationId, segmentIndex: parseInt(segmentB), }, resultSegmentInfo: { segmentationId: activeRepresentation.segmentation.segmentationId, segmentIndex: parseInt(resultSegmentIndex), }, logicalOperation: operation.logicalOperation, }); }, [ activeRepresentation?.segmentation?.segmentationId, createNewSegment, newSegmentName, nextSegmentIndex, operation.logicalOperation, runCommand, segmentA, segmentB, ]); return (
{options.map(option => { const { value, icon } = option; return ( setOperation(option)} > ); })}
{t(operation.label)}
setNewSegmentName(e.target.value)} />
); } export default LogicalContourOperationOptions;