Browse Source

feat: 集成三个新测量工具(CBLO/股骨头覆盖率/髋臼背覆盖)到配置驱动架构

- 在 measurementToolManager.ts 中为三个工具添加完整的激活/停用/清除方法
- 在 stack.image.viewer.tsx 中导入工具、注册到工具组并导出包装函数
- 在 ViewerContainer.tsx 中添加工具配置、导入激活函数并添加到清除列表
- 在 measurementPanelSlice.ts 中添加三个新工具的类型定义
- 在 cornerstoneToolsSetup.ts 中全局注册三个新工具

新增工具:
- 水平截骨术 (CBLOMeasurementTool) - 9点测量,计算CORA和半径
- 股骨头覆盖率 (HipCoverageMeasurementTool) - 8点测量,计算左右侧覆盖率
- 髋臼背覆盖 (HipDorsalCoverageTool) - 5点测量,计算覆盖率百分比

改动文件:
- src/utils/measurementToolManager.ts
- src/pages/view/components/viewers/stack.image.viewer.tsx
- src/pages/view/components/ViewerContainer.tsx
- src/states/view/measurementPanelSlice.ts
- src/utils/cornerstoneToolsSetup.ts
sw 3 days ago
parent
commit
9817ba58e9

+ 33 - 1
src/pages/view/components/ViewerContainer.tsx

@@ -27,6 +27,10 @@ import StackViewer, {
   activateHipDIMeasurement,
   activateHipNHAAngleMeasurement,
   activateTPLOMeasurement,
+  activateTTAMeasurement,
+  activateCBLOMeasurement,
+  activateHipCoverageMeasurement,
+  activateHipDorsalCoverageMeasurement,
 } from './viewers/stack.image.viewer';
 import { useSelector, useDispatch } from 'react-redux';
 import store, { RootState } from '@/states/store';
@@ -91,6 +95,26 @@ const MEASUREMENT_TOOL_CONFIGS: Record<string, MeasurementToolConfig> = {
     activateFunction: activateTPLOMeasurement,
     logPrefix: 'TPLO',
   },
+  '胫骨结节前移术': {
+    toolName: 'TTAMeasurementTool',
+    activateFunction: activateTTAMeasurement,
+    logPrefix: 'TTA',
+  },
+  '水平截骨术': {
+    toolName: 'CBLOMeasurementTool',
+    activateFunction: activateCBLOMeasurement,
+    logPrefix: 'CBLO',
+  },
+  '股骨头覆盖率': {
+    toolName: 'HipCoverageMeasurementTool',
+    activateFunction: activateHipCoverageMeasurement,
+    logPrefix: 'HipCoverage',
+  },
+  '髋臼背覆盖': {
+    toolName: 'HipDorsalCoverageTool',
+    activateFunction: activateHipDorsalCoverageMeasurement,
+    logPrefix: 'HipDorsalCoverage',
+  },
 };
 
 const setup = () => {
@@ -499,6 +523,10 @@ const ViewerContainer: React.FC<ViewerContainerProps> = ({ imageUrls }) => {
             MeasurementToolManager.clearHipNHAAngleMeasurementsForViewports(selectedViewportIds);
             MeasurementToolManager.clearVHSMeasurementsForViewports(selectedViewportIds);
             MeasurementToolManager.clearTPLOMeasurementsForViewports(selectedViewportIds);
+            MeasurementToolManager.clearTTAMeasurementsForViewports(selectedViewportIds);
+            MeasurementToolManager.clearCBLOMeasurementsForViewports(selectedViewportIds);
+            MeasurementToolManager.clearHipCoverageMeasurementsForViewports(selectedViewportIds);
+            MeasurementToolManager.clearHipDorsalCoverageMeasurementsForViewports(selectedViewportIds);
           }
           console.log('Clearing All Measurements from MeasurementPanel');
           break;
@@ -544,7 +572,11 @@ const ViewerContainer: React.FC<ViewerContainerProps> = ({ imageUrls }) => {
         case '髋关节牵引指数':
         case '髋关节水平角':
         case '心锥比':
-        case '胫骨平台骨切开术': {
+        case '胫骨平台骨切开术':
+        case '胫骨结节前移术':
+        case '水平截骨术':
+        case '股骨头覆盖率':
+        case '髋臼背覆盖': {
           const config = MEASUREMENT_TOOL_CONFIGS[measurementAction];
           if (config) {
             activateMeasurementTool(config, selectedViewportIds);

+ 123 - 0
src/pages/view/components/viewers/stack.image.viewer.tsx

@@ -12,6 +12,10 @@ import HipDIMeasurementTool from '@/components/measures/HipDIMeasurementTool';
 import HipNHAAngleMeasurementTool from '@/components/measures/HipNHAAngleMeasurementTool';
 import VHSMeasurementTool from '@/components/measures/VHSMeasurementTool';
 import TPLOMeasurementTool from '@/components/measures/TPLOMeasurementTool';
+import TTAMeasurementTool from '@/components/measures/TTAMeasurementTool';
+import CBLOMeasurementTool from '@/components/measures/CBLOMeasurementTool';
+import HipCoverageMeasurementTool from '@/components/measures/HipCoverageMeasurementTool';
+import HipDorsalCoverageTool from '@/components/measures/HipDorsalCoverageTool';
 import { boolean } from 'zod';
 
 const {
@@ -129,6 +133,10 @@ function registerTools(viewportId, renderingEngineId) {
   toolGroup.addTool(HipNHAAngleMeasurementTool.toolName); // 添加髋关节水平角测量工具
   toolGroup.addTool(VHSMeasurementTool.toolName); // 添加心锥比测量工具
   toolGroup.addTool(TPLOMeasurementTool.toolName); // 添加TPLO测量工具
+  toolGroup.addTool(TTAMeasurementTool.toolName); // 添加TTA测量工具
+  toolGroup.addTool(CBLOMeasurementTool.toolName); // 添加CBLO测量工具
+  toolGroup.addTool(HipCoverageMeasurementTool.toolName); // 添加股骨头覆盖率测量工具
+  toolGroup.addTool(HipDorsalCoverageTool.toolName); // 添加髋臼背覆盖测量工具
 
   // 设置默认工具状态
   setupDefaultToolStates(toolGroup);
@@ -183,6 +191,10 @@ function setupDefaultToolStates(toolGroup: cornerstoneTools.Types.IToolGroup) {
   toolGroup.setToolPassive(HipNHAAngleMeasurementTool.toolName);
   toolGroup.setToolPassive(VHSMeasurementTool.toolName);
   toolGroup.setToolPassive(TPLOMeasurementTool.toolName);
+  toolGroup.setToolPassive(TTAMeasurementTool.toolName);
+  toolGroup.setToolPassive(CBLOMeasurementTool.toolName);
+  toolGroup.setToolPassive(HipCoverageMeasurementTool.toolName);
+  toolGroup.setToolPassive(HipDorsalCoverageTool.toolName);
 }
 export function addLMark(currentViewportId: string): void {
   // Implement the logic to add an L mark
@@ -929,6 +941,117 @@ export function isTPLOMeasurementActive(viewportId: string): boolean {
   return MeasurementToolManager.isTPLOMeasurementToolActive(viewportId);
 }
 
+// ==================== TTA测量相关函数 ====================
+
+/**
+ * 激活TTA测量工具
+ */
+export function activateTTAMeasurement(viewportId: string): boolean {
+  console.log(
+    `[activateTTAMeasurement] Activating TTA measurement for viewport: ${viewportId}`
+  );
+  return MeasurementToolManager.activateTTAMeasurementTool(viewportId);
+}
+
+/**
+ * 停用TTA测量工具
+ */
+export function deactivateTTAMeasurement(viewportId: string): boolean {
+  console.log(
+    `[deactivateTTAMeasurement] Deactivating TTA measurement for viewport: ${viewportId}`
+  );
+  return MeasurementToolManager.deactivateTTAMeasurementTool(viewportId);
+}
+
+/**
+ * 切换TTA测量工具状态
+ */
+export function toggleTTAMeasurement(viewportId: string): boolean {
+  console.log(
+    `[toggleTTAMeasurement] Toggling TTA measurement for viewport: ${viewportId}`
+  );
+  return MeasurementToolManager.toggleTTAMeasurementTool(viewportId);
+}
+
+/**
+ * 清除TTA测量标注
+ */
+export function clearTTAMeasurements(viewportId: string): boolean {
+  console.log(
+    `[clearTTAMeasurements] Clearing TTA measurements for viewport: ${viewportId}`
+  );
+  return MeasurementToolManager.clearTTAMeasurements(viewportId);
+}
+
+/**
+ * 获取TTA测量结果
+ */
+// eslint-disable-next-line
+export function getTTAMeasurements(viewportId: string): any[] {
+  console.log(
+    `[getTTAMeasurements] Getting TTA measurements for viewport: ${viewportId}`
+  );
+  return MeasurementToolManager.getTTAMeasurements(viewportId);
+}
+
+/**
+ * 检查TTA测量工具是否激活
+ */
+export function isTTAMeasurementActive(viewportId: string): boolean {
+  return MeasurementToolManager.isTTAMeasurementToolActive(viewportId);
+}
+
+// ==================== CBLO测量相关函数 ====================
+
+export function activateCBLOMeasurement(viewportId: string): boolean {
+  console.log(`[activateCBLOMeasurement] Activating CBLO measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.activateCBLOMeasurementTool(viewportId);
+}
+
+export function deactivateCBLOMeasurement(viewportId: string): boolean {
+  console.log(`[deactivateCBLOMeasurement] Deactivating CBLO measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.deactivateCBLOMeasurementTool(viewportId);
+}
+
+export function clearCBLOMeasurements(viewportId: string): boolean {
+  console.log(`[clearCBLOMeasurements] Clearing CBLO measurements for viewport: ${viewportId}`);
+  return MeasurementToolManager.clearCBLOMeasurements(viewportId);
+}
+
+// ==================== HipCoverage测量相关函数 ====================
+
+export function activateHipCoverageMeasurement(viewportId: string): boolean {
+  console.log(`[activateHipCoverageMeasurement] Activating HipCoverage measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.activateHipCoverageMeasurementTool(viewportId);
+}
+
+export function deactivateHipCoverageMeasurement(viewportId: string): boolean {
+  console.log(`[deactivateHipCoverageMeasurement] Deactivating HipCoverage measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.deactivateHipCoverageMeasurementTool(viewportId);
+}
+
+export function clearHipCoverageMeasurements(viewportId: string): boolean {
+  console.log(`[clearHipCoverageMeasurements] Clearing HipCoverage measurements for viewport: ${viewportId}`);
+  return MeasurementToolManager.clearHipCoverageMeasurements(viewportId);
+}
+
+// ==================== HipDorsalCoverage测量相关函数 ====================
+
+export function activateHipDorsalCoverageMeasurement(viewportId: string): boolean {
+  console.log(`[activateHipDorsalCoverageMeasurement] Activating HipDorsalCoverage measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.activateHipDorsalCoverageTool(viewportId);
+}
+
+export function deactivateHipDorsalCoverageMeasurement(viewportId: string): boolean {
+  console.log(`[deactivateHipDorsalCoverageMeasurement] Deactivating HipDorsalCoverage measurement for viewport: ${viewportId}`);
+  return MeasurementToolManager.deactivateHipDorsalCoverageTool(viewportId);
+}
+
+export function clearHipDorsalCoverageMeasurements(viewportId: string): boolean {
+  console.log(`[clearHipDorsalCoverageMeasurements] Clearing HipDorsalCoverage measurements for viewport: ${viewportId}`);
+  return MeasurementToolManager.clearHipDorsalCoverageMeasurements(viewportId);
+}
+
 const StackViewer = ({
   imageIndex = 0,
   imageUrls = [],

+ 4 - 0
src/states/view/measurementPanelSlice.ts

@@ -13,6 +13,10 @@ export type MeasurementAction =
   | '髋关节牵引指数'
   | '心锥比'
   | '胫骨平台骨切开术'
+  | '胫骨结节前移术'
+  | '水平截骨术'
+  | '股骨头覆盖率'
+  | '髋臼背覆盖'
   | null;
 
 // 测量结果类型

+ 8 - 0
src/utils/cornerstoneToolsSetup.ts

@@ -5,6 +5,10 @@ import HipDIMeasurementTool from '@/components/measures/HipDIMeasurementTool';
 import HipNHAAngleMeasurementTool from '@/components/measures/HipNHAAngleMeasurementTool';
 import VHSMeasurementTool from '@/components/measures/VHSMeasurementTool';
 import TPLOMeasurementTool from '@/components/measures/TPLOMeasurementTool';
+import TTAMeasurementTool from '@/components/measures/TTAMeasurementTool';
+import CBLOMeasurementTool from '@/components/measures/CBLOMeasurementTool';
+import HipCoverageMeasurementTool from '@/components/measures/HipCoverageMeasurementTool';
+import HipDorsalCoverageTool from '@/components/measures/HipDorsalCoverageTool';
 
 const {
   MagnifyTool,
@@ -50,6 +54,10 @@ export function registerGlobalTools(): void {
     cornerstoneTools.addTool(HipNHAAngleMeasurementTool); // 添加髋关节水平角测量工具
     cornerstoneTools.addTool(VHSMeasurementTool); // 添加心锥比测量工具
     cornerstoneTools.addTool(TPLOMeasurementTool); // 添加胫骨平台骨切开术测量工具
+    cornerstoneTools.addTool(TTAMeasurementTool); // 添加胫骨结节前移术测量工具
+    cornerstoneTools.addTool(CBLOMeasurementTool); // 添加水平截骨术测量工具
+    cornerstoneTools.addTool(HipCoverageMeasurementTool); // 添加股骨头覆盖率测量工具
+    cornerstoneTools.addTool(HipDorsalCoverageTool); // 添加髋臼背覆盖测量工具
 
     toolsRegistered = true;
     console.log('[cornerstoneToolsSetup] All tools registered successfully');

+ 600 - 0
src/utils/measurementToolManager.ts

@@ -6,6 +6,10 @@ import HipDIMeasurementTool from '@/components/measures/HipDIMeasurementTool';
 import HipNHAAngleMeasurementTool from '@/components/measures/HipNHAAngleMeasurementTool';
 import VHSMeasurementTool from '@/components/measures/VHSMeasurementTool';
 import TPLOMeasurementTool from '@/components/measures/TPLOMeasurementTool';
+import TTAMeasurementTool from '@/components/measures/TTAMeasurementTool';
+import CBLOMeasurementTool from '@/components/measures/CBLOMeasurementTool';
+import HipCoverageMeasurementTool from '@/components/measures/HipCoverageMeasurementTool';
+import HipDorsalCoverageTool from '@/components/measures/HipDorsalCoverageTool';
 
 const {
   ToolGroupManager,
@@ -2017,4 +2021,600 @@ export class MeasurementToolManager {
       this.clearTPLOMeasurements(viewportId)
     );
   }
+
+  // ==================== TTA测量工具 ====================
+
+  /**
+   * 激活TTA测量工具
+   */
+  static activateTTAMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      // 停用其他可能冲突的工具
+      toolGroup.setToolPassive(WindowLevelTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(MagnifyTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(LengthTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(AngleTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(TibialPlateauAngleTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(DARAMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(HipDIMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(HipNHAAngleMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(VHSMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+      toolGroup.setToolPassive(TPLOMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+
+      // 激活TTA测量工具
+      toolGroup.setToolActive(TTAMeasurementTool.toolName, {
+        bindings: [{ mouseButton: MouseBindings.Primary }],
+      });
+
+      // 获取工具实例并激活修改模式
+      const toolInstance = toolGroup.getToolInstance(
+        TTAMeasurementTool.toolName
+      ) as TTAMeasurementTool;
+
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (toolInstance && viewport.element) {
+        toolInstance._activateModify(viewport.element);
+      }
+
+      // 自动创建一个预设的注解
+      try {
+        if (viewport && viewport.element) {
+          // 创建预设注解
+          const defaultAnnotation = TTAMeasurementTool.createDefaultAnnotation(
+            viewport.element as HTMLDivElement,
+            viewport as cornerstone.Types.IStackViewport
+          );
+
+          // 添加注解到状态管理
+          cornerstoneTools.annotation.state.addAnnotation(
+            defaultAnnotation,
+            viewport.element
+          );
+
+          // 获取工具实例并更新缓存统计数据
+          const enabledElement = cornerstone.getEnabledElement(viewport.element);
+          if (enabledElement) {
+            const toolInstance = toolGroup.getToolInstance(
+              TTAMeasurementTool.toolName
+            ) as TTAMeasurementTool;
+
+            if (toolInstance && '_updateCachedStats' in toolInstance) {
+              (toolInstance as any)._updateCachedStats(defaultAnnotation, enabledElement);
+            }
+          }
+
+          // 触发渲染更新
+          viewport.render();
+
+          console.log('[MeasurementToolManager] Default TTA annotation created successfully');
+        }
+      } catch (error) {
+        console.error('[MeasurementToolManager] Failed to create default TTA annotation:', error);
+        // 注解创建失败不影响工具激活
+      }
+
+      console.log(
+        `[MeasurementToolManager] TTA tool activated for viewport: ${viewportId}`
+      );
+      return true;
+    } catch (error) {
+      console.error(
+        `[MeasurementToolManager] Error activating TTA tool:`,
+        error
+      );
+      return false;
+    }
+  }
+
+  /**
+   * 停用TTA测量工具
+   */
+  static deactivateTTAMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(TTAMeasurementTool.toolName, {
+        removeAllBindings: true,
+      });
+      console.log(
+        `[MeasurementToolManager] TTA tool deactivated for viewport: ${viewportId}`
+      );
+      return true;
+    } catch (error) {
+      console.error(
+        `[MeasurementToolManager] Error deactivating TTA tool:`,
+        error
+      );
+      return false;
+    }
+  }
+
+  /**
+   * 检查TTA测量工具是否处于激活状态
+   */
+  static isTTAMeasurementToolActive(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      const activeTool = toolGroup.getActivePrimaryMouseButtonTool();
+      return activeTool === TTAMeasurementTool.toolName;
+    } catch (error) {
+      console.error(
+        `[MeasurementToolManager] Error checking TTA tool state:`,
+        error
+      );
+      return false;
+    }
+  }
+
+  /**
+   * 切换TTA测量工具状态
+   */
+  static toggleTTAMeasurementTool(viewportId: string): boolean {
+    const isActive = this.isTTAMeasurementToolActive(viewportId);
+
+    if (isActive) {
+      return this.deactivateTTAMeasurementTool(viewportId);
+    } else {
+      return this.activateTTAMeasurementTool(viewportId);
+    }
+  }
+
+  /**
+   * 清除指定 viewport 的所有TTA测量标注
+   */
+  static clearTTAMeasurements(viewportId: string): boolean {
+    try {
+      const viewport =
+        cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (!viewport) {
+        console.warn(
+          `[MeasurementToolManager] Viewport not found: ${viewportId}`
+        );
+        return false;
+      }
+
+      const annotations = cornerstoneTools.annotation.state.getAnnotations(
+        TTAMeasurementTool.toolName,
+        viewport.element
+      );
+
+      let removedCount = 0;
+      annotations.forEach((annotation) => {
+        if (annotation.annotationUID) {
+          cornerstoneTools.annotation.state.removeAnnotation(
+            annotation.annotationUID
+          );
+          removedCount++;
+        }
+      });
+
+      viewport.render();
+      console.log(
+        `[MeasurementToolManager] Cleared ${removedCount} TTA measurements for viewport: ${viewportId}`
+      );
+      return true;
+    } catch (error) {
+      console.error(
+        `[MeasurementToolManager] Error clearing TTA measurements:`,
+        error
+      );
+      return false;
+    }
+  }
+
+  /**
+   * 获取指定 viewport 的所有TTA测量结果
+   */
+  // eslint-disable-next-line
+  static getTTAMeasurements(viewportId: string): any[] {
+    try {
+      const viewport =
+        cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (!viewport) {
+        console.warn(
+          `[MeasurementToolManager] Viewport not found: ${viewportId}`
+        );
+        return [];
+      }
+
+      const annotations = cornerstoneTools.annotation.state.getAnnotations(
+        TTAMeasurementTool.toolName,
+        viewport.element
+      );
+
+      return annotations.map((annotation) => ({
+        annotationUID: annotation.annotationUID,
+        ttaDistance: annotation.data?.cachedStats?.ttaDistance || 0,
+        unit: 'mm',
+        points: annotation.data?.handles?.points || [],
+      }));
+    } catch (error) {
+      console.error(
+        `[MeasurementToolManager] Error getting TTA measurements:`,
+        error
+      );
+      return [];
+    }
+  }
+
+  /**
+   * 为多个 viewport 批量激活TTA测量工具
+   */
+  static activateTTAMeasurementToolForViewports(
+    viewportIds: string[]
+  ): boolean[] {
+    return viewportIds.map((viewportId) =>
+      this.activateTTAMeasurementTool(viewportId)
+    );
+  }
+
+  /**
+   * 为多个 viewport 批量停用TTA测量工具
+   */
+  static deactivateTTAMeasurementToolForViewports(
+    viewportIds: string[]
+  ): boolean[] {
+    return viewportIds.map((viewportId) =>
+      this.deactivateTTAMeasurementTool(viewportId)
+    );
+  }
+
+  /**
+   * 为多个 viewport 批量清除TTA测量
+   */
+  static clearTTAMeasurementsForViewports(viewportIds: string[]): boolean[] {
+    return viewportIds.map((viewportId) =>
+      this.clearTTAMeasurements(viewportId)
+    );
+  }
+
+  // ==================== CBLO(水平截骨术)测量工具 ====================
+
+  /**
+   * 激活CBLO测量工具
+   */
+  static activateCBLOMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(WindowLevelTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(MagnifyTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(LengthTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(AngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TibialPlateauAngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(DARAMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipDIMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipNHAAngleMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(VHSMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TPLOMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TTAMeasurementTool.toolName, { removeAllBindings: true });
+
+      toolGroup.setToolActive(CBLOMeasurementTool.toolName, {
+        bindings: [{ mouseButton: MouseBindings.Primary }],
+      });
+
+      const toolInstance = toolGroup.getToolInstance(CBLOMeasurementTool.toolName) as CBLOMeasurementTool;
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (toolInstance && viewport.element) {
+        toolInstance._activateModify(viewport.element);
+      }
+
+      try {
+        if (viewport && viewport.element) {
+          const defaultAnnotation = CBLOMeasurementTool.createDefaultAnnotation(
+            viewport.element as HTMLDivElement,
+            viewport as cornerstone.Types.IStackViewport
+          );
+
+          cornerstoneTools.annotation.state.addAnnotation(defaultAnnotation, viewport.element);
+
+          const enabledElement = cornerstone.getEnabledElement(viewport.element);
+          if (enabledElement) {
+            const toolInstance = toolGroup.getToolInstance(CBLOMeasurementTool.toolName) as CBLOMeasurementTool;
+            if (toolInstance && '_updateCachedStats' in toolInstance) {
+              (toolInstance as any)._updateCachedStats(defaultAnnotation, enabledElement);
+            }
+          }
+
+          viewport.render();
+          console.log('[MeasurementToolManager] Default CBLO annotation created successfully');
+        }
+      } catch (error) {
+        console.error('[MeasurementToolManager] Failed to create default CBLO annotation:', error);
+      }
+
+      console.log(`[MeasurementToolManager] CBLO tool activated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error activating CBLO tool:`, error);
+      return false;
+    }
+  }
+
+  static deactivateCBLOMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(CBLOMeasurementTool.toolName, { removeAllBindings: true });
+      console.log(`[MeasurementToolManager] CBLO tool deactivated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error deactivating CBLO tool:`, error);
+      return false;
+    }
+  }
+
+  static clearCBLOMeasurements(viewportId: string): boolean {
+    try {
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (!viewport) return false;
+
+      const annotations = cornerstoneTools.annotation.state.getAnnotations(
+        CBLOMeasurementTool.toolName,
+        viewport.element
+      );
+
+      let removedCount = 0;
+      annotations.forEach((annotation) => {
+        if (annotation.annotationUID) {
+          cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
+          removedCount++;
+        }
+      });
+
+      viewport.render();
+      console.log(`[MeasurementToolManager] Cleared ${removedCount} CBLO measurements for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error clearing CBLO measurements:`, error);
+      return false;
+    }
+  }
+
+  static clearCBLOMeasurementsForViewports(viewportIds: string[]): boolean[] {
+    return viewportIds.map((viewportId) => this.clearCBLOMeasurements(viewportId));
+  }
+
+  // ==================== HipCoverage(股骨头覆盖率)测量工具 ====================
+
+  static activateHipCoverageMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(WindowLevelTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(MagnifyTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(LengthTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(AngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TibialPlateauAngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(DARAMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipDIMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipNHAAngleMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(VHSMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TPLOMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TTAMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(CBLOMeasurementTool.toolName, { removeAllBindings: true });
+
+      toolGroup.setToolActive(HipCoverageMeasurementTool.toolName, {
+        bindings: [{ mouseButton: MouseBindings.Primary }],
+      });
+
+      const toolInstance = toolGroup.getToolInstance(HipCoverageMeasurementTool.toolName) as HipCoverageMeasurementTool;
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (toolInstance && viewport.element) {
+        toolInstance._activateModify(viewport.element);
+      }
+
+      try {
+        if (viewport && viewport.element) {
+          const defaultAnnotation = HipCoverageMeasurementTool.createDefaultAnnotation(
+            viewport.element as HTMLDivElement,
+            viewport as cornerstone.Types.IStackViewport
+          );
+
+          cornerstoneTools.annotation.state.addAnnotation(defaultAnnotation, viewport.element);
+
+          const enabledElement = cornerstone.getEnabledElement(viewport.element);
+          if (enabledElement) {
+            const toolInstance = toolGroup.getToolInstance(HipCoverageMeasurementTool.toolName) as HipCoverageMeasurementTool;
+            if (toolInstance && '_updateCachedStats' in toolInstance) {
+              (toolInstance as any)._updateCachedStats(defaultAnnotation, enabledElement);
+            }
+          }
+
+          viewport.render();
+          console.log('[MeasurementToolManager] Default HipCoverage annotation created successfully');
+        }
+      } catch (error) {
+        console.error('[MeasurementToolManager] Failed to create default HipCoverage annotation:', error);
+      }
+
+      console.log(`[MeasurementToolManager] HipCoverage tool activated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error activating HipCoverage tool:`, error);
+      return false;
+    }
+  }
+
+  static deactivateHipCoverageMeasurementTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(HipCoverageMeasurementTool.toolName, { removeAllBindings: true });
+      console.log(`[MeasurementToolManager] HipCoverage tool deactivated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error deactivating HipCoverage tool:`, error);
+      return false;
+    }
+  }
+
+  static clearHipCoverageMeasurements(viewportId: string): boolean {
+    try {
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (!viewport) return false;
+
+      const annotations = cornerstoneTools.annotation.state.getAnnotations(
+        HipCoverageMeasurementTool.toolName,
+        viewport.element
+      );
+
+      let removedCount = 0;
+      annotations.forEach((annotation) => {
+        if (annotation.annotationUID) {
+          cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
+          removedCount++;
+        }
+      });
+
+      viewport.render();
+      console.log(`[MeasurementToolManager] Cleared ${removedCount} HipCoverage measurements for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error clearing HipCoverage measurements:`, error);
+      return false;
+    }
+  }
+
+  static clearHipCoverageMeasurementsForViewports(viewportIds: string[]): boolean[] {
+    return viewportIds.map((viewportId) => this.clearHipCoverageMeasurements(viewportId));
+  }
+
+  // ==================== HipDorsalCoverage(髋臼背覆盖)测量工具 ====================
+
+  static activateHipDorsalCoverageTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(WindowLevelTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(MagnifyTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(LengthTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(AngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TibialPlateauAngleTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(DARAMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipDIMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipNHAAngleMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(VHSMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TPLOMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(TTAMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(CBLOMeasurementTool.toolName, { removeAllBindings: true });
+      toolGroup.setToolPassive(HipCoverageMeasurementTool.toolName, { removeAllBindings: true });
+
+      toolGroup.setToolActive(HipDorsalCoverageTool.toolName, {
+        bindings: [{ mouseButton: MouseBindings.Primary }],
+      });
+
+      const toolInstance = toolGroup.getToolInstance(HipDorsalCoverageTool.toolName) as HipDorsalCoverageTool;
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (toolInstance && viewport.element) {
+        toolInstance._activateModify(viewport.element);
+      }
+
+      try {
+        if (viewport && viewport.element) {
+          const defaultAnnotation = HipDorsalCoverageTool.createDefaultAnnotation(
+            viewport.element as HTMLDivElement,
+            viewport as cornerstone.Types.IStackViewport
+          );
+
+          cornerstoneTools.annotation.state.addAnnotation(defaultAnnotation, viewport.element);
+
+          const enabledElement = cornerstone.getEnabledElement(viewport.element);
+          if (enabledElement) {
+            const toolInstance = toolGroup.getToolInstance(HipDorsalCoverageTool.toolName) as HipDorsalCoverageTool;
+            if (toolInstance && '_updateCachedStats' in toolInstance) {
+              (toolInstance as any)._updateCachedStats(defaultAnnotation, enabledElement);
+            }
+          }
+
+          viewport.render();
+          console.log('[MeasurementToolManager] Default HipDorsalCoverage annotation created successfully');
+        }
+      } catch (error) {
+        console.error('[MeasurementToolManager] Failed to create default HipDorsalCoverage annotation:', error);
+      }
+
+      console.log(`[MeasurementToolManager] HipDorsalCoverage tool activated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error activating HipDorsalCoverage tool:`, error);
+      return false;
+    }
+  }
+
+  static deactivateHipDorsalCoverageTool(viewportId: string): boolean {
+    const toolGroup = this.getToolGroup(viewportId);
+    if (!toolGroup) return false;
+
+    try {
+      toolGroup.setToolPassive(HipDorsalCoverageTool.toolName, { removeAllBindings: true });
+      console.log(`[MeasurementToolManager] HipDorsalCoverage tool deactivated for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error deactivating HipDorsalCoverage tool:`, error);
+      return false;
+    }
+  }
+
+  static clearHipDorsalCoverageMeasurements(viewportId: string): boolean {
+    try {
+      const viewport = cornerstone.getEnabledElementByViewportId(viewportId)?.viewport;
+      if (!viewport) return false;
+
+      const annotations = cornerstoneTools.annotation.state.getAnnotations(
+        HipDorsalCoverageTool.toolName,
+        viewport.element
+      );
+
+      let removedCount = 0;
+      annotations.forEach((annotation) => {
+        if (annotation.annotationUID) {
+          cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
+          removedCount++;
+        }
+      });
+
+      viewport.render();
+      console.log(`[MeasurementToolManager] Cleared ${removedCount} HipDorsalCoverage measurements for viewport: ${viewportId}`);
+      return true;
+    } catch (error) {
+      console.error(`[MeasurementToolManager] Error clearing HipDorsalCoverage measurements:`, error);
+      return false;
+    }
+  }
+
+  static clearHipDorsalCoverageMeasurementsForViewports(viewportIds: string[]): boolean[] {
+    return viewportIds.map((viewportId) => this.clearHipDorsalCoverageMeasurements(viewportId));
+  }
 }