feat(Segments Looping) : Fixed looping through Segments of SEG (#4793)

This commit is contained in:
Yiannis Theocharakis 2025-02-25 23:25:52 +02:00 committed by GitHub
parent 4ecffe89cb
commit 4b63d48518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 87 additions and 55 deletions

View File

@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { ViewportActionArrows } from '@ohif/ui';
import { useViewportGrid } from '@ohif/ui-next';
import { utils } from '@ohif/extension-cornerstone';
import promptHydrateRT from '../utils/promptHydrateRT';
import _getStatusComponent from './_getStatusComponent';
@ -48,7 +49,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
const [viewportGrid, viewportGridService] = useViewportGrid();
// States
let selectedSegmentObjectIndex: number = 0;
const selectedSegmentObjectIndex: number = 0;
const { setPositionPresentation } = usePositionPresentationStore();
// Hydration means that the RT is opened and segments are loaded into the
@ -141,38 +142,15 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
const onSegmentChange = useCallback(
direction => {
const segmentationId = rtDisplaySet.displaySetInstanceUID;
const segmentation = segmentationService.getSegmentation(segmentationId);
const { segments } = segmentation;
const numberOfSegments = Object.keys(segments).length;
//Get activeSegment each time because the user can select any segment from the list and thus the index should be updated
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (activeSegment) {
const activeSegmentIndex = Object.values(segments).findIndex(
segment => segment.segmentIndex === activeSegment.segmentIndex
);
//from the activeSegment get the actual obeject array index to be used
selectedSegmentObjectIndex = activeSegmentIndex;
}
let newSelectedSegmentIndex = selectedSegmentObjectIndex + direction;
//Handle looping through list of segments
if (newSelectedSegmentIndex > numberOfSegments - 1) {
newSelectedSegmentIndex = 0;
} else if (newSelectedSegmentIndex < 0) {
newSelectedSegmentIndex = numberOfSegments - 1;
}
//convert segmentationId from object array index to property value of type Segment
//Functions below uses the segmentIndex object attribute so we have to do the conversion
const keyIndex = Object.values(segments)[newSelectedSegmentIndex]?.segmentIndex;
segmentationService.setActiveSegment(segmentationId, keyIndex);
segmentationService.jumpToSegmentCenter(segmentationId, keyIndex, viewportId);
selectedSegmentObjectIndex = newSelectedSegmentIndex;
utils.handleSegmentChange({
direction,
segDisplaySet: rtDisplaySet,
viewportId,
selectedSegmentObjectIndex,
segmentationService,
});
},
[selectedSegmentObjectIndex, segmentationService]
[selectedSegmentObjectIndex]
);
useEffect(() => {

View File

@ -7,6 +7,7 @@ import promptHydrateSEG from '../utils/promptHydrateSEG';
import _getStatusComponent from './_getStatusComponent';
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import { utils } from '@ohif/extension-cornerstone';
const SEG_TOOLGROUP_BASE_NAME = 'SEGToolGroup';
@ -46,7 +47,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
const [viewportGrid, viewportGridService] = useViewportGrid();
// States
const [selectedSegment, setSelectedSegment] = useState(1);
let selectedSegmentObjectIndex: number = 0;
const { setPositionPresentation } = usePositionPresentationStore();
// Hydration means that the SEG is opened and segments are loaded into the
@ -131,27 +132,15 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
const onSegmentChange = useCallback(
direction => {
const segmentationId = segDisplaySet.displaySetInstanceUID;
const segmentation = segmentationService.getSegmentation(segmentationId);
const { segments } = segmentation;
const numberOfSegments = Object.keys(segments).length;
let newSelectedSegmentIndex = selectedSegment + direction;
// Segment 0 is always background
if (newSelectedSegmentIndex > numberOfSegments - 1) {
newSelectedSegmentIndex = 1;
} else if (newSelectedSegmentIndex === 0) {
newSelectedSegmentIndex = numberOfSegments - 1;
}
segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, viewportId);
setSelectedSegment(newSelectedSegmentIndex);
utils.handleSegmentChange({
direction,
segDisplaySet: segDisplaySet,
viewportId,
selectedSegmentObjectIndex,
segmentationService,
});
},
[selectedSegment]
[selectedSegmentObjectIndex]
);
const hydrateSEG = useCallback(() => {

View File

@ -54,6 +54,7 @@ import PanelMeasurement from './panels/PanelMeasurement';
import DicomUpload from './components/DicomUpload/DicomUpload';
import { useSegmentations } from './hooks/useSegmentations';
import { StudySummaryFromMetadata } from './components/StudySummaryFromMetadata';
import utils from './utils';
const { imageRetrieveMetadataProvider } = cornerstone.utilities;
@ -254,5 +255,6 @@ export {
PanelMeasurement,
DicomUpload,
StudySummaryFromMetadata,
utils,
};
export default cornerstoneExtension;

View File

@ -0,0 +1,7 @@
import { handleSegmentChange } from './segmentUtils';
const utils = {
handleSegmentChange,
};
export default utils;

View File

@ -0,0 +1,47 @@
import SegmentationServiceType from '../services/SegmentationService';
export const handleSegmentChange = ({
direction,
segDisplaySet,
viewportId,
selectedSegmentObjectIndex,
segmentationService,
}: {
direction: number;
segDisplaySet: AppTypes.DisplaySet;
viewportId: string;
selectedSegmentObjectIndex: number;
segmentationService: SegmentationServiceType;
}) => {
const segmentationId = segDisplaySet.displaySetInstanceUID;
const segmentation = segmentationService.getSegmentation(segmentationId);
const { segments } = segmentation;
const numberOfSegments = Object.keys(segments).length;
// Get activeSegment each time because the user can select any segment from the list and thus the index should be updated
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (activeSegment) {
// from the activeSegment get the actual object array index to be used
selectedSegmentObjectIndex = Object.values(segments).findIndex(
segment => segment.segmentIndex === activeSegment.segmentIndex
);
}
let newSelectedSegmentIndex = selectedSegmentObjectIndex + direction;
// Handle looping through list of segments
if (newSelectedSegmentIndex > numberOfSegments - 1) {
newSelectedSegmentIndex = 0;
} else if (newSelectedSegmentIndex < 0) {
newSelectedSegmentIndex = numberOfSegments - 1;
}
// Convert segmentationId from object array index to property value of type Segment
// Functions below use the segmentIndex object attribute so we have to do the conversion
const segmentIndex = Object.values(segments)[newSelectedSegmentIndex]?.segmentIndex;
segmentationService.setActiveSegment(segmentationId, segmentIndex);
segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex, viewportId);
selectedSegmentObjectIndex = newSelectedSegmentIndex;
};

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Button } from '../../components/Button/Button';
import {
DropdownMenu,
@ -97,6 +97,13 @@ const DataRow: React.FC<DataRowProps> = ({
}) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const isTitleLong = title?.length > 25;
const rowRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isSelected && rowRef.current) {
rowRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}, [isSelected]);
const handleAction = (action: string, e: React.MouseEvent) => {
e.stopPropagation();
@ -181,7 +188,9 @@ const DataRow: React.FC<DataRowProps> = ({
};
return (
<div className={`flex flex-col ${isVisible ? '' : 'opacity-60'}`}>
<div
ref={rowRef}
className={`flex flex-col ${isVisible ? '' : 'opacity-60'}`}>
<div
className={`flex items-center ${
isSelected ? 'bg-popover' : 'bg-muted'