fix: Navigate to contour should work (#5718)
* fix: Navigate to contour should work * Switched back to jump to segment center for hte unit tests
This commit is contained in:
parent
b2f6e1f237
commit
0fd517d6ab
@ -1608,7 +1608,7 @@ function commandsModule({
|
||||
? animationFunctionType
|
||||
: undefined;
|
||||
|
||||
segmentationService.jumpToSegmentCenter(
|
||||
segmentationService.jumpToSegmentNext(
|
||||
segmentationId,
|
||||
segmentIndex,
|
||||
undefined,
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
Enums as csToolsEnums,
|
||||
segmentation as cstSegmentation,
|
||||
Types as cstTypes,
|
||||
annotation as cstAnnotation,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import { PubSubService, Types as OHIFTypes } from '@ohif/core';
|
||||
@ -24,6 +25,7 @@ import { VOLUME_LOADER_SCHEME } from '../../constants';
|
||||
import { mapROIContoursToRTStructData } from './RTSTRUCT/mapROIContoursToRTStructData';
|
||||
import { SegmentationPresentation, SegmentationPresentationItem } from '../../types/Presentation';
|
||||
import { EasingFunctionEnum, EasingFunctionMap } from '../../utils/transitions';
|
||||
import { ViewReference } from '@cornerstonejs/core/types';
|
||||
|
||||
const {
|
||||
Labelmap: LABELMAP,
|
||||
@ -1246,6 +1248,123 @@ class SegmentationService extends PubSubService {
|
||||
cstSegmentation.removeSegmentationRepresentations(viewportId, specifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jumps to the next slice that contains the specified segment in the viewport.
|
||||
* For labelmaps, it jumps to the segment center. For contours, it cycles through
|
||||
* all slices that contain contour data for the segment.
|
||||
*
|
||||
* @param segmentationId - The ID of the segmentation
|
||||
* @param segmentIndex - The index of the segment to jump to
|
||||
* @param viewportId - Optional viewport ID. If not provided, applies to all viewports with this segmentation
|
||||
* @param highlightAlpha - Alpha value for highlighting (0-1)
|
||||
* @param highlightSegment - Whether to highlight the segment after jumping
|
||||
* @param animationLength - Length of highlight animation in milliseconds
|
||||
* @param highlightHideOthers - Whether to hide other segments during highlight
|
||||
* @param animationFunctionType - The easing function to use for animation
|
||||
*/
|
||||
public jumpToSegmentNext(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
forViewportId?: string,
|
||||
direction = 1,
|
||||
highlightAlpha = 0.9,
|
||||
highlightSegment = true,
|
||||
animationLength = 750,
|
||||
highlightHideOthers = false,
|
||||
animationFunctionType: EasingFunctionEnum = EasingFunctionEnum.EASE_IN_OUT
|
||||
): void {
|
||||
const viewportIds = forViewportId
|
||||
? [forViewportId]
|
||||
: this.getViewportIdsWithSegmentation(segmentationId);
|
||||
|
||||
viewportIds.forEach(viewportId => {
|
||||
const representations = this.getSegmentationRepresentations(viewportId, {
|
||||
segmentationId,
|
||||
});
|
||||
|
||||
if (!representations || representations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const representation = representations[0];
|
||||
const { type } = representation;
|
||||
|
||||
// For labelmaps, use the existing jumpToSegmentCenter method
|
||||
if (type !== CONTOUR) {
|
||||
this.jumpToSegmentCenter(
|
||||
segmentationId,
|
||||
segmentIndex,
|
||||
viewportId,
|
||||
highlightAlpha,
|
||||
highlightSegment,
|
||||
animationLength,
|
||||
highlightHideOthers,
|
||||
animationFunctionType
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { viewport } = getEnabledElementByViewportId(viewportId);
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewRefs = this._getContourViewReferences(segmentationId, viewport, segmentIndex);
|
||||
if (!viewRefs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current slice index
|
||||
const currentSliceIndex = viewport.getCurrentImageIdIndex();
|
||||
let nearestSliceIndex = null;
|
||||
let loopSliceIndex = null;
|
||||
|
||||
for (const [sliceIndex, viewRef] of viewRefs.entries()) {
|
||||
// Track loop index for wraparound (smallest for forward, largest for backward)
|
||||
if (direction > 0) {
|
||||
if (loopSliceIndex === null || sliceIndex < loopSliceIndex) {
|
||||
loopSliceIndex = sliceIndex;
|
||||
}
|
||||
} else {
|
||||
if (loopSliceIndex === null || sliceIndex > loopSliceIndex) {
|
||||
loopSliceIndex = sliceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (direction > 0) {
|
||||
// Forward direction: find nearest slice after current
|
||||
if (sliceIndex <= currentSliceIndex) {
|
||||
continue;
|
||||
}
|
||||
if (nearestSliceIndex === null || sliceIndex < nearestSliceIndex) {
|
||||
nearestSliceIndex = sliceIndex;
|
||||
}
|
||||
} else {
|
||||
// Backward direction: find nearest slice before current
|
||||
if (sliceIndex >= currentSliceIndex) {
|
||||
continue;
|
||||
}
|
||||
if (nearestSliceIndex === null || sliceIndex > nearestSliceIndex) {
|
||||
nearestSliceIndex = sliceIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wraparound: if no slice found in direction, use loop index
|
||||
nearestSliceIndex = nearestSliceIndex ?? loopSliceIndex;
|
||||
if (nearestSliceIndex === null) {
|
||||
return;
|
||||
}
|
||||
const viewRef = viewRefs.get(nearestSliceIndex);
|
||||
viewport.setViewReference(viewRef);
|
||||
viewport.render();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Jumps the viewport to the center of hte given segment.
|
||||
* Only works for labelmaps, and may result in not showing any contours.
|
||||
*/
|
||||
public jumpToSegmentCenter(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
@ -1943,6 +2062,62 @@ class SegmentationService extends PubSubService {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets slice indices for contour segmentations by checking view references from annotation metadata
|
||||
* @private
|
||||
*/
|
||||
protected _getContourViewReferences(
|
||||
segmentationId: string,
|
||||
viewport: csTypes.IViewport,
|
||||
segmentIndex?: number
|
||||
): Map<number, ViewReference> | undefined {
|
||||
const segmentation = cstSegmentation.state.getSegmentation(segmentationId);
|
||||
const contourData = segmentation.representationData[CONTOUR];
|
||||
|
||||
if (!contourData || !contourData.annotationUIDsMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewReferences = new Map<number, ViewReference>();
|
||||
// Iterate through the annotationUIDsMap
|
||||
contourData.annotationUIDsMap.forEach((annotationUIDs, currentSegmentIndex) => {
|
||||
// Filter by segment index if specified
|
||||
if (segmentIndex !== undefined && currentSegmentIndex !== segmentIndex) {
|
||||
return;
|
||||
}
|
||||
// Process each annotation UID
|
||||
annotationUIDs.forEach(annotationUID => {
|
||||
const annotation = cstAnnotation.state.getAnnotation(annotationUID);
|
||||
|
||||
if (!annotation?.metadata) {
|
||||
return;
|
||||
}
|
||||
const { metadata } = annotation;
|
||||
// Check if the viewport can view this annotation's view reference with navigation
|
||||
const isViewable = viewport.isReferenceViewable(metadata, {
|
||||
withNavigation: true,
|
||||
});
|
||||
|
||||
if (!isViewable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sliceIndex } = metadata;
|
||||
if (sliceIndex === undefined) {
|
||||
console.warn("Can't find slice index:", metadata);
|
||||
return;
|
||||
}
|
||||
|
||||
viewReferences.set(sliceIndex, metadata);
|
||||
});
|
||||
});
|
||||
|
||||
if (viewReferences.size === 0) {
|
||||
return;
|
||||
}
|
||||
return viewReferences;
|
||||
}
|
||||
}
|
||||
|
||||
export default SegmentationService;
|
||||
|
||||
@ -56,6 +56,7 @@ setActiveSegmentation(viewportId, segmentationId)
|
||||
getSegmentations()
|
||||
getSegmentation(segmentationId)
|
||||
jumpToSegmentCenter(segmentationId, segmentIndex, viewportId)
|
||||
jumpToSegmentNext(segmentationId, segmentIndex, forViewportId?, direction?, highlightAlpha?, highlightSegment?, animationLength?, highlightHideOthers?, animationFunctionType?)
|
||||
highlightSegment(segmentationId, segmentIndex, viewportId)
|
||||
```
|
||||
|
||||
@ -174,3 +175,59 @@ segmentationService.setSegmentColor(
|
||||
[255, 0, 0, 255] // RGBA
|
||||
);
|
||||
```
|
||||
|
||||
### Navigation
|
||||
|
||||
#### jumpToSegmentNext
|
||||
|
||||
Jumps to the next or previous slice that contains the specified segment in the viewport. This method handles both labelmap and contour segmentations:
|
||||
- For **labelmaps**: Jumps to the segment center
|
||||
- For **contours**: Cycles through all slices that contain contour data for the segment in the specified direction
|
||||
|
||||
```typescript
|
||||
segmentationService.jumpToSegmentNext(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
forViewportId?: string, // Optional viewport ID. If not provided, applies to all viewports with this segmentation
|
||||
direction?: number, // 1 for forward (default), -1 for backward
|
||||
highlightAlpha?: number, // Alpha value for highlighting (0-1), default: 0.9
|
||||
highlightSegment?: boolean, // Whether to highlight the segment after jumping, default: true
|
||||
animationLength?: number, // Length of highlight animation in milliseconds, default: 750
|
||||
highlightHideOthers?: boolean, // Whether to hide other segments during highlight, default: false
|
||||
animationFunctionType?: EasingFunctionEnum // The easing function to use for animation, default: EASE_IN_OUT
|
||||
)
|
||||
```
|
||||
|
||||
**Navigation Behavior:**
|
||||
- **Forward (direction = 1)**: Finds the next slice after the current one that contains the segment. If no slice is found after the current one, wraps around to the first slice with the segment.
|
||||
- **Backward (direction = -1)**: Finds the previous slice before the current one that contains the segment. If no slice is found before the current one, wraps around to the last slice with the segment.
|
||||
|
||||
**Example Usage:**
|
||||
|
||||
```typescript
|
||||
// Jump to next slice with segment 1
|
||||
segmentationService.jumpToSegmentNext(
|
||||
segmentationId,
|
||||
1, // segmentIndex
|
||||
'viewport-1'
|
||||
);
|
||||
|
||||
// Jump to previous slice with segment 2, with custom highlighting
|
||||
segmentationService.jumpToSegmentNext(
|
||||
segmentationId,
|
||||
2, // segmentIndex
|
||||
'viewport-1',
|
||||
-1, // backward direction
|
||||
0.95, // highlightAlpha
|
||||
true, // highlightSegment
|
||||
1000, // animationLength in ms
|
||||
true // highlightHideOthers
|
||||
);
|
||||
|
||||
// Apply to all viewports with this segmentation
|
||||
segmentationService.jumpToSegmentNext(
|
||||
segmentationId,
|
||||
1 // segmentIndex
|
||||
// forViewportId omitted - applies to all viewports
|
||||
);
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue
Block a user