fix(segmentation): restore navigation for duplicated contour segments (#6038)
--------- Co-authored-by: diattamo <mmddiatta@gmail.com>
This commit is contained in:
parent
9a2d2c3d13
commit
0c1ca6b04a
@ -2403,14 +2403,25 @@ function commandsModule({
|
|||||||
targetSegmentInfo?: SegmentInfo;
|
targetSegmentInfo?: SegmentInfo;
|
||||||
}) => {
|
}) => {
|
||||||
if (!targetSegmentInfo) {
|
if (!targetSegmentInfo) {
|
||||||
|
const sourceSegmentation = segmentationService.getSegmentation(
|
||||||
|
sourceSegmentInfo.segmentationId
|
||||||
|
);
|
||||||
|
const sourceCachedStats =
|
||||||
|
sourceSegmentation?.segments?.[sourceSegmentInfo.segmentIndex]?.cachedStats;
|
||||||
|
|
||||||
targetSegmentInfo = {
|
targetSegmentInfo = {
|
||||||
segmentationId: sourceSegmentInfo.segmentationId,
|
segmentationId: sourceSegmentInfo.segmentationId,
|
||||||
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
|
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
|
||||||
sourceSegmentInfo.segmentationId
|
sourceSegmentInfo.segmentationId
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Copy source cachedStats so jump-to-segment navigation works on the duplicate segment.
|
||||||
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
|
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
|
||||||
segmentIndex: targetSegmentInfo.segmentIndex,
|
segmentIndex: targetSegmentInfo.segmentIndex,
|
||||||
|
...(sourceCachedStats && {
|
||||||
|
cachedStats: csUtils.deepClone(sourceCachedStats) as Record<string, unknown>,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -896,6 +896,7 @@ class SegmentationService extends PubSubService {
|
|||||||
active?: boolean;
|
active?: boolean;
|
||||||
color?: csTypes.Color; // Add color type
|
color?: csTypes.Color; // Add color type
|
||||||
visibility?: boolean; // Add visibility option
|
visibility?: boolean; // Add visibility option
|
||||||
|
cachedStats?: Record<string, unknown>;
|
||||||
} = {}
|
} = {}
|
||||||
): void {
|
): void {
|
||||||
if (config?.segmentIndex === 0) {
|
if (config?.segmentIndex === 0) {
|
||||||
|
|||||||
@ -1,20 +1,10 @@
|
|||||||
import {
|
import { expect, test, visitStudy, waitForViewportsRendered, getSvgAttribute } from './utils';
|
||||||
expect,
|
|
||||||
test,
|
|
||||||
visitStudy,
|
|
||||||
waitForViewportsRendered,
|
|
||||||
} from './utils';
|
|
||||||
import { getSvgAttribute } from './utils/getSvgAttribute';
|
|
||||||
|
|
||||||
const studyInstanceUID = '1.2.840.113619.2.290.3.3767434740.226.1600859119.501';
|
const studyInstanceUID = '1.2.840.113619.2.290.3.3767434740.226.1600859119.501';
|
||||||
const defaultSegment0Name = 'Threshold';
|
const defaultSegment0Name = 'Threshold';
|
||||||
const defaultSegment1Name = 'Big Sphere';
|
const defaultSegment1Name = 'Big Sphere';
|
||||||
|
|
||||||
test.beforeEach(async ({
|
test.beforeEach(async ({ page, leftPanelPageObject, DOMOverlayPageObject }) => {
|
||||||
page,
|
|
||||||
leftPanelPageObject,
|
|
||||||
DOMOverlayPageObject,
|
|
||||||
}) => {
|
|
||||||
const mode = 'segmentation';
|
const mode = 'segmentation';
|
||||||
await visitStudy(page, studyInstanceUID, mode, 2000);
|
await visitStudy(page, studyInstanceUID, mode, 2000);
|
||||||
|
|
||||||
@ -26,7 +16,6 @@ test.beforeEach(async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should duplicate a contour segment and add a new row to the panel', async ({
|
test('should duplicate a contour segment and add a new row to the panel', async ({
|
||||||
page,
|
|
||||||
rightPanelPageObject,
|
rightPanelPageObject,
|
||||||
}) => {
|
}) => {
|
||||||
const panel = rightPanelPageObject.contourSegmentationPanel.panel;
|
const panel = rightPanelPageObject.contourSegmentationPanel.panel;
|
||||||
@ -44,32 +33,43 @@ test('should duplicate a contour segment and add a new row to the panel', async
|
|||||||
|
|
||||||
//New segment's default name is formatted as "Segment {segmentCount}"
|
//New segment's default name is formatted as "Segment {segmentCount}"
|
||||||
const newSegmentLocator = panel.nthSegment(initialCount).title;
|
const newSegmentLocator = panel.nthSegment(initialCount).title;
|
||||||
await expect(newSegmentLocator, 'Expected correct title for duplicated segment').toHaveText(`Segment 5`);
|
await expect(newSegmentLocator, 'Expected correct title for duplicated segment').toHaveText(
|
||||||
|
`Segment 5`
|
||||||
|
);
|
||||||
|
|
||||||
// Original segment titles should be unchanged
|
// Original segment titles should be unchanged
|
||||||
await expect(panel.nthSegment(0).title).toHaveText(defaultSegment0Name);
|
await expect(panel.nthSegment(0).title).toHaveText(defaultSegment0Name);
|
||||||
await expect(panel.nthSegment(1).title).toHaveText(defaultSegment1Name);
|
await expect(panel.nthSegment(1).title).toHaveText(defaultSegment1Name);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should duplicate the same segment multiple times', async ({
|
test('should duplicate the same segment multiple times', async ({ rightPanelPageObject }) => {
|
||||||
page,
|
|
||||||
rightPanelPageObject,
|
|
||||||
}) => {
|
|
||||||
const panel = rightPanelPageObject.contourSegmentationPanel.panel;
|
const panel = rightPanelPageObject.contourSegmentationPanel.panel;
|
||||||
|
|
||||||
const segment0 = panel.nthSegment(0);
|
const segment0 = panel.nthSegment(0);
|
||||||
|
|
||||||
await segment0.actions.duplicate();
|
await segment0.actions.duplicate();
|
||||||
expect(await panel.getSegmentCount(), 'Expected one additional segment row after duplicating').toBe(5);
|
expect(
|
||||||
|
await panel.getSegmentCount(),
|
||||||
|
'Expected one additional segment row after duplicating'
|
||||||
|
).toBe(5);
|
||||||
|
|
||||||
const firstDuplicateTitleLocator = panel.nthSegment(4).title;
|
const firstDuplicateTitleLocator = panel.nthSegment(4).title;
|
||||||
await expect(firstDuplicateTitleLocator, 'Expected correct title for first duplicated segment').toHaveText(`Segment 5`);
|
await expect(
|
||||||
|
firstDuplicateTitleLocator,
|
||||||
|
'Expected correct title for first duplicated segment'
|
||||||
|
).toHaveText(`Segment 5`);
|
||||||
|
|
||||||
await segment0.actions.duplicate();
|
await segment0.actions.duplicate();
|
||||||
expect(await panel.getSegmentCount(), 'Expected another segment row after duplicating the same segment again').toBe(6);
|
expect(
|
||||||
|
await panel.getSegmentCount(),
|
||||||
|
'Expected another segment row after duplicating the same segment again'
|
||||||
|
).toBe(6);
|
||||||
|
|
||||||
const secondDuplicateTitleLocator = panel.nthSegment(5).title;
|
const secondDuplicateTitleLocator = panel.nthSegment(5).title;
|
||||||
await expect(secondDuplicateTitleLocator, 'Expected correct title for second duplicated segment').toHaveText(`Segment 6`);
|
await expect(
|
||||||
|
secondDuplicateTitleLocator,
|
||||||
|
'Expected correct title for second duplicated segment'
|
||||||
|
).toHaveText(`Segment 6`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render the duplicated contour on the viewport', async ({
|
test('should render the duplicated contour on the viewport', async ({
|
||||||
@ -84,10 +84,16 @@ test('should render the duplicated contour on the viewport', async ({
|
|||||||
await segment0.toggleVisibility();
|
await segment0.toggleVisibility();
|
||||||
await segment0.click();
|
await segment0.click();
|
||||||
|
|
||||||
const sourceSvgPath = await getSvgAttribute({viewportPageObject, svgInnerElement: 'path', attributeName: 'd'});
|
const sourceSvgPath = await getSvgAttribute({
|
||||||
|
viewportPageObject,
|
||||||
|
svgInnerElement: 'path',
|
||||||
|
attributeName: 'd',
|
||||||
|
});
|
||||||
expect(sourceSvgPath, 'Expected a visible SVG path for the source segment').not.toBeNull();
|
expect(sourceSvgPath, 'Expected a visible SVG path for the source segment').not.toBeNull();
|
||||||
const sourceSvgPaths = (await viewportPageObject.getById('default')).svg('path');
|
const sourceSvgPaths = (await viewportPageObject.getById('default')).svg('path');
|
||||||
expect(sourceSvgPaths, 'Expected only one SVG path element for the original segment').toHaveCount(1);
|
expect(sourceSvgPaths, 'Expected only one SVG path element for the original segment').toHaveCount(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
// New segment is at index 4
|
// New segment is at index 4
|
||||||
await segment0.actions.duplicate();
|
await segment0.actions.duplicate();
|
||||||
@ -97,13 +103,108 @@ test('should render the duplicated contour on the viewport', async ({
|
|||||||
await segment0.toggleVisibility();
|
await segment0.toggleVisibility();
|
||||||
await duplicatedSegment.click();
|
await duplicatedSegment.click();
|
||||||
|
|
||||||
const duplicatedSvgPath = await getSvgAttribute({viewportPageObject, svgInnerElement: 'path', attributeName: 'd'});
|
const duplicatedSvgPath = await getSvgAttribute({
|
||||||
expect(duplicatedSvgPath, 'Expected a visible SVG path for the duplicated segment').not.toBeNull();
|
viewportPageObject,
|
||||||
|
svgInnerElement: 'path',
|
||||||
|
attributeName: 'd',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
duplicatedSvgPath,
|
||||||
|
'Expected a visible SVG path for the duplicated segment'
|
||||||
|
).not.toBeNull();
|
||||||
const duplicatedSvgPaths = (await viewportPageObject.getById('default')).svg('path');
|
const duplicatedSvgPaths = (await viewportPageObject.getById('default')).svg('path');
|
||||||
expect(duplicatedSvgPaths, 'Expected only one SVG path element for the duplicated segment').toHaveCount(1);
|
expect(
|
||||||
|
duplicatedSvgPaths,
|
||||||
|
'Expected only one SVG path element for the duplicated segment'
|
||||||
|
).toHaveCount(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
duplicatedSvgPath,
|
duplicatedSvgPath,
|
||||||
'Expected the duplicated segment to have the same SVG path as the source'
|
'Expected the duplicated segment to have the same SVG path as the source'
|
||||||
).toBe(sourceSvgPath);
|
).toBe(sourceSvgPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should navigate to the correct instance number when a duplicated contour segment is selected', async ({
|
||||||
|
rightPanelPageObject,
|
||||||
|
viewportPageObject,
|
||||||
|
}) => {
|
||||||
|
const panel = rightPanelPageObject.contourSegmentationPanel.panel;
|
||||||
|
|
||||||
|
// get instance overlay of the contour segment at index 0
|
||||||
|
const originalSegment = panel.nthSegment(0);
|
||||||
|
await originalSegment.click();
|
||||||
|
const originalSegmentInstanceInfo = (await viewportPageObject.getById('default')).overlayText
|
||||||
|
.bottomRight.instanceNumber;
|
||||||
|
expect(
|
||||||
|
originalSegmentInstanceInfo,
|
||||||
|
'Expected instance information to be displayed in the viewport overlay'
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
originalSegmentInstanceInfo,
|
||||||
|
'Expected instance information to be slice 46 for the Threshold segment'
|
||||||
|
).toHaveText('I:46 (46/47)');
|
||||||
|
|
||||||
|
// Duplicate segment so new segment is at index 4
|
||||||
|
await originalSegment.actions.duplicate();
|
||||||
|
|
||||||
|
//click another segment to ensure instance number changes accordingly
|
||||||
|
await panel.nthSegment(2).click();
|
||||||
|
const anotherSegmentInstanceInfo = (await viewportPageObject.getById('default')).overlayText
|
||||||
|
.bottomRight.instanceNumber;
|
||||||
|
expect(
|
||||||
|
anotherSegmentInstanceInfo,
|
||||||
|
'Expected instance information to be displayed in the viewport overlay'
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
anotherSegmentInstanceInfo,
|
||||||
|
'Expected instance information to be different from original contour'
|
||||||
|
).not.toHaveText('I:46 (46/47)');
|
||||||
|
|
||||||
|
//click duplicated segment to ensure instance number is consistent with original segment
|
||||||
|
const duplicatedSegment = panel.nthSegment(4);
|
||||||
|
await duplicatedSegment.click();
|
||||||
|
const duplicatedSegmentInstanceInfoAfter = (await viewportPageObject.getById('default'))
|
||||||
|
.overlayText.bottomRight.instanceNumber;
|
||||||
|
expect(
|
||||||
|
duplicatedSegmentInstanceInfoAfter,
|
||||||
|
'Expected instance information to be displayed in the viewport overlay'
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
duplicatedSegmentInstanceInfoAfter,
|
||||||
|
'Expected instance information to be same as original contour after clicking duplicated segment'
|
||||||
|
).toHaveText('I:46 (46/47)');
|
||||||
|
|
||||||
|
//verify the svg paths are the same for the original and duplicated segments
|
||||||
|
await rightPanelPageObject.contourSegmentationPanel.segmentsVisibilityToggle.click();
|
||||||
|
await originalSegment.toggleVisibility();
|
||||||
|
await originalSegment.click();
|
||||||
|
const originalSegmentSvgPath = await getSvgAttribute({
|
||||||
|
viewportPageObject,
|
||||||
|
svgInnerElement: 'path',
|
||||||
|
attributeName: 'd',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
originalSegmentSvgPath,
|
||||||
|
'Expected a visible SVG path for the original segment'
|
||||||
|
).not.toBeNull();
|
||||||
|
|
||||||
|
// hide original segment to show duplicate only
|
||||||
|
await originalSegment.toggleVisibility();
|
||||||
|
|
||||||
|
await duplicatedSegment.toggleVisibility();
|
||||||
|
await duplicatedSegment.click();
|
||||||
|
const duplicatedSegmentSvgPath = await getSvgAttribute({
|
||||||
|
viewportPageObject,
|
||||||
|
svgInnerElement: 'path',
|
||||||
|
attributeName: 'd',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
duplicatedSegmentSvgPath,
|
||||||
|
'Expected a visible SVG path for the duplicated segment'
|
||||||
|
).not.toBeNull();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
duplicatedSegmentSvgPath,
|
||||||
|
'Expected the duplicated segment to have the same SVG path as the original segment'
|
||||||
|
).toBe(originalSegmentSvgPath);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user