|
@@ -22,17 +22,9 @@ import StackViewer, {
|
|
deactivateLengthMeasurement,
|
|
deactivateLengthMeasurement,
|
|
clearLengthMeasurements,
|
|
clearLengthMeasurements,
|
|
activateAngleMeasurement,
|
|
activateAngleMeasurement,
|
|
- deactivateAngleMeasurement,
|
|
|
|
- clearAngleMeasurements,
|
|
|
|
activateTibialPlateauAngleMeasurement,
|
|
activateTibialPlateauAngleMeasurement,
|
|
- deactivateTibialPlateauAngleMeasurement,
|
|
|
|
- clearTibialPlateauAngleMeasurements,
|
|
|
|
activateDARAMeasurement,
|
|
activateDARAMeasurement,
|
|
- deactivateDARAMeasurement,
|
|
|
|
- clearDARAMeasurements,
|
|
|
|
activateHipDIMeasurement,
|
|
activateHipDIMeasurement,
|
|
- deactivateHipDIMeasurement,
|
|
|
|
- clearHipDIMeasurements,
|
|
|
|
activateHipNHAAngleMeasurement,
|
|
activateHipNHAAngleMeasurement,
|
|
activateTPLOMeasurement,
|
|
activateTPLOMeasurement,
|
|
} from './viewers/stack.image.viewer';
|
|
} from './viewers/stack.image.viewer';
|
|
@@ -60,6 +52,47 @@ import * as cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
|
import { MeasurementToolManager } from '@/utils/measurementToolManager';
|
|
import { MeasurementToolManager } from '@/utils/measurementToolManager';
|
|
|
|
|
|
const renderingEngineId = 'myRenderingEngine';
|
|
const renderingEngineId = 'myRenderingEngine';
|
|
|
|
+
|
|
|
|
+// 测量工具配置映射
|
|
|
|
+interface MeasurementToolConfig {
|
|
|
|
+ toolName: string;
|
|
|
|
+ activateFunction: (viewportId: string) => boolean;
|
|
|
|
+ logPrefix: string;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const MEASUREMENT_TOOL_CONFIGS: Record<string, MeasurementToolConfig> = {
|
|
|
|
+ '胫骨平台夹角': {
|
|
|
|
+ toolName: 'TibialPlateauAngleTool',
|
|
|
|
+ activateFunction: activateTibialPlateauAngleMeasurement,
|
|
|
|
+ logPrefix: 'TibialPlateauAngle',
|
|
|
|
+ },
|
|
|
|
+ '髋臼水平角': {
|
|
|
|
+ toolName: 'DARAMeasurementTool',
|
|
|
|
+ activateFunction: activateDARAMeasurement,
|
|
|
|
+ logPrefix: 'DARA',
|
|
|
|
+ },
|
|
|
|
+ '髋关节牵引指数': {
|
|
|
|
+ toolName: 'HipDIMeasurementTool',
|
|
|
|
+ activateFunction: activateHipDIMeasurement,
|
|
|
|
+ logPrefix: 'HipDI',
|
|
|
|
+ },
|
|
|
|
+ '髋关节水平角': {
|
|
|
|
+ toolName: 'HipNHAAngleMeasurementTool',
|
|
|
|
+ activateFunction: activateHipNHAAngleMeasurement,
|
|
|
|
+ logPrefix: 'HipNHA',
|
|
|
|
+ },
|
|
|
|
+ '心锥比': {
|
|
|
|
+ toolName: 'VHSMeasurementTool',
|
|
|
|
+ activateFunction: MeasurementToolManager.activateVHSMeasurementTool,
|
|
|
|
+ logPrefix: 'VHS',
|
|
|
|
+ },
|
|
|
|
+ '胫骨平台骨切开术': {
|
|
|
|
+ toolName: 'TPLOMeasurementTool',
|
|
|
|
+ activateFunction: activateTPLOMeasurement,
|
|
|
|
+ logPrefix: 'TPLO',
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
const setup = () => {
|
|
const setup = () => {
|
|
// 初始化 Cornerstone
|
|
// 初始化 Cornerstone
|
|
cornerstone.init();
|
|
cornerstone.init();
|
|
@@ -148,6 +181,54 @@ const ViewerContainer: React.FC<ViewerContainerProps> = ({ imageUrls }) => {
|
|
));
|
|
));
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 通用测量工具激活函数
|
|
|
|
+ * @param config 测量工具配置
|
|
|
|
+ * @param selectedViewportIds 选中的 viewport IDs
|
|
|
|
+ */
|
|
|
|
+ const activateMeasurementTool = (
|
|
|
|
+ config: MeasurementToolConfig,
|
|
|
|
+ selectedViewportIds: string[]
|
|
|
|
+ ) => {
|
|
|
|
+ console.log(`开始${config.logPrefix}测量`);
|
|
|
|
+
|
|
|
|
+ const viewportIds = selectedViewportIds.length > 0
|
|
|
|
+ ? selectedViewportIds
|
|
|
|
+ : Array.from({ length: getVisibleViewportCount() }, (_, i) => `viewport-${i}`);
|
|
|
|
+
|
|
|
|
+ viewportIds.forEach((viewportId) => {
|
|
|
|
+ const success = config.activateFunction(viewportId);
|
|
|
|
+ if (success) {
|
|
|
|
+ console.log(`激活${config.logPrefix}测量工具成功`);
|
|
|
|
+ dispatch(
|
|
|
|
+ setToolActive({
|
|
|
|
+ toolName: config.toolName,
|
|
|
|
+ viewportId: viewportId,
|
|
|
|
+ })
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ console.log(`Activating ${config.logPrefix} Measurement from MeasurementPanel`);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取当前可见的 viewport 数量
|
|
|
|
+ */
|
|
|
|
+ const getVisibleViewportCount = (): number => {
|
|
|
|
+ switch (gridLayout) {
|
|
|
|
+ case '1x1':
|
|
|
|
+ return 1;
|
|
|
|
+ case '1x2':
|
|
|
|
+ case '2x1':
|
|
|
|
+ return Math.min(2, imageUrls.length);
|
|
|
|
+ case '2x2':
|
|
|
|
+ return Math.min(4, imageUrls.length);
|
|
|
|
+ default:
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
renderGrid();
|
|
renderGrid();
|
|
}, [selectedBodyPosition, gridLayout]);
|
|
}, [selectedBodyPosition, gridLayout]);
|
|
@@ -459,208 +540,17 @@ const ViewerContainer: React.FC<ViewerContainerProps> = ({ imageUrls }) => {
|
|
break;
|
|
break;
|
|
|
|
|
|
case '胫骨平台夹角':
|
|
case '胫骨平台夹角':
|
|
- console.log(`开始胫骨平台夹角测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = activateTibialPlateauAngleMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活胫骨平台夹角测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'TibialPlateauAngleTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = activateTibialPlateauAngleMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'TibialPlateauAngleTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- console.log('Activating TibialPlateauAngle Measurement from MeasurementPanel');
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
case '髋臼水平角':
|
|
case '髋臼水平角':
|
|
- console.log(`开始髋臼水平角测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = activateDARAMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活髋臼水平角测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'DARAMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = activateDARAMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'DARAMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- console.log('Activating DARA Measurement from MeasurementPanel');
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
case '髋关节牵引指数':
|
|
case '髋关节牵引指数':
|
|
- console.log(`开始髋关节牵引指数测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = activateHipDIMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活髋关节牵引指数测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'HipDIMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = activateHipDIMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'HipDIMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- console.log('Activating HipDI Measurement from MeasurementPanel');
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
case '髋关节水平角':
|
|
case '髋关节水平角':
|
|
- console.log(`开始髋关节水平角测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = activateHipNHAAngleMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活髋关节水平角测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'HipNHAAngleMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = activateHipNHAAngleMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'HipNHAAngleMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- console.log('Activating HipNHA Measurement from MeasurementPanel');
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
case '心锥比':
|
|
case '心锥比':
|
|
- console.log(`开始心锥比测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = MeasurementToolManager.activateVHSMeasurementTool(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活心锥比测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'VHSMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = MeasurementToolManager.activateVHSMeasurementTool(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'VHSMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- console.log('Activating VHS Measurement from MeasurementPanel');
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case '胫骨平台骨切开术':
|
|
|
|
- console.log(`开始胫骨平台骨切开术测量`);
|
|
|
|
- if (selectedViewportIds.length > 0) {
|
|
|
|
- selectedViewportIds.forEach((viewportId) => {
|
|
|
|
- const success = activateTPLOMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- console.log(`激活TPLO测量工具成功`);
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'TPLOMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- // 如果没有选中的 viewport,为所有可见的 viewport 激活
|
|
|
|
- const visibleViewportCount = getVisibleViewportCount();
|
|
|
|
- for (let i = 0; i < visibleViewportCount; i++) {
|
|
|
|
- const viewportId = `viewport-${i}`;
|
|
|
|
- const success = activateTPLOMeasurement(viewportId);
|
|
|
|
- if (success) {
|
|
|
|
- dispatch(
|
|
|
|
- setToolActive({
|
|
|
|
- toolName: 'TPLOMeasurementTool',
|
|
|
|
- viewportId: viewportId,
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ case '胫骨平台骨切开术': {
|
|
|
|
+ const config = MEASUREMENT_TOOL_CONFIGS[measurementAction];
|
|
|
|
+ if (config) {
|
|
|
|
+ activateMeasurementTool(config, selectedViewportIds);
|
|
}
|
|
}
|
|
- console.log('Activating TPLO Measurement from MeasurementPanel');
|
|
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
|
|
case '测量校正':
|
|
case '测量校正':
|
|
console.log('Measurement Calibration - 功能待实现');
|
|
console.log('Measurement Calibration - 功能待实现');
|
|
@@ -676,23 +566,6 @@ const ViewerContainer: React.FC<ViewerContainerProps> = ({ imageUrls }) => {
|
|
}
|
|
}
|
|
}, [measurementAction, selectedViewerUrls, gridLayout, dispatch]);
|
|
}, [measurementAction, selectedViewerUrls, gridLayout, dispatch]);
|
|
|
|
|
|
- /**
|
|
|
|
- * 获取当前可见的 viewport 数量
|
|
|
|
- */
|
|
|
|
- const getVisibleViewportCount = (): number => {
|
|
|
|
- switch (gridLayout) {
|
|
|
|
- case '1x1':
|
|
|
|
- return 1;
|
|
|
|
- case '1x2':
|
|
|
|
- case '2x1':
|
|
|
|
- return Math.min(2, imageUrls.length);
|
|
|
|
- case '2x2':
|
|
|
|
- return Math.min(4, imageUrls.length);
|
|
|
|
- default:
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
const handleSelectViewer = (imageUrl: string, event: React.MouseEvent) => {
|
|
const handleSelectViewer = (imageUrl: string, event: React.MouseEvent) => {
|
|
console.log(`handleSelectViewer : ${imageUrl}`);
|
|
console.log(`handleSelectViewer : ${imageUrl}`);
|
|
console.log(`selectedViewers 旧值:`, selectedViewerUrls);
|
|
console.log(`selectedViewers 旧值:`, selectedViewerUrls);
|