Skip to content

Commit

Permalink
feat(segmentation): Enhanced segmentation panel design for TMTV (#3988)
Browse files Browse the repository at this point in the history
Co-authored-by: Alireza <[email protected]>
  • Loading branch information
IbrahimCSAE and sedghi authored Apr 3, 2024
1 parent e8e2092 commit 9f3235f
Show file tree
Hide file tree
Showing 51 changed files with 2,001 additions and 572 deletions.
6 changes: 5 additions & 1 deletion extensions/cornerstone-dicom-seg/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ const commandsModule = ({
loadSegmentationDisplaySetsForViewport: async ({ viewportId, displaySets }) => {
// Todo: handle adding more than one segmentation
const displaySet = displaySets[0];
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
displaySet.referencedDisplaySetInstanceUID
);

updateViewportsForSegmentationRendering({
viewportId,
Expand All @@ -221,7 +224,8 @@ const commandsModule = ({

const boundFn = segmentationService[serviceFunction].bind(segmentationService);
const segmentationId = await boundFn(segDisplaySet, null, suppressEvents);

const segmentation = segmentationService.getSegmentation(segmentationId);
segmentation.description = `S${referencedDisplaySet.SeriesNumber}: ${referencedDisplaySet.SeriesDescription}`;
return segmentationId;
},
});
Expand Down
11 changes: 11 additions & 0 deletions extensions/cornerstone-dicom-seg/src/getPanelModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { useAppConfig } from '@state';
import { Toolbox } from '@ohif/ui';
import PanelSegmentation from './panels/PanelSegmentation';
import { SegmentationPanelMode } from './types/segmentation';

const getPanelModule = ({
commandsManager,
Expand All @@ -17,6 +18,9 @@ const getPanelModule = ({
const [appConfig] = useAppConfig();

const disableEditingForMode = customizationService.get('segmentation.disableEditing');
const segmentationPanelMode =
customizationService.get('segmentation.segmentationPanelMode')?.value ||
SegmentationPanelMode.Dropdown;

return (
<PanelSegmentation
Expand All @@ -26,12 +30,18 @@ const getPanelModule = ({
configuration={{
...configuration,
disableEditing: appConfig.disableEditing || disableEditingForMode?.value,
segmentationPanelMode: segmentationPanelMode,
}}
/>
);
};

const wrappedPanelSegmentationWithTools = configuration => {
const [appConfig] = useAppConfig();
const segmentationPanelMode =
customizationService.get('segmentation.segmentationPanelMode')?.value ||
SegmentationPanelMode.Dropdown;

return (
<>
<Toolbox
Expand All @@ -50,6 +60,7 @@ const getPanelModule = ({
extensionManager={extensionManager}
configuration={{
...configuration,
segmentationPanelMode: segmentationPanelMode,
}}
/>
</>
Expand Down
4 changes: 3 additions & 1 deletion extensions/cornerstone-dicom-seg/src/getToolbarModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export function getToolbarModule({ commandsManager, servicesManager }) {
return [
{
name: 'evaluate.cornerstone.segmentation',
evaluate: ({ viewportId, button, toolNames }) => {
evaluate: ({ viewportId, button, toolNames, disabledText }) => {
// Todo: we need to pass in the button section Id since we are kind of
// forcing the button to have black background since initially
// it is designed for the toolbox not the toolbar on top
Expand All @@ -13,6 +13,7 @@ export function getToolbarModule({ commandsManager, servicesManager }) {
return {
disabled: true,
className: '!text-common-bright !bg-black opacity-50',
disabledText: disabledText ?? 'No segmentations available',
};
}

Expand All @@ -28,6 +29,7 @@ export function getToolbarModule({ commandsManager, servicesManager }) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

Expand Down
123 changes: 69 additions & 54 deletions extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { createReportAsync } from '@ohif/extension-default';
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { SegmentationGroupTable } from '@ohif/ui';

import { SegmentationGroupTable, SegmentationGroupTableExpanded } from '@ohif/ui';
import { SegmentationPanelMode } from '../types/segmentation';
import callInputDialog from './callInputDialog';
import callColorPickerDialog from './colorPickerDialog';
import { useTranslation } from 'react-i18next';

const components = {
[SegmentationPanelMode.Expanded]: SegmentationGroupTableExpanded,
[SegmentationPanelMode.Dropdown]: SegmentationGroupTable,
};

export default function PanelSegmentation({
servicesManager,
commandsManager,
Expand Down Expand Up @@ -170,6 +175,22 @@ export default function PanelSegmentation({

const onToggleSegmentationVisibility = segmentationId => {
segmentationService.toggleSegmentationVisibility(segmentationId);
const segmentation = segmentationService.getSegmentation(segmentationId);
const isVisible = segmentation.isVisible;
const segments = segmentation.segments;

const toolGroupIds = getToolGroupIds(segmentationId);

toolGroupIds.forEach(toolGroupId => {
segments.forEach((segment, segmentIndex) => {
segmentationService.setSegmentVisibility(
segmentationId,
segmentIndex,
isVisible,
toolGroupId
);
});
});
};

const _setSegmentationConfiguration = useCallback(
Expand Down Expand Up @@ -221,59 +242,53 @@ export default function PanelSegmentation({
});
};

const SegmentationGroupTableComponent = components[configuration?.segmentationPanelMode];

return (
<>
<div className="ohif-scrollbar flex min-h-0 flex-auto select-none flex-col justify-between overflow-auto">
<SegmentationGroupTable
title={t('Segmentations')}
segmentations={segmentations}
disableEditing={configuration.disableEditing}
activeSegmentationId={selectedSegmentationId || ''}
onSegmentationAdd={onSegmentationAdd}
onSegmentationClick={onSegmentationClick}
onSegmentationDelete={onSegmentationDelete}
onSegmentationDownload={onSegmentationDownload}
onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
storeSegmentation={storeSegmentation}
onSegmentationEdit={onSegmentationEdit}
onSegmentClick={onSegmentClick}
onSegmentEdit={onSegmentEdit}
onSegmentAdd={onSegmentAdd}
onSegmentColorClick={onSegmentColorClick}
onSegmentDelete={onSegmentDelete}
onToggleSegmentVisibility={onToggleSegmentVisibility}
onToggleSegmentLock={onToggleSegmentLock}
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
showDeleteSegment={true}
segmentationConfig={{ initialConfig: segmentationConfiguration }}
setRenderOutline={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value)
}
setOutlineOpacityActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineOpacity', value)
}
setRenderFill={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderFill', value)
}
setRenderInactiveSegmentations={value =>
_setSegmentationConfiguration(
selectedSegmentationId,
'renderInactiveSegmentations',
value
)
}
setOutlineWidthActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineWidthActive', value)
}
setFillAlpha={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlpha', value)
}
setFillAlphaInactive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
}
/>
</div>
</>
<SegmentationGroupTableComponent
title={t('Segmentations')}
segmentations={segmentations}
disableEditing={configuration.disableEditing}
activeSegmentationId={selectedSegmentationId || ''}
onSegmentationAdd={onSegmentationAdd}
onSegmentationClick={onSegmentationClick}
onSegmentationDelete={onSegmentationDelete}
onSegmentationDownload={onSegmentationDownload}
onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
storeSegmentation={storeSegmentation}
onSegmentationEdit={onSegmentationEdit}
onSegmentClick={onSegmentClick}
onSegmentEdit={onSegmentEdit}
onSegmentAdd={onSegmentAdd}
onSegmentColorClick={onSegmentColorClick}
onSegmentDelete={onSegmentDelete}
onToggleSegmentVisibility={onToggleSegmentVisibility}
onToggleSegmentLock={onToggleSegmentLock}
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
showDeleteSegment={true}
segmentationConfig={{ initialConfig: segmentationConfiguration }}
setRenderOutline={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value)
}
setOutlineOpacityActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineOpacity', value)
}
setRenderFill={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderFill', value)
}
setRenderInactiveSegmentations={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderInactiveSegmentations', value)
}
setOutlineWidthActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineWidthActive', value)
}
setFillAlpha={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlpha', value)
}
setFillAlphaInactive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
}
/>
);
}

Expand Down
4 changes: 4 additions & 0 deletions extensions/cornerstone-dicom-seg/src/types/segmentation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SegmentationPanelMode {
Expanded = 'expanded',
Dropdown = 'dropdown',
}
12 changes: 8 additions & 4 deletions extensions/cornerstone/src/getToolbarModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
// enabled or not
{
name: 'evaluate.cornerstoneTool',
evaluate: ({ viewportId, button }) => {
evaluate: ({ viewportId, button, disabledText }) => {
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

if (!toolGroup) {
Expand All @@ -34,6 +34,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

Expand Down Expand Up @@ -109,7 +110,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
},
{
name: 'evaluate.cornerstoneTool.toggle',
evaluate: ({ viewportId, button }) => {
evaluate: ({ viewportId, button, disabledText }) => {
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

if (!toolGroup) {
Expand All @@ -121,6 +122,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

Expand Down Expand Up @@ -168,13 +170,14 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
},
{
name: 'evaluate.not3D',
evaluate: ({ viewportId, button }) => {
evaluate: ({ viewportId, disabledText }) => {
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);

if (viewport?.type === 'volume3d') {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}
},
Expand Down Expand Up @@ -211,7 +214,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
},
{
name: 'evaluate.mpr',
evaluate: ({ viewportId, button }) => {
evaluate: ({ viewportId, disabledText = 'Selected viewport is not reconstructable' }) => {
const { protocol } = hangingProtocolService.getActiveProtocol();

const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
Expand All @@ -230,6 +233,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,7 @@ class SegmentationService extends PubSubService {
referencedVolumeId: volumeId, // Todo: this is so ugly
},
},
description: `S${displaySet.SeriesNumber}: ${displaySet.SeriesDescription}`,
};

this.addOrUpdateSegmentation(segmentation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type Segment = {
isVisible: boolean;
// whether the segment is locked
isLocked: boolean;
// display texts
displayText?: string[];
};

type Segmentation = {
Expand Down
13 changes: 1 addition & 12 deletions extensions/default/src/Toolbar/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ export function Toolbar({ servicesManager }) {
}

const { id, Component, componentProps } = toolDef;
const { disabled } = componentProps;

const tool = (
<Component
key={id}
Expand All @@ -33,16 +31,7 @@ export function Toolbar({ servicesManager }) {
/>
);

return disabled ? (
<Tooltip
key={id}
position="bottom"
content={componentProps.label}
secondaryContent={'Not available on the current viewport'}
>
<div className={classnames('mr-1')}>{tool}</div>
</Tooltip>
) : (
return (
<div
key={id}
className="mr-1"
Expand Down
7 changes: 7 additions & 0 deletions extensions/tmtv/.webpack/webpack.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ const pkg = require('./../package.json');
const ROOT_DIR = path.join(__dirname, './..');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const ENTRY = {
app: `${SRC_DIR}/index.tsx`,
};

const outputName = `ohif-${pkg.name.split('/').pop()}`;

module.exports = (env, argv) => {
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });

Expand Down Expand Up @@ -42,6 +45,10 @@ module.exports = (env, argv) => {
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new MiniCssExtractPlugin({
filename: `./dist/${outputName}.css`,
chunkFilename: `./dist/${outputName}.css`,
}),
],
});
};
Loading

0 comments on commit 9f3235f

Please sign in to comment.