fix(segmentation): List surface representations in the segmentation table for 3D views. (#5700)
This was done by using an array of representation types instead of a single type for a panel. The first element of the array is the primary type of the panel, and the rest are secondary types that can also be displayed in the panel. PR feedback: - added test to check number of segments in side panel for 3D only view - fixed jumping to segment in 3D only view - fixed exception when adding contour segment in 3D only view
This commit is contained in:
1 parent
83c9ce0f5f
commit
82e6a18735
27 files changed
+356
-293
No files matched your search
@@ -53,7 +53,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.15.6",
|
||||
"@cornerstonejs/dicom-image-loader": "4.15.7",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@ohif/core": "3.12.0-beta.121",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.121",
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/core": "4.15.6",
|
||||
"@cornerstonejs/dicom-image-loader": "4.15.6",
|
||||
"@cornerstonejs/core": "4.15.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.15.7",
|
||||
"@ohif/ui": "3.12.0-beta.121",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "1.8.21"
|
||||
|
||||
@@ -14,13 +14,13 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
|
||||
disableEditing,
|
||||
mode,
|
||||
disabled,
|
||||
segmentationRepresentationType,
|
||||
segmentationRepresentationTypes,
|
||||
} = useSegmentationTableContext('AddSegmentationRow');
|
||||
|
||||
// Check if we have at least one segmentation of the representation type for the panel this component is contained in.
|
||||
const hasRepresentationType =
|
||||
(!segmentationRepresentationType && data.length > 0) ||
|
||||
data.some(info => segmentationRepresentationType === info.representation?.type);
|
||||
(!segmentationRepresentationTypes && data.length > 0) ||
|
||||
data.some(info => segmentationRepresentationTypes?.includes(info.representation?.type));
|
||||
|
||||
if (hasRepresentationType && mode === 'collapsed') {
|
||||
return null;
|
||||
@@ -35,7 +35,11 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
|
||||
data-cy="addSegmentation"
|
||||
className={`group ${disabled ? 'pointer-events-none cursor-not-allowed opacity-70' : ''}`}
|
||||
onClick={() =>
|
||||
!disabled && onSegmentationAdd({ segmentationId: '', segmentationRepresentationType })
|
||||
!disabled &&
|
||||
onSegmentationAdd({
|
||||
segmentationId: '',
|
||||
segmentationRepresentationType: segmentationRepresentationTypes?.[0],
|
||||
})
|
||||
}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -50,9 +50,8 @@ const SegmentationCollapsedDropdownMenu = ({ children }: { children: React.React
|
||||
// Selector component - for the segmentation selection dropdown
|
||||
const SegmentationCollapsedSelector = () => {
|
||||
const { t } = useTranslation('SegmentationPanel.HeaderCollapsed');
|
||||
const { data, onSegmentationClick, segmentationRepresentationType } = useSegmentationTableContext(
|
||||
'SegmentationCollapsedSelector'
|
||||
);
|
||||
const { data, onSegmentationClick, segmentationRepresentationTypes } =
|
||||
useSegmentationTableContext('SegmentationCollapsedSelector');
|
||||
const { segmentation } = useSegmentationExpanded('SegmentationCollapsedSelector');
|
||||
|
||||
if (!data?.length) {
|
||||
@@ -63,8 +62,8 @@ const SegmentationCollapsedSelector = () => {
|
||||
// Only show segmentations of the representation type for this panel. Show all segmentations if no type is specified.
|
||||
.filter(
|
||||
seg =>
|
||||
!segmentationRepresentationType ||
|
||||
segmentationRepresentationType === seg.representation.type
|
||||
!segmentationRepresentationTypes ||
|
||||
segmentationRepresentationTypes.includes(seg.representation?.type)
|
||||
)
|
||||
.map(seg => ({
|
||||
id: seg.segmentation.segmentationId,
|
||||
@@ -132,14 +131,14 @@ const SegmentationCollapsedContent = ({ children }: { children: React.ReactNode
|
||||
const SegmentationCollapsedRoot: React.FC<{ children?: React.ReactNode }> = ({
|
||||
children = null,
|
||||
}) => {
|
||||
const { mode, data, segmentationRepresentationType, selectedSegmentationIdForType } =
|
||||
const { mode, data, segmentationRepresentationTypes, selectedSegmentationIdForType } =
|
||||
useSegmentationTableContext('SegmentationCollapsed');
|
||||
|
||||
// Find the segmentations for the representation type for this collapsed view.
|
||||
const segmentations = data.filter(
|
||||
segmentation =>
|
||||
!segmentationRepresentationType ||
|
||||
segmentationRepresentationType === segmentation.representation?.type
|
||||
!segmentationRepresentationTypes ||
|
||||
segmentationRepresentationTypes.includes(segmentation.representation?.type)
|
||||
);
|
||||
|
||||
// Check if we should render.
|
||||
|
||||
@@ -93,7 +93,7 @@ const SegmentationExpandedContent = ({ children }: { children: React.ReactNode }
|
||||
|
||||
// Main compound component
|
||||
const SegmentationExpandedRoot = ({ children }) => {
|
||||
const { data, activeSegmentationId, onSegmentationClick, mode, segmentationRepresentationType } =
|
||||
const { data, activeSegmentationId, onSegmentationClick, mode, segmentationRepresentationTypes } =
|
||||
useSegmentationTableContext('SegmentationExpanded');
|
||||
|
||||
const { ref: scrollableContainerRef, maxHeight } = useDynamicMaxHeight(data);
|
||||
@@ -116,8 +116,8 @@ const SegmentationExpandedRoot = ({ children }) => {
|
||||
{data
|
||||
.filter(
|
||||
segmentationInfo =>
|
||||
!segmentationRepresentationType ||
|
||||
segmentationInfo.representation.type === segmentationRepresentationType
|
||||
!segmentationRepresentationTypes ||
|
||||
segmentationRepresentationTypes.includes(segmentationInfo.representation.type)
|
||||
)
|
||||
.map(segmentationInfo => {
|
||||
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import React, { ReactNode, useState, Children, isValidElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PanelSection } from '../PanelSection';
|
||||
import { SegmentationTableProvider, SegmentationTableContextType } from './contexts';
|
||||
import {
|
||||
SegmentationTableProvider,
|
||||
SegmentationTableContextType,
|
||||
} from './contexts/SegmentationTableContext';
|
||||
import { SegmentationSegments } from './SegmentationSegments';
|
||||
import { SegmentStatistics } from './SegmentStatistics';
|
||||
import { SegmentationTableConfig } from './SegmentationTableConfig';
|
||||
|
||||
@@ -25,7 +25,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
setOutlineWidth,
|
||||
renderInactiveSegmentations,
|
||||
toggleRenderInactiveSegmentations,
|
||||
segmentationRepresentationType,
|
||||
segmentationRepresentationTypes,
|
||||
data,
|
||||
} = useSegmentationTableContext('SegmentationTableConfig');
|
||||
|
||||
@@ -49,21 +49,22 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
renderFill && renderOutline ? 'fill-and-outline' : renderOutline ? 'outline' : 'fill'
|
||||
}
|
||||
onValueChange={value => {
|
||||
const type = segmentationRepresentationTypes?.[0];
|
||||
if (value === 'fill-and-outline') {
|
||||
setRenderFill({ type: segmentationRepresentationType }, true);
|
||||
setRenderOutline({ type: segmentationRepresentationType }, true);
|
||||
setRenderFillInactive({ type: segmentationRepresentationType }, true);
|
||||
setRenderOutlineInactive({ type: segmentationRepresentationType }, true);
|
||||
setRenderFill({ type }, true);
|
||||
setRenderOutline({ type }, true);
|
||||
setRenderFillInactive({ type }, true);
|
||||
setRenderOutlineInactive({ type }, true);
|
||||
} else if (value === 'outline') {
|
||||
setRenderFill({ type: segmentationRepresentationType }, false);
|
||||
setRenderOutline({ type: segmentationRepresentationType }, true);
|
||||
setRenderFillInactive({ type: segmentationRepresentationType }, false);
|
||||
setRenderOutlineInactive({ type: segmentationRepresentationType }, true);
|
||||
setRenderFill({ type }, false);
|
||||
setRenderOutline({ type }, true);
|
||||
setRenderFillInactive({ type }, false);
|
||||
setRenderOutlineInactive({ type }, true);
|
||||
} else {
|
||||
setRenderFill({ type: segmentationRepresentationType }, true);
|
||||
setRenderOutline({ type: segmentationRepresentationType }, false);
|
||||
setRenderFillInactive({ type: segmentationRepresentationType }, true);
|
||||
setRenderOutlineInactive({ type: segmentationRepresentationType }, false);
|
||||
setRenderFill({ type }, true);
|
||||
setRenderOutline({ type }, false);
|
||||
setRenderFillInactive({ type }, true);
|
||||
setRenderOutlineInactive({ type }, false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -90,7 +91,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
className="mx-1 flex-1"
|
||||
value={[fillAlpha]}
|
||||
onValueChange={([value]) =>
|
||||
setFillAlpha({ type: segmentationRepresentationType }, value)
|
||||
setFillAlpha({ type: segmentationRepresentationTypes?.[0] }, value)
|
||||
}
|
||||
max={1}
|
||||
min={0}
|
||||
@@ -100,7 +101,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
className="mx-1 w-10 flex-none"
|
||||
value={fillAlpha}
|
||||
onChange={e =>
|
||||
setFillAlpha({ type: segmentationRepresentationType }, Number(e.target.value))
|
||||
setFillAlpha({ type: segmentationRepresentationTypes?.[0] }, Number(e.target.value))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -112,7 +113,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
<Slider
|
||||
value={[outlineWidth]}
|
||||
onValueChange={([value]) =>
|
||||
setOutlineWidth({ type: segmentationRepresentationType }, value)
|
||||
setOutlineWidth({ type: segmentationRepresentationTypes?.[0] }, value)
|
||||
}
|
||||
max={10}
|
||||
min={0}
|
||||
@@ -122,7 +123,10 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
||||
<Input
|
||||
value={outlineWidth}
|
||||
onChange={e =>
|
||||
setOutlineWidth({ type: segmentationRepresentationType }, Number(e.target.value))
|
||||
setOutlineWidth(
|
||||
{ type: segmentationRepresentationTypes?.[0] },
|
||||
Number(e.target.value)
|
||||
)
|
||||
}
|
||||
className="mx-1 w-10 flex-none text-center"
|
||||
/>
|
||||
|
||||
+5
-4
@@ -65,11 +65,12 @@ export interface SegmentationTableContextType {
|
||||
showSegmentIndex?: boolean;
|
||||
renderInactiveSegmentations?: boolean;
|
||||
|
||||
// The type segmentations displayed/filtered in this table. If undefined, show all types.
|
||||
segmentationRepresentationType?: string;
|
||||
// The types of segmentations displayed/filtered in this table. If undefined, show all types.
|
||||
// The first element is the primary type. Additional elements are secondary types.
|
||||
segmentationRepresentationTypes?: string[];
|
||||
|
||||
// The (last) selected segmentation ID for the representation type above.
|
||||
// If the type above is undefined, then it will store the last active segmentation ID.
|
||||
// The (last) selected segmentation ID for the representation types above.
|
||||
// If the representation types above is undefined, then it will store the last active segmentation ID.
|
||||
selectedSegmentationIdForType?: string;
|
||||
|
||||
// Function handlers
|
||||
|
||||
Reference in new issue
Block a user