fix(core): Address 3D reconstruction and Android compatibility issues and clean up 4D data mode (#4762)
This commit is contained in:
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user