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:
200 files changed
+9589
-2178
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
@@ -7,7 +7,6 @@ import Tooltip from '../Tooltip';
|
||||
import InputRange from '../InputRange';
|
||||
|
||||
import './CinePlayer.css';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export type CinePlayerProps = {
|
||||
className: string;
|
||||
@@ -19,6 +18,12 @@ export type CinePlayerProps = {
|
||||
onFrameRateChange: (value: number) => void;
|
||||
onPlayPauseChange: (value: boolean) => void;
|
||||
onClose: () => void;
|
||||
updateDynamicInfo?: () => void;
|
||||
dynamicInfo?: {
|
||||
timePointIndex: number;
|
||||
numTimePoints: number;
|
||||
label?: string;
|
||||
};
|
||||
};
|
||||
|
||||
const fpsButtonClassNames =
|
||||
@@ -31,12 +36,15 @@ const CinePlayer: React.FC<CinePlayerProps> = ({
|
||||
maxFrameRate,
|
||||
stepFrameRate,
|
||||
frameRate: defaultFrameRate,
|
||||
dynamicInfo = {},
|
||||
onFrameRateChange,
|
||||
onPlayPauseChange,
|
||||
onClose,
|
||||
updateDynamicInfo,
|
||||
}) => {
|
||||
const isDynamic = !!dynamicInfo?.numTimePoints;
|
||||
const [frameRate, setFrameRate] = useState(defaultFrameRate);
|
||||
const debouncedSetFrameRate = debounce(onFrameRateChange, 300);
|
||||
const debouncedSetFrameRate = useCallback(debounce(onFrameRateChange, 100), [onFrameRateChange]);
|
||||
|
||||
const getPlayPauseIconName = () => (isPlaying ? 'icon-pause' : 'icon-play');
|
||||
|
||||
@@ -52,45 +60,92 @@ const CinePlayer: React.FC<CinePlayerProps> = ({
|
||||
setFrameRate(defaultFrameRate);
|
||||
}, [defaultFrameRate]);
|
||||
|
||||
const handleTimePointChange = useCallback(
|
||||
(newIndex: number) => {
|
||||
if (isDynamic && dynamicInfo) {
|
||||
// Here, you would update the component's state or context that controls the current time point index
|
||||
// For demonstration, assuming a hypothetical function that updates the time point index
|
||||
updateDynamicInfo({
|
||||
...dynamicInfo,
|
||||
timePointIndex: newIndex,
|
||||
});
|
||||
}
|
||||
},
|
||||
[isDynamic, dynamicInfo]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'border-secondary-light/60 bg-primary-dark flex select-none items-center gap-2 rounded border px-2 py-2'
|
||||
<div className={className}>
|
||||
{isDynamic && dynamicInfo && (
|
||||
<InputRange
|
||||
value={dynamicInfo.timePointIndex}
|
||||
onChange={handleTimePointChange}
|
||||
minValue={0}
|
||||
maxValue={dynamicInfo.numTimePoints - 1}
|
||||
step={1}
|
||||
containerClassName="mb-3 w-full"
|
||||
labelClassName="text-xs text-white"
|
||||
leftColor="#3a3f99"
|
||||
rightColor="#3a3f99"
|
||||
trackHeight="4px"
|
||||
thumbColor="#348cfd"
|
||||
thumbColorOuter="#000000"
|
||||
showLabel={false}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
name={getPlayPauseIconName()}
|
||||
className="active:text-primary-light hover:bg-customblue-300 cursor-pointer text-white hover:rounded"
|
||||
onClick={() => onPlayPauseChange(!isPlaying)}
|
||||
/>
|
||||
<Tooltip
|
||||
position="top"
|
||||
className="group/fps cine-fps-range-tooltip"
|
||||
tight={true}
|
||||
content={
|
||||
<InputRange
|
||||
containerClassName="h-9 px-2"
|
||||
inputClassName="w-40"
|
||||
value={frameRate}
|
||||
minValue={minFrameRate}
|
||||
maxValue={maxFrameRate}
|
||||
step={stepFrameRate}
|
||||
onChange={handleSetFrameRate}
|
||||
showLabel={false}
|
||||
/>
|
||||
<div
|
||||
className={
|
||||
'border-secondary-light/60 bg-primary-dark inline-flex select-none items-center gap-2 rounded border px-2 py-2'
|
||||
}
|
||||
>
|
||||
<div className="border-secondary-light flex h-6 items-stretch gap-1 rounded border">
|
||||
<Icon
|
||||
name={getPlayPauseIconName()}
|
||||
className="active:text-primary-light hover:bg-customblue-300 cursor-pointer text-white hover:rounded"
|
||||
onClick={() => onPlayPauseChange(!isPlaying)}
|
||||
/>
|
||||
{isDynamic && dynamicInfo && (
|
||||
<div className="min-w-16 max-w-44 flex flex-col text-white">
|
||||
{/* Add Tailwind classes for monospace font and center alignment */}
|
||||
<div className="text-[11px]">
|
||||
<span className="w-2 text-white">{dynamicInfo.timePointIndex}</span>{' '}
|
||||
<span className="text-aqua-pale">{`/${dynamicInfo.numTimePoints}`}</span>
|
||||
</div>
|
||||
<div className="text-aqua-pale text-xs">{dynamicInfo.label}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-secondary-light ml-4 flex h-6 items-stretch gap-1 rounded border">
|
||||
<div
|
||||
className={`${fpsButtonClassNames} rounded-l`}
|
||||
onClick={() => handleSetFrameRate(frameRate - 1)}
|
||||
>
|
||||
<Icon name="arrow-left-small" />
|
||||
</div>
|
||||
<div className="group-hover/fps:text-primary-light w-11 text-center text-sm leading-[22px] text-white">
|
||||
{`${frameRate} FPS`}
|
||||
</div>
|
||||
<Tooltip
|
||||
position="top"
|
||||
className="group/fps cine-fps-range-tooltip"
|
||||
tight={true}
|
||||
content={
|
||||
<InputRange
|
||||
containerClassName="h-9 px-2"
|
||||
inputClassName="w-40"
|
||||
value={frameRate}
|
||||
minValue={minFrameRate}
|
||||
maxValue={maxFrameRate}
|
||||
step={stepFrameRate}
|
||||
onChange={handleSetFrameRate}
|
||||
showLabel={false}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<div className="flex-shrink-0 text-center text-sm leading-[22px] text-white">
|
||||
<span className="inline-block text-right">{`${frameRate} `}</span>
|
||||
<span className="text-aqua-pale whitespace-nowrap text-xs">{' FPS'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<div
|
||||
className={`${fpsButtonClassNames} rounded-r`}
|
||||
onClick={() => handleSetFrameRate(frameRate + 1)}
|
||||
@@ -98,12 +153,12 @@ const CinePlayer: React.FC<CinePlayerProps> = ({
|
||||
<Icon name="arrow-right-small" />
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Icon
|
||||
name="icon-close"
|
||||
className="text-primary-active active:text-primary-light hover:bg-customblue-300 cursor-pointer hover:rounded"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Icon
|
||||
name="icon-close"
|
||||
className="text-primary-active active:text-primary-light hover:bg-customblue-300 cursor-pointer hover:rounded"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -119,6 +174,8 @@ CinePlayer.defaultProps = {
|
||||
onPlayPauseChange: noop,
|
||||
onFrameRateChange: noop,
|
||||
onClose: noop,
|
||||
isDynamic: false,
|
||||
dynamicInfo: {},
|
||||
};
|
||||
|
||||
CinePlayer.propTypes = {
|
||||
@@ -134,6 +191,12 @@ CinePlayer.propTypes = {
|
||||
onPlayPauseChange: PropTypes.func,
|
||||
onFrameRateChange: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
isDynamic: PropTypes.bool,
|
||||
dynamicInfo: PropTypes.shape({
|
||||
timePointIndex: PropTypes.number,
|
||||
numTimePoints: PropTypes.number,
|
||||
label: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
export default CinePlayer;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { CinePlayer } from '../../../components';
|
||||
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
|
||||
|
||||
export const argTypes = {
|
||||
component: CinePlayer,
|
||||
title: 'Components/CinePlayer',
|
||||
};
|
||||
|
||||
<Meta
|
||||
title="Components/CinePlayer"
|
||||
component={CinePlayer}
|
||||
/>
|
||||
|
||||
export const CinePlayerTemplate = args => (
|
||||
<div className="relative h-96 w-96 bg-black">
|
||||
<div className="absolute left-1/2 bottom-3 -translate-x-1/2">
|
||||
<CinePlayer {...args} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
<Heading
|
||||
title="CinePlayer"
|
||||
componentRelativePath="CinePlayer/CinePlayer.tsx"
|
||||
/>
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Props](#props)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
## Overview
|
||||
|
||||
CinePlayer is a component that allows you to use as a boolean value
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Overview"
|
||||
args={{
|
||||
isDynamic: true,
|
||||
dynamicInfo: {
|
||||
timePointIndex: 5,
|
||||
numTimePoints: 20,
|
||||
label: 'TemporalPositionIdentifier',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{CinePlayerTemplate.bind({})}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Props
|
||||
|
||||
<ArgsTable of={CinePlayer} />
|
||||
|
||||
## Contribute
|
||||
|
||||
<Footer componentRelativePath="CinePlayer/__stories__/CinePlayer.stories.mdx" />
|
||||
Reference in new issue
Block a user