feat(Segments Looping) : Fixed looping through Segments of SEG (#4793)
This commit is contained in:
parent
4ecffe89cb
commit
4b63d48518
@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ViewportActionArrows } from '@ohif/ui';
|
import { ViewportActionArrows } from '@ohif/ui';
|
||||||
import { useViewportGrid } from '@ohif/ui-next';
|
import { useViewportGrid } from '@ohif/ui-next';
|
||||||
|
import { utils } from '@ohif/extension-cornerstone';
|
||||||
|
|
||||||
import promptHydrateRT from '../utils/promptHydrateRT';
|
import promptHydrateRT from '../utils/promptHydrateRT';
|
||||||
import _getStatusComponent from './_getStatusComponent';
|
import _getStatusComponent from './_getStatusComponent';
|
||||||
@ -48,7 +49,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
|
|||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
|
|
||||||
// States
|
// States
|
||||||
let selectedSegmentObjectIndex: number = 0;
|
const selectedSegmentObjectIndex: number = 0;
|
||||||
const { setPositionPresentation } = usePositionPresentationStore();
|
const { setPositionPresentation } = usePositionPresentationStore();
|
||||||
|
|
||||||
// Hydration means that the RT is opened and segments are loaded into the
|
// Hydration means that the RT is opened and segments are loaded into the
|
||||||
@ -141,38 +142,15 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
|
|||||||
|
|
||||||
const onSegmentChange = useCallback(
|
const onSegmentChange = useCallback(
|
||||||
direction => {
|
direction => {
|
||||||
const segmentationId = rtDisplaySet.displaySetInstanceUID;
|
utils.handleSegmentChange({
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
direction,
|
||||||
|
segDisplaySet: rtDisplaySet,
|
||||||
const { segments } = segmentation;
|
viewportId,
|
||||||
|
selectedSegmentObjectIndex,
|
||||||
const numberOfSegments = Object.keys(segments).length;
|
segmentationService,
|
||||||
//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;
|
|
||||||
},
|
},
|
||||||
[selectedSegmentObjectIndex, segmentationService]
|
[selectedSegmentObjectIndex]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import promptHydrateSEG from '../utils/promptHydrateSEG';
|
|||||||
import _getStatusComponent from './_getStatusComponent';
|
import _getStatusComponent from './_getStatusComponent';
|
||||||
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
|
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
|
||||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||||
|
import { utils } from '@ohif/extension-cornerstone';
|
||||||
|
|
||||||
const SEG_TOOLGROUP_BASE_NAME = 'SEGToolGroup';
|
const SEG_TOOLGROUP_BASE_NAME = 'SEGToolGroup';
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
|||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
|
|
||||||
// States
|
// States
|
||||||
const [selectedSegment, setSelectedSegment] = useState(1);
|
let selectedSegmentObjectIndex: number = 0;
|
||||||
const { setPositionPresentation } = usePositionPresentationStore();
|
const { setPositionPresentation } = usePositionPresentationStore();
|
||||||
|
|
||||||
// Hydration means that the SEG is opened and segments are loaded into the
|
// Hydration means that the SEG is opened and segments are loaded into the
|
||||||
@ -131,27 +132,15 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
|||||||
|
|
||||||
const onSegmentChange = useCallback(
|
const onSegmentChange = useCallback(
|
||||||
direction => {
|
direction => {
|
||||||
const segmentationId = segDisplaySet.displaySetInstanceUID;
|
utils.handleSegmentChange({
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
direction,
|
||||||
|
segDisplaySet: segDisplaySet,
|
||||||
const { segments } = segmentation;
|
viewportId,
|
||||||
|
selectedSegmentObjectIndex,
|
||||||
const numberOfSegments = Object.keys(segments).length;
|
segmentationService,
|
||||||
|
});
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
[selectedSegment]
|
[selectedSegmentObjectIndex]
|
||||||
);
|
);
|
||||||
|
|
||||||
const hydrateSEG = useCallback(() => {
|
const hydrateSEG = useCallback(() => {
|
||||||
|
|||||||
@ -54,6 +54,7 @@ import PanelMeasurement from './panels/PanelMeasurement';
|
|||||||
import DicomUpload from './components/DicomUpload/DicomUpload';
|
import DicomUpload from './components/DicomUpload/DicomUpload';
|
||||||
import { useSegmentations } from './hooks/useSegmentations';
|
import { useSegmentations } from './hooks/useSegmentations';
|
||||||
import { StudySummaryFromMetadata } from './components/StudySummaryFromMetadata';
|
import { StudySummaryFromMetadata } from './components/StudySummaryFromMetadata';
|
||||||
|
import utils from './utils';
|
||||||
|
|
||||||
const { imageRetrieveMetadataProvider } = cornerstone.utilities;
|
const { imageRetrieveMetadataProvider } = cornerstone.utilities;
|
||||||
|
|
||||||
@ -254,5 +255,6 @@ export {
|
|||||||
PanelMeasurement,
|
PanelMeasurement,
|
||||||
DicomUpload,
|
DicomUpload,
|
||||||
StudySummaryFromMetadata,
|
StudySummaryFromMetadata,
|
||||||
|
utils,
|
||||||
};
|
};
|
||||||
export default cornerstoneExtension;
|
export default cornerstoneExtension;
|
||||||
|
|||||||
7
extensions/cornerstone/src/utils/index.ts
Normal file
7
extensions/cornerstone/src/utils/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { handleSegmentChange } from './segmentUtils';
|
||||||
|
|
||||||
|
const utils = {
|
||||||
|
handleSegmentChange,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default utils;
|
||||||
47
extensions/cornerstone/src/utils/segmentUtils.ts
Normal file
47
extensions/cornerstone/src/utils/segmentUtils.ts
Normal 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;
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Button } from '../../components/Button/Button';
|
import { Button } from '../../components/Button/Button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@ -97,6 +97,13 @@ const DataRow: React.FC<DataRowProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
const isTitleLong = title?.length > 25;
|
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) => {
|
const handleAction = (action: string, e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -181,7 +188,9 @@ const DataRow: React.FC<DataRowProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col ${isVisible ? '' : 'opacity-60'}`}>
|
<div
|
||||||
|
ref={rowRef}
|
||||||
|
className={`flex flex-col ${isVisible ? '' : 'opacity-60'}`}>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center ${
|
className={`flex items-center ${
|
||||||
isSelected ? 'bg-popover' : 'bg-muted'
|
isSelected ? 'bg-popover' : 'bg-muted'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user