fix: Measurement Tracking: Various UI and functionality improvements (#4481)

This commit is contained in:
Alireza 2024-11-12 13:14:47 -05:00 committed by GitHub
parent 4a125236dd
commit 62b2748847
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 117 additions and 126 deletions

View File

@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.1",
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/adapters": "^2.2.3",
"@cornerstonejs/core": "^2.2.3",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}

View File

@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.1",
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/adapters": "^2.2.3",
"@cornerstonejs/core": "^2.2.3",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}

View File

@ -46,9 +46,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.1",
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/tools": "^2.2.1",
"@cornerstonejs/adapters": "^2.2.3",
"@cornerstonejs/core": "^2.2.3",
"@cornerstonejs/tools": "^2.2.3",
"classnames": "^2.3.2"
}
}

View File

@ -42,8 +42,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/tools": "^2.2.1",
"@cornerstonejs/core": "^2.2.3",
"@cornerstonejs/tools": "^2.2.3",
"classnames": "^2.3.2"
}
}

View File

@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.1",
"@cornerstonejs/dicom-image-loader": "^2.2.3",
"@icr/polyseg-wasm": "^0.4.0",
"@ohif/core": "3.9.0-beta.110",
"@ohif/ui": "3.9.0-beta.110",
@ -55,9 +55,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.1",
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/tools": "^2.2.1",
"@cornerstonejs/adapters": "^2.2.3",
"@cornerstonejs/core": "^2.2.3",
"@cornerstonejs/tools": "^2.2.3",
"@icr/polyseg-wasm": "^0.4.0",
"@kitware/vtk.js": "32.1.0",
"html2canvas": "^1.4.1",

View File

@ -3,6 +3,7 @@ import React from 'react';
import { Toolbox } from '@ohif/ui-next';
import PanelSegmentation from './panels/PanelSegmentation';
import ActiveViewportWindowLevel from './components/ActiveViewportWindowLevel';
import PanelMeasurementTable from './panels/PanelMeasurement';
const getPanelModule = ({ commandsManager, servicesManager, extensionManager }: withAppTypes) => {
const wrappedPanelSegmentation = ({ configuration }) => {
@ -56,6 +57,19 @@ const getPanelModule = ({ commandsManager, servicesManager, extensionManager }:
);
};
const wrappedPanelMeasurement = ({ configuration }) => {
return (
<PanelMeasurementTable
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
configuration={{
...configuration,
}}
/>
);
};
return [
{
name: 'activeViewportWindowLevel',
@ -63,6 +77,13 @@ const getPanelModule = ({ commandsManager, servicesManager, extensionManager }:
return <ActiveViewportWindowLevel servicesManager={servicesManager} />;
},
},
{
name: 'panelMeasurement',
iconName: 'tab-linear',
iconLabel: 'Measure',
label: 'Measurement',
component: wrappedPanelMeasurement,
},
{
name: 'panelSegmentation',
iconName: 'tab-segmentation',

View File

@ -330,6 +330,9 @@ const connectMeasurementServiceToTools = (measurementService, cornerstoneViewpor
}
removeAnnotation(uid);
}
// trigger a render
cornerstoneViewportService.getRenderingEngine().render();
});
measurementService.subscribe(

View File

@ -148,6 +148,11 @@ export default class SyncGroupService {
const { type, target = true, source = true, options = {}, id = type } = syncGroupObj;
const synchronizer = this._getOrCreateSynchronizer(type, id, options);
if (!synchronizer) {
return;
}
synchronizer.setOptions(viewportId, options);
const viewportInfo = { viewportId, renderingEngineId };

View File

@ -763,9 +763,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
});
}
if (!volumesNotLoaded.length) {
return;
}
// It's crucial not to return here because the volume may be loaded,
// but the viewport also needs to set the volume.
// if (!volumesNotLoaded.length) {
// return;
// }
// This returns the async continuation only
return this.setVolumesForViewport(viewport, volumeInputArray, presentations);

View File

@ -19,7 +19,18 @@ function PanelStudyBrowserHeader({
<div className="bg-muted flex h-[40px] select-none rounded-t p-2">
<div className={'flex h-[24px] w-full select-none justify-center self-center text-[14px]'}>
<div className="flex w-full items-center gap-[10px]">
<div className="flex h-full items-center justify-center">
<div className="flex items-center justify-center">
<div className="text-primary-active flex items-center space-x-1">
{actionIcons.map((icon: actionIcon, index) =>
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
key: index,
onClick: () => updateActionIconValue(icon),
className: `cursor-pointer`,
})
)}
</div>
</div>
<div className="ml-auto flex h-full items-center justify-center">
<ToggleGroup
type="single"
value={viewPresets.filter(preset => preset.selected)[0].id}
@ -40,18 +51,6 @@ function PanelStudyBrowserHeader({
))}
</ToggleGroup>
</div>
<div className="flex items-center justify-center">
<div className="text-primary-active flex items-center space-x-1">
{actionIcons.map((icon: actionIcon, index) =>
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
key: index,
onClick: () => updateActionIconValue(icon),
className: `cursor-pointer`,
})
)}
</div>
</div>
</div>
</div>
</div>

View File

@ -35,6 +35,7 @@ import colorPickerDialog from './utils/colorPickerDialog';
import promptSaveReport from './utils/promptSaveReport';
import promptLabelAnnotation from './utils/promptLabelAnnotation';
import usePatientInfo from './hooks/usePatientInfo';
import { PanelStudyBrowserHeader } from './Panels/StudyBrowser/PanelStudyBrowserHeader';
import * as utils from './utils';
const defaultExtension: Types.Extensions.Extension = {
@ -99,5 +100,6 @@ export {
promptLabelAnnotation,
colorPickerDialog,
usePatientInfo,
PanelStudyBrowserHeader,
utils,
};

View File

@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@cornerstonejs/core": "^2.2.1",
"@cornerstonejs/tools": "^2.2.1",
"@cornerstonejs/core": "^2.2.3",
"@cornerstonejs/tools": "^2.2.3",
"@ohif/core": "3.9.0-beta.110",
"@ohif/extension-cornerstone-dicom-sr": "3.9.0-beta.110",
"@ohif/extension-default": "3.9.0-beta.110",

View File

@ -30,12 +30,7 @@ function TrackedMeasurementsContextProvider(
const [viewportGrid, viewportGridService] = useViewportGrid();
const { activeViewportId, viewports } = viewportGrid;
const {
measurementService,
displaySetService,
customizationService,
cornerstoneViewportService,
} = servicesManager.services;
const { measurementService, displaySetService, customizationService } = servicesManager.services;
const machineOptions = Object.assign({}, defaultOptions);
machineOptions.actions = Object.assign({}, machineOptions.actions, {

View File

@ -118,7 +118,7 @@ function PanelMeasurementTableTracking({
downloadCSVReport(trackedMeasurements);
}}
>
<Icons.Download />
<Icons.Download className="h-5 w-5" />
<span className="pl-1">CSV</span>
</Button>
<Button
@ -133,7 +133,18 @@ function PanelMeasurementTableTracking({
}}
>
<Icons.Add />
Create DICOM SR
Create SR
</Button>
<Button
size="sm"
variant="ghost"
className="pl-0.5"
onClick={() => {
measurementService.clearMeasurements();
}}
>
<Icons.Delete />
Delete All
</Button>
</div>
</div>

View File

@ -8,7 +8,7 @@ import { StudyBrowser } from '@ohif/ui-next';
import { useTrackedMeasurements } from '../../getContextModule';
import { Separator } from '@ohif/ui-next';
import { PanelStudyBrowserTrackingHeader } from './PanelStudyBrowserTrackingHeader';
import { PanelStudyBrowserHeader } from '@ohif/extension-default';
import { defaultActionIcons, defaultViewPresets } from './constants';
const { formatDate, createStudyBrowserTabs } = utils;
@ -482,7 +482,7 @@ function PanelStudyBrowserTracking({
return (
<>
<>
<PanelStudyBrowserTrackingHeader
<PanelStudyBrowserHeader
viewPresets={viewPresets}
updateViewPresetValue={updateViewPresetValue}
actionIcons={actionIcons}

View File

@ -1,61 +0,0 @@
import React from 'react';
import { ToggleGroup, ToggleGroupItem } from '@ohif/ui-next';
import { Icons } from '@ohif/ui-next';
import { actionIcon, viewPreset } from './types';
function PanelStudyBrowserTrackingHeader({
viewPresets,
updateViewPresetValue,
actionIcons,
updateActionIconValue,
}: {
viewPresets: viewPreset[];
updateViewPresetValue: (viewPreset: viewPreset) => void;
actionIcons: actionIcon[];
updateActionIconValue: (actionIcon: actionIcon) => void;
}) {
return (
<>
<div className="bg-muted flex h-[40px] select-none rounded-t p-2">
<div className={'flex h-[24px] w-full select-none justify-center self-center text-[14px]'}>
<div className="flex w-full items-center gap-[10px]">
<div className="flex h-full items-center justify-center">
<ToggleGroup
type="single"
value={viewPresets.filter(preset => preset.selected)[0].id}
onValueChange={value => {
const selectedViewPreset = viewPresets.find(preset => preset.id === value);
updateViewPresetValue(selectedViewPreset);
}}
>
{viewPresets.map((viewPreset: viewPreset, index) => (
<ToggleGroupItem
key={index}
aria-label={viewPreset.id}
value={viewPreset.id}
className="text-actions-primary"
>
{React.createElement(Icons[viewPreset.iconName] || Icons.MissingIcon)}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
<div className="flex items-center justify-center">
<div className="text-primary-active flex items-center space-x-1">
{actionIcons.map((icon: actionIcon, index) =>
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
key: index,
onClick: () => updateActionIconValue(icon),
className: `cursor-pointer`,
})
)}
</div>
</div>
</div>
</div>
</div>
</>
);
}
export { PanelStudyBrowserTrackingHeader };

View File

@ -19,7 +19,7 @@ const ohif = {
};
const cornerstone = {
measurements: '@ohif/extension-cornerstone.panelModule.measurements',
measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement',
segmentation: '@ohif/extension-cornerstone.panelModule.panelSegmentation',
};

View File

@ -16,7 +16,12 @@ describe('OHIF Measurement Panel', function () {
cy.get('@measurementsPanel').should('not.exist');
cy.get('@RightCollapseBtn').click();
cy.get('@measurementsPanel').should('exist');
// segmentation panel should be visible
cy.get('@segmentationPanel').should('be.visible');
// measurements panel should be clickable
cy.get('@measurementsBtn').click();
cy.get('@measurementsPanel').should('be.visible');
});

View File

@ -27,6 +27,7 @@ export function initCommonElementsAliases(skipMarkers) {
// TODO: Panels are not in DOM when closed, move this somewhere else
cy.get('[data-cy="trackedMeasurements-panel"]').as('measurementsPanel');
cy.get('[data-cy="panelSegmentation-btn"]').as('segmentationPanel');
cy.get('[data-cy="studyBrowser-panel"]').as('seriesPanel');
cy.get('[data-cy="viewport-overlay-top-right"]').as('viewportInfoTopRight');
cy.get('[data-cy="viewport-overlay-top-left"]').as('viewportInfoTopLeft');

View File

@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.1",
"@cornerstonejs/dicom-image-loader": "^2.2.3",
"@emotion/serialize": "^1.1.3",
"@ohif/core": "3.9.0-beta.110",
"@ohif/extension-cornerstone": "3.9.0-beta.110",

View File

@ -37,7 +37,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.1",
"@cornerstonejs/dicom-image-loader": "^2.2.3",
"@ohif/ui": "3.9.0-beta.110",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"

View File

@ -10,11 +10,11 @@ import TabItem from '@theme/TabItem';
## Panel Measurements
The panel in the default extension is renamed from `measure` to `measurements` to be more consistent with the rest of the extensions.
The panel in the default extension is renamed from `measure` to `panelMeasurement` to be more consistent with the rest of the extensions.
**Action Needed**
Update any references to the `measure` panel to `measurements` in your code.
Update any references to the `measure` panel to `panelMeasurement` in your code.
Find and replace
@ -23,7 +23,7 @@ Find and replace
@ohif/extension-default.panelModule.measure
</TabItem>
<TabItem value="After" label="After 🚀" >
@ohif/extension-default.panelModule.measurements
@ohif/extension-cornerstone.panelModule.panelMeasurement
</TabItem>
</Tabs>

View File

@ -3,6 +3,15 @@ title: UI
---
## New Components
You can explore our new playground at `docs.ohif.org/ui` to see the latest components and their properties. We haven't provided a migration guide yet because the old components are still available. Feel free to update your codebase, including custom extensions and UI, to use the new Button, Dropdown, Icons, and other new components from `@ohif/ui-next`. The old methods (importing from `@ohif/ui`) will continue to work for now. However, the new components have a slightly different API, and we plan to deprecate the old components in a future release, as we see the new ones as the future of OHIF.
## `UINotificationService`

View File

@ -612,7 +612,6 @@ export const Icons = {
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<title>icon-list-view</title>
<g
id="icon-list-view"
stroke="none"

View File

@ -2580,10 +2580,10 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cornerstonejs/adapters@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.2.1.tgz#9bed051a7fd9ca5ad5af3ccb97fbb79862feadc1"
integrity sha512-7eIZTN3PO4s95vaLrAAD018bNKsD1w7l0FL+bLv+eqebNHrhBMQQbAfEWuQCueE+tYd1mVDCpNWVDMKMMabHmQ==
"@cornerstonejs/adapters@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.2.3.tgz#6dc37ec2b002c8a1406924c4545509d0bda575af"
integrity sha512-hVUsE2bghSshQGjRszksBk+2UdzGyOzzqNU7+OtJkMHkmiEWvZoBYNlEp7w/hQsF6bKaHnsc0zJGZ3txho65sQ==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@ -2616,19 +2616,19 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
"@cornerstonejs/core@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.2.1.tgz#37cc052d40fffcc093f0f773051787c610d07386"
integrity sha512-5ZZcpa7hU+E45jM4MvEJrIFA+WHFaLWIVh9FO1HksJXtkIeBurdYr6R95aOfk7ujkkft2rsgQA29a5Jl8E6aGg==
"@cornerstonejs/core@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.2.3.tgz#e6fe86e38b41269eb6347de1b2445a5d80d00e1a"
integrity sha512-dq+CYUY2sk/Kpwc2lpnbr+9/9upKhzU/F4phdaVR0OfFlQRSJuv0lyI3lgyDw31ZsNt7H6LWcx5DtMTO+ZFtJg==
dependencies:
"@kitware/vtk.js" "32.1.0"
comlink "^4.4.1"
gl-matrix "^3.4.3"
"@cornerstonejs/dicom-image-loader@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.2.1.tgz#01f38061cd128c56b00fb5a3c47488880fb0f6bf"
integrity sha512-yFO6K4vFZqG1dkUBpVSsQhOxS6nkb7uKEETwobhroAs8lIIfQ8141R8u2DfxBTIY7o9uJuYpabYzlhbyUYr4eA==
"@cornerstonejs/dicom-image-loader@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.2.3.tgz#021a312f37afdd924b13762780bfa9ebcfa3a10e"
integrity sha512-OvReDd+hgO9O9TFeXv8sEntDva+I4tb4vR0SVJ97u39ye6NEWHCb4PJ6bLZIQ2yNpKQqUDlS5LQc5At+QxfkDA==
dependencies:
"@cornerstonejs/codec-charls" "^1.2.3"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
@ -2639,10 +2639,10 @@
pako "^2.0.4"
uuid "^9.0.0"
"@cornerstonejs/tools@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.2.1.tgz#a6d34c39c330927c485e4af8b4c99026226249e4"
integrity sha512-cxTZAr+zA1atAJjspA4u4yD+mMZsV+H/fqf2Oa8yn3EZU5ONmFF37QbwLiXd3Zo47GYfEgTPDKQwFWG7wIAsxg==
"@cornerstonejs/tools@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.2.3.tgz#31b04918861a2e76249eb62340f63ae7645ca7c1"
integrity sha512-GyMz7LwssYk0D8ao4OPrWlob8jIKzEHYEJdYQbZ72rTOKHbpmu147rUjQ3AiihL5T1lftFsV4t9kTfUTHd2ZBA==
dependencies:
"@types/offscreencanvas" "2019.7.3"
comlink "^4.4.1"