fix(core): Address 3D reconstruction and Android compatibility issues and clean up 4D data mode (#4762)

This commit is contained in:
Alireza authored and GitHub committed 2025-02-04 12:25:10 -05:00
1 parent 4d06a7d286
commit 149d6d049c
32 files changed
+529 -529

No files matched your search

@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Button, PanelSection, ButtonGroup, IconButton, InputNumber } from '@ohif/ui';
import { DoubleSlider, Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
import { Icons, Tooltip, TooltipTrigger, TooltipContent, Numeric } from '@ohif/ui-next';
import { Enums } from '@cornerstonejs/core';
const controlClassNames = {
@@ -20,7 +20,10 @@ const Header = ({ title, tooltip }) => (
/>
</span>
</TooltipTrigger>
<TooltipContent sideOffset={4} className="max-w-xs p-2 bg-primary-dark text-white">
<TooltipContent
sideOffset={4}
className="bg-primary-dark max-w-xs p-2 text-white"
>
<div>{tooltip}</div>
</TooltipContent>
</Tooltip>
@@ -37,23 +40,16 @@ const DynamicVolumeControls = ({
minFps,
maxFps,
// Frames
currentFrameIndex,
onFrameChange,
framesLength,
currentDimensionGroupNumber,
onDimensionGroupChange,
numDimensionGroups,
onGenerate,
onDoubleRangeChange,
rangeValues,
onDynamicClick,
}) => {
const [computedView, setComputedView] = useState(false);
const [computeViewMode, setComputeViewMode] = useState(Enums.DynamicOperatorType.SUM);
const [sliderRangeValues, setSliderRangeValues] = useState([0, framesLength - 1]);
const handleSliderChange = newValues => {
onDoubleRangeChange(newValues);
setSliderRangeValues(newValues);
};
const formatLabel = value => Math.round(value);
return (
<div className="flex select-none flex-col">
@@ -89,7 +85,7 @@ const DynamicVolumeControls = ({
</ButtonGroup>
</div>
<div>
<FrameControls
<DimensionGroupControls
onPlayPauseChange={onPlayPauseChange}
isPlaying={isPlaying}
computedView={computedView}
@@ -99,9 +95,9 @@ const DynamicVolumeControls = ({
minFps={minFps}
maxFps={maxFps}
//
framesLength={framesLength}
onFrameChange={onFrameChange}
currentFrameIndex={currentFrameIndex}
numDimensionGroups={numDimensionGroups}
onDimensionGroupChange={onDimensionGroupChange}
currentDimensionGroupNumber={currentDimensionGroupNumber}
/>
</div>
<div className={`mt-6 flex flex-col ${computedView ? '' : 'ohif-disabled'}`}>
@@ -111,9 +107,10 @@ const DynamicVolumeControls = ({
<div>
Operation Buttons (SUM, AVERAGE, SUBTRACT): Select the mathematical operation to be
applied to the data set.
<br /> Range Slider: Choose the numeric range within which the operation will be
performed.
<br />Generate Button: Execute the chosen operation on the specified range of data.
<br /> Range Slider: Choose the numeric range of dimension groups within which the
operation will be performed.
<br />
Generate Button: Execute the chosen operation on the specified range of data.
</div>
}
/>
@@ -141,15 +138,15 @@ const DynamicVolumeControls = ({
</button>
</ButtonGroup>
<div className="mt-2 w-full">
<DoubleSlider
min={0}
max={framesLength - 1}
step={1}
defaultValue={sliderRangeValues}
onValueChange={handleSliderChange}
formatLabel={formatLabel}
className="w-full"
/>
<Numeric.Container
mode="doubleRange"
min={1}
max={numDimensionGroups}
values={rangeValues}
onChange={onDoubleRangeChange}
>
<Numeric.DoubleRange showNumberInputs />
</Numeric.Container>
</div>
<Button
className="mt-2 !h-[26px] !w-[115px] self-start !p-0"
@@ -167,16 +164,16 @@ const DynamicVolumeControls = ({
export default DynamicVolumeControls;
function FrameControls({
function DimensionGroupControls({
isPlaying,
onPlayPauseChange,
fps,
minFps,
maxFps,
onFpsChange,
framesLength,
onFrameChange,
currentFrameIndex,
numDimensionGroups,
onDimensionGroupChange,
currentDimensionGroupNumber,
computedView,
}) {
const getPlayPauseIconName = () => (isPlaying ? 'icon-pause' : 'icon-play');
@@ -187,14 +184,16 @@ function FrameControls({
title="4D Controls"
tooltip={
<div>
Play/Pause Button: Begin or pause the animation of the 4D visualization. <br /> Frame
Selector: Navigate through individual frames of the 4D data. <br /> FPS (Frames Per
Second) Selector: Adjust the playback speed of the animation.
Play/Pause Button: Begin or pause the animation of the 4D visualization. <br />
Dimension Group Selector: Navigate through individual dimension groups of the 4D data.{' '}
<br />
FPS (Frames Per Second) Selector: Adjust the playback speed of the animation.
</div>
}
/>
<div className="mt-3 flex justify-between">
<IconButton
id="play-pause-button"
className="bg-customblue-30 h-[26px] w-[58px] rounded-[4px]"
onClick={() => onPlayPauseChange(!isPlaying)}
>
@@ -204,11 +203,11 @@ function FrameControls({
/>
</IconButton>
<InputNumber
value={currentFrameIndex}
onChange={onFrameChange}
minValue={0}
maxValue={framesLength - 1}
label="Frame"
value={currentDimensionGroupNumber}
onChange={onDimensionGroupChange}
minValue={1}
maxValue={numDimensionGroups}
label="Group"
{...controlClassNames}
/>
<InputNumber
@@ -222,4 +221,4 @@ function FrameControls({
</div>
</div>
);
}
}
@@ -15,13 +15,12 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
const [{ activeViewportId }] = useViewportGrid();
//
const [timePointsRange, setTimePointsRange] = useState([0, 0]);
const [timePointsRangeToUseForGenerate, setTimePointsRangeToUseForGenerate] = useState([0, 0]);
const [dimensionGroupRange, setDimensionGroupRange] = useState([1, 1]);
const [computedDisplaySet, setComputedDisplaySet] = useState(null);
const [dynamicVolume, setDynamicVolume] = useState(null);
const [frameRate, setFrameRate] = useState(20);
const [isPlaying, setIsPlaying] = useState(isCineEnabled);
const [timePointRendered, setTimePointRendered] = useState(null);
const [dimensionGroupNumberRendered, setDimensionGroupNumberRendered] = useState(null);
const [displayingComputed, setDisplayingComputed] = useState(false);
//
@@ -38,9 +37,8 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
if (volumeData.volume?.isDynamicVolume()) {
setDynamicVolume(volumeData.volume);
uuidDynamicVolume.current = volumeData.displaySetInstanceUID;
const newRange = [1, volumeData.volume.numTimePoints];
setTimePointsRange(newRange);
setTimePointsRangeToUseForGenerate(newRange);
const newRange = [1, volumeData.volume.numDimensionGroups];
setDimensionGroupRange(newRange);
}
});
};
@@ -65,10 +63,10 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
}, [cornerstoneViewportService, cineService, servicesManager.services.cineService]);
useEffect(() => {
const evt = Enums.Events.DYNAMIC_VOLUME_TIME_POINT_INDEX_CHANGED;
const evt = Enums.Events.DYNAMIC_VOLUME_DIMENSION_GROUP_CHANGED;
const callback = evt => {
setTimePointRendered(evt.detail.timePointIndex);
setDimensionGroupNumberRendered(evt.detail.dimensionGroupNumber);
};
eventTarget.addEventListener(evt, callback);
@@ -76,7 +74,7 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
return () => {
eventTarget.removeEventListener(evt, callback);
};
}, [cornerstoneViewportService]);
}, []);
useEffect(() => {
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(activeViewportId);
@@ -102,9 +100,7 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
setDynamicVolume(dynamicVolume);
uuidDynamicVolume.current = dynamicVolumeDisplaySet.displaySetInstanceUID;
const newRange = [1, dynamicVolume.numTimePoints];
setTimePointsRange(newRange);
setTimePointsRangeToUseForGenerate(newRange);
setDimensionGroupRange([1, dynamicVolume.numDimensionGroups]);
}, [
activeViewportId,
viewportGridService,
@@ -141,11 +137,12 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
volumeId: computedVolumeId,
});
}
const [start, end] = timePointsRangeToUseForGenerate;
const frameNumbers = Array.from({ length: end - start + 1 }, (_, i) => i + start - 1);
const [start, end] = dimensionGroupRange;
// from start to end, with steps of 1
const frameNumbers = Array.from({ length: end - start + 1 }, (_, i) => start + i);
const options = {
frameNumbers: operationName === 'SUBTRACT' ? [start, end - 1] : frameNumbers,
dimensionGroupNumbers: operationName === 'SUBTRACT' ? [start, end] : frameNumbers,
targetVolume: computedVolume,
};
@@ -207,17 +204,6 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
handlePlay();
};
function handleSliderChange(newValues) {
if (
newValues[0] === timePointsRangeToUseForGenerate[0] &&
newValues[1] === timePointsRangeToUseForGenerate[1]
) {
return;
}
setTimePointsRangeToUseForGenerate(newValues);
}
return (
<DynamicVolumeControls
fps={frameRate}
@@ -225,16 +211,16 @@ export default function PanelGenerateImage({ servicesManager, commandsManager }:
onPlayPauseChange={onPlayPauseChange}
minFps={1}
maxFps={50}
currentFrameIndex={timePointRendered}
onFpsChange={handleSetFrameRate}
framesLength={timePointsRange[1]}
onFrameChange={timePointIndex => {
dynamicVolume.timePointIndex = timePointIndex;
currentDimensionGroupNumber={dimensionGroupNumberRendered}
numDimensionGroups={dynamicVolume?.numDimensionGroups || 1}
onDimensionGroupChange={dimensionGroupNumber => {
dynamicVolume.dimensionGroupNumber = dimensionGroupNumber;
}}
onGenerate={onGenerateImage}
onDynamicClick={displayingComputed ? () => renderDynamicImage(computedDisplaySet) : null}
onDoubleRangeChange={handleSliderChange}
initialRangeValues={timePointsRangeToUseForGenerate}
onDoubleRangeChange={setDimensionGroupRange}
rangeValues={dimensionGroupRange}
/>
);
}