feat(4D): Add 4D dynamic volume rendering and new pre-clinical 4d pt/ct mode (#3664)

Co-authored-by: Alireza <ar.sedghi@gmail.com>
Co-authored-by: Neil <neil.a.macphee@gmail.com>
This commit is contained in:
authored and GitHub committed 2024-04-09 23:57:34 -04:00
1 parent dc0b183dd4
commit d57e8bc157
200 files changed
+9589 -2178

No files matched your search

@@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, ButtonEnums } from '@ohif/ui';
function ActionButtons({ onExportClick, onCreateReportClick, disabled }) {
const { t } = useTranslation('MeasurementTable');
return (
<React.Fragment>
<Button
onClick={onExportClick}
disabled={disabled}
type={ButtonEnums.type.secondary}
size={ButtonEnums.size.small}
>
{t('Export')}
</Button>
<Button
className="ml-2"
onClick={onCreateReportClick}
type={ButtonEnums.type.secondary}
size={ButtonEnums.size.small}
disabled={disabled}
>
{t('Create Report')}
</Button>
</React.Fragment>
);
}
ActionButtons.propTypes = {
onExportClick: PropTypes.func,
onCreateReportClick: PropTypes.func,
disabled: PropTypes.bool,
};
ActionButtons.defaultProps = {
onExportClick: () => alert('Export'),
onCreateReportClick: () => alert('Create Report'),
disabled: false,
};
export default ActionButtons;
@@ -1,19 +1,12 @@
import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import {
StudySummary,
MeasurementTable,
Dialog,
Input,
useViewportGrid,
ButtonEnums,
} from '@ohif/ui';
import { StudySummary, MeasurementTable, useViewportGrid, ActionButtons } from '@ohif/ui';
import { DicomMetadataStore, utils } from '@ohif/core';
import { useDebounce } from '@hooks';
import { useAppConfig } from '@state';
import ActionButtons from './ActionButtons';
import { useTrackedMeasurements } from '../../getContextModule';
import debounce from 'lodash.debounce';
import { useTranslation } from 'react-i18next';
const { downloadCSVReport } = utils;
const { formatDate } = utils;
@@ -27,9 +20,11 @@ const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = {
function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
const [viewportGrid] = useViewportGrid();
const { t } = useTranslation('MeasurementTable');
const [measurementChangeTimestamp, setMeasurementsUpdated] = useState(Date.now().toString());
const debouncedMeasurementChangeTimestamp = useDebounce(measurementChangeTimestamp, 200);
const { measurementService, uiDialogService, displaySetService, customizationService } = servicesManager.services;
const { measurementService, uiDialogService, displaySetService, customizationService } =
servicesManager.services;
const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements();
const { trackedStudy, trackedSeries } = trackedMeasurements.context;
const [displayStudySummary, setDisplayStudySummary] = useState(
@@ -172,6 +167,9 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
dm => dm.measurementType === measurementService.VALUE_TYPES.POINT
);
const disabled =
additionalFindings.length === 0 && displayMeasurementsWithoutFindings.length === 0;
return (
<>
<div
@@ -206,16 +204,23 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
{!appConfig?.disableEditing && (
<div className="flex justify-center p-4">
<ActionButtons
onExportClick={exportReport}
onCreateReportClick={() => {
sendTrackedMeasurementsEvent('SAVE_REPORT', {
viewportId: viewportGrid.activeViewportId,
isBackupSave: true,
});
}}
disabled={
additionalFindings.length === 0 && displayMeasurementsWithoutFindings.length === 0
}
t={t}
actions={[
{
label: 'Export',
onClick: exportReport,
},
{
label: 'Create Report',
onClick: () => {
sendTrackedMeasurementsEvent('SAVE_REPORT', {
viewportId: viewportGrid.activeViewportId,
isBackupSave: true,
});
},
},
]}
disabled={disabled}
/>
</div>
)}
@@ -19,8 +19,13 @@ function PanelStudyBrowserTracking({
requestDisplaySetCreationForStudy,
dataSource,
}) {
const { displaySetService, uiDialogService, hangingProtocolService, uiNotificationService, measurementService } =
servicesManager.services;
const {
displaySetService,
uiDialogService,
hangingProtocolService,
uiNotificationService,
measurementService,
} = servicesManager.services;
const navigate = useNavigate();
const { t } = useTranslation('Common');
@@ -129,7 +134,8 @@ function PanelStudyBrowserTracking({
const newImageSrcEntry = {};
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
const imageId = getImageIdForThumbnail(displaySet, imageIds);
// TODO: Is it okay that imageIds are not returned here for SR displaySets?
if (!imageId || displaySet?.unsupported) {
@@ -195,7 +201,7 @@ function PanelStudyBrowserTracking({
}
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
const imageId = getImageIdForThumbnail(displaySet, imageIds);
// TODO: Is it okay that imageIds are not returned here for SR displaysets?
if (!imageId) {
@@ -366,6 +372,19 @@ PanelStudyBrowserTracking.propTypes = {
export default PanelStudyBrowserTracking;
function getImageIdForThumbnail(displaySet: any, imageIds: any) {
let imageId;
if (displaySet.isDynamicVolume) {
const timePoints = displaySet.dynamicVolumeInfo.timePoints;
const middleIndex = Math.floor(timePoints.length / 2);
const middleTimePointImageIds = timePoints[middleIndex];
imageId = middleTimePointImageIds[Math.floor(middleTimePointImageIds.length / 2)];
} else {
imageId = imageIds[Math.floor(imageIds.length / 2)];
}
return imageId;
}
/**
* Maps from the DataSource's format to a naturalized object
*
@@ -631,4 +650,4 @@ function _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs) {
}
}
}
}
}