stack.image.viewer.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. import React, { useEffect, useRef } from 'react';
  2. import * as cornerstone from '@cornerstonejs/core';
  3. import type { Types } from '@cornerstonejs/core';
  4. import * as cornerstoneTools from '@cornerstonejs/tools';
  5. import * as cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
  6. import { useSelector } from 'react-redux';
  7. import { RootState } from '@/states/store';
  8. import { SystemMode } from '@/states/systemModeSlice';
  9. import store from '@/states/store';
  10. import { clearAction } from '@/states/view/functionAreaSlice';
  11. import { useDispatch } from 'react-redux';
  12. import { SplineROITool } from '@cornerstonejs/tools';
  13. import { eventTarget } from '@cornerstonejs/core';
  14. const {
  15. MagnifyTool,
  16. PanTool,
  17. WindowLevelTool,
  18. StackScrollTool,
  19. ZoomTool,
  20. LabelTool,
  21. ToolGroupManager,
  22. Enums: csToolsEnums,
  23. PlanarRotateTool,
  24. } = cornerstoneTools;
  25. const { MouseBindings } = csToolsEnums;
  26. let toolGroup: cornerstoneTools.Types.IToolGroup;
  27. let currentViewportId: string;
  28. function overlayRedRectangle() {
  29. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  30. .viewport as cornerstone.StackViewport;
  31. const viewportElement = viewport.element;
  32. const annotations = cornerstoneTools.annotation.state.getAnnotations(
  33. 'LinearSplineROI',
  34. viewportElement
  35. );
  36. if (!annotations || annotations.length === 0) {
  37. console.log('No ROI annotations found');
  38. return;
  39. }
  40. const annotation = annotations[annotations.length - 1];
  41. const points = annotation?.data?.handles?.points;
  42. // 创建一个覆盖 Canvas
  43. const canvas = document.createElement('canvas');
  44. canvas.style.position = 'absolute';
  45. canvas.width = viewportElement.clientWidth;
  46. canvas.height = viewportElement.clientHeight;
  47. viewportElement.firstChild?.appendChild(canvas);
  48. const ctx = canvas.getContext('2d');
  49. if (!ctx) {
  50. throw new Error('Failed to get 2D context from canvas');
  51. }
  52. ctx.fillStyle = 'rgba(0, 0, 0, 1)';
  53. // 将 ROI 坐标转换为 Canvas 坐标
  54. if (!points) {
  55. console.log('No points found in handles');
  56. return;
  57. }
  58. // Convert all points to canvas coordinates
  59. const z = viewport.getCurrentImageIdIndex() || 0;
  60. const canvasPoints = points.map((point: Types.Point2 | Types.Point3) => {
  61. const point3D: Types.Point3 =
  62. point.length === 2 ? [point[0], point[1], z] : point;
  63. return viewport.worldToCanvas(point3D);
  64. });
  65. // Log for debugging
  66. console.log('Canvas Points:', canvasPoints);
  67. // Draw the polygon
  68. ctx.beginPath();
  69. ctx.rect(0, 0, canvas.width, canvas.height); // Full canvas for evenodd rule
  70. ctx.moveTo(canvasPoints[0][0], canvasPoints[0][1]); // Start at the first point
  71. for (let i = 1; i < canvasPoints.length; i++) {
  72. ctx.lineTo(canvasPoints[i][0], canvasPoints[i][1]); // Connect to subsequent points
  73. }
  74. ctx.closePath(); // Close the polygon
  75. ctx.fill('evenodd'); // Fill with red
  76. }
  77. eventTarget.addEventListener(
  78. cornerstoneTools.Enums.Events.ANNOTATION_COMPLETED,
  79. (evt) => {
  80. const { annotation } = evt.detail;
  81. console.log('Annotation completed event:', annotation);
  82. if (annotation.metadata.toolName === 'LinearSplineROI') {
  83. console.log('SplineROITool annotation completed:', annotation);
  84. overlayRedRectangle();
  85. //取消工具激活状态
  86. const toolGroup = ToolGroupManager.getToolGroup('STACK_TOOL_GROUP_ID');
  87. if (!toolGroup) {
  88. console.log('toolGroup not found');
  89. }
  90. toolGroup?.setToolPassive('LinearSplineROI', { removeAllBindings: true });
  91. }
  92. }
  93. );
  94. function registerTools(viewportId, renderingEngineId) {
  95. // Add tools to Cornerstone3D
  96. cornerstoneTools.addTool(MagnifyTool);
  97. cornerstoneTools.addTool(PanTool);
  98. cornerstoneTools.addTool(WindowLevelTool);
  99. cornerstoneTools.addTool(StackScrollTool);
  100. cornerstoneTools.addTool(ZoomTool);
  101. cornerstoneTools.addTool(LabelTool);
  102. cornerstoneTools.addTool(PlanarRotateTool);
  103. // Define a tool group
  104. const toolGroupId = 'STACK_TOOL_GROUP_ID';
  105. const toolGroupTmp = ToolGroupManager.createToolGroup(toolGroupId);
  106. if (!toolGroupTmp) {
  107. return;
  108. }
  109. toolGroup = toolGroupTmp;
  110. // Add tools to the tool group
  111. toolGroup.addTool(MagnifyTool.toolName);
  112. toolGroup.addTool(PanTool.toolName);
  113. toolGroup.addTool(WindowLevelTool.toolName);
  114. toolGroup.addTool(StackScrollTool.toolName);
  115. toolGroup.addTool(ZoomTool.toolName);
  116. toolGroup.addTool(LabelTool.toolName);
  117. toolGroup.addTool(PlanarRotateTool.toolName);
  118. // Set the LabelTool as active
  119. // toolGroup.setToolActive(LabelTool.toolName, {
  120. // bindings: [
  121. // {
  122. // mouseButton: MouseBindings.Primary, // Left Click
  123. // },
  124. // ],
  125. // });
  126. // Set the initial state of the tools
  127. // toolGroup.setToolActive(WindowLevelTool.toolName, {
  128. // bindings: [
  129. // {
  130. // mouseButton: MouseBindings.Primary, // Left Click
  131. // },
  132. // ],
  133. // });
  134. toolGroup.setToolActive(PanTool.toolName, {
  135. bindings: [
  136. {
  137. mouseButton: MouseBindings.Auxiliary, // Middle Click
  138. },
  139. ],
  140. });
  141. toolGroup.setToolActive(ZoomTool.toolName, {
  142. bindings: [
  143. {
  144. mouseButton: MouseBindings.Secondary, // Right Click
  145. },
  146. ],
  147. });
  148. toolGroup.setToolActive(StackScrollTool.toolName, {
  149. bindings: [
  150. {
  151. mouseButton: MouseBindings.Wheel, // Mouse Wheel
  152. },
  153. ],
  154. });
  155. toolGroup.addViewport(viewportId, renderingEngineId);
  156. }
  157. function addLMark(): void {
  158. // Implement the logic to add an L mark
  159. console.log('Adding L Mark');
  160. toolGroup.setToolActive(LabelTool.toolName, {
  161. bindings: [
  162. // {
  163. // mouseButton: MouseBindings.Primary, // Left Click
  164. // },
  165. ],
  166. });
  167. const position: Types.Point3 = [100, 100, 0]; // Example position
  168. const text = 'L'; // Predefined text
  169. LabelTool.hydrate(currentViewportId, position, text);
  170. toolGroup.setToolPassive(LabelTool.toolName, {
  171. removeAllBindings: true,
  172. });
  173. // const enabledElement = cornerstone.getEnabledElementByViewportId(currentViewportId);
  174. // cursors.elementCursor.resetElementCursor(elementRef.current as HTMLDivElement);
  175. }
  176. function addRLabel(viewportId) {
  177. toolGroup.setToolActive(LabelTool.toolName, {
  178. bindings: [],
  179. });
  180. const element = document.getElementById(viewportId);
  181. const elementHeight = element ? element.getBoundingClientRect().height : 0;
  182. const position: Types.Point3 = [100, elementHeight / 2, 0]; // Example position
  183. const text = 'R'; // Predefined text
  184. LabelTool.hydrate(viewportId, position, text);
  185. toolGroup.setToolPassive(LabelTool.toolName, { removeAllBindings: true });
  186. }
  187. function adjustBrightnessAndContrast() {
  188. const planar = toolGroup.getToolInstance(WindowLevelTool.toolName); // Reset rotation angle
  189. const isActive = planar.mode === csToolsEnums.ToolModes.Active;
  190. if (isActive) {
  191. toolGroup.setToolPassive(WindowLevelTool.toolName, {
  192. removeAllBindings: true,
  193. });
  194. } else {
  195. toolGroup.setToolActive(WindowLevelTool.toolName, {
  196. bindings: [
  197. {
  198. mouseButton: MouseBindings.Primary, // Left Click
  199. },
  200. ],
  201. });
  202. }
  203. }
  204. function fitImageSize() {
  205. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  206. .viewport as cornerstone.StackViewport;
  207. viewport.resetCamera();
  208. viewport.render();
  209. }
  210. function deleteSelectedMark(): void {
  211. const viewport =
  212. cornerstone.getEnabledElementByViewportId(currentViewportId).viewport;
  213. const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
  214. for (const annotation of allAnnotations) {
  215. if (annotation.data.text === 'L' || annotation.data.text === 'R') {
  216. cornerstoneTools.annotation.state.removeAnnotation(
  217. annotation.annotationUID!
  218. );
  219. }
  220. }
  221. viewport.render();
  222. }
  223. function addMask(): void {
  224. // Implement the logic to add a mask
  225. console.log('Adding Mask');
  226. // Add the specific logic to add a mask here
  227. cornerstoneTools.addTool(SplineROITool);
  228. toolGroup.addTool(SplineROITool.toolName);
  229. toolGroup.addToolInstance('LinearSplineROI', SplineROITool.toolName, {
  230. spline: {
  231. type: SplineROITool.SplineTypes.Linear,
  232. },
  233. });
  234. toolGroup.setToolActive('LinearSplineROI', {
  235. bindings: [{ mouseButton: MouseBindings.Primary }],
  236. });
  237. }
  238. function HorizontalFlip(): void {
  239. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  240. .viewport as cornerstone.StackViewport;
  241. // 切换水平翻转状态
  242. const { flipHorizontal } = viewport.getCamera();
  243. viewport.setCamera({ flipHorizontal: !flipHorizontal });
  244. console.log('Flipping Image Horizontally');
  245. }
  246. function VerticalFlip(): void {
  247. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  248. .viewport as cornerstone.StackViewport;
  249. // 切换竖直翻转状态
  250. const { flipVertical } = viewport.getCamera();
  251. viewport.setCamera({ flipVertical: !flipVertical });
  252. }
  253. function ApplyColormap(): void {
  254. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  255. .viewport as cornerstone.StackViewport;
  256. // Implement the logic to apply colormap
  257. viewport.setProperties({ colormap: { name: 'hsv' } });
  258. viewport.render();
  259. console.log('Applying Colormap');
  260. }
  261. function RotateCounterclockwise90(): void {
  262. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  263. .viewport as cornerstone.StackViewport;
  264. // let { rotation } = viewport.getViewPresentation();
  265. // const currentRotataion = viewport.getRotation();
  266. // viewport.rotation(22)
  267. // console.log(`rotation:${rotation}`)
  268. // if(!!rotation){
  269. // rotation=0;
  270. // }
  271. // viewport.setViewPresentation({ rotation: rotation ?? 0 + 30 });
  272. // // // Implement the logic to rotate the image counterclockwise
  273. // // viewport.setCamera({ rotation: -90 });
  274. // viewport.render();
  275. // // 1. 获取当前的视图表现状态(一个完整的对象)
  276. // const currentViewPresentation = viewport.getViewPresentation();
  277. // // 2. 创建一个新的对象,基于当前状态,只更新你需要修改的属性
  278. // // 使用展开运算符 (...) 来复制所有现有属性
  279. // const newViewPresentation = {
  280. // ...currentViewPresentation, // 保留所有原有属性,如 scale, translation, flip 等
  281. // rotation: currentViewPresentation.rotation ?? + 30, // 只覆盖 rotation 属性
  282. // };
  283. // // 3. 将完整的新状态对象设置回 viewport
  284. // viewport.setViewPresentation(newViewPresentation);
  285. // viewport.render();
  286. //----------------------------------------
  287. // // 获取当前的完整视图状态
  288. // const currentViewState = viewport.getViewPresentation();
  289. // // 创建新的视图状态对象,保持所有其他属性不变
  290. // const newViewState = {
  291. // ...currentViewState, // 保留所有现有属性
  292. // rotation: (currentViewState.rotation ?? 0 + 30) % 360 // 只修改rotation
  293. // };
  294. // // 设置新的视图状态
  295. // viewport.setViewPresentation(newViewState);
  296. // viewport.render();
  297. //-----------------------------------------
  298. // 获取当前相机
  299. const camera = viewport.getCamera();
  300. // // 计算新的旋转角度(当前角度 + 90度)
  301. // const newRotation = (camera.rotation ?? 0) + 90;
  302. // // 转换为弧度
  303. // const radians = newRotation * Math.PI / 180;
  304. // // 计算新的viewUp向量(顺时针旋转90度)
  305. // const newViewUp:[number, number, number] = [
  306. // Math.cos(radians), // X分量
  307. // Math.sin(radians), // Y分量
  308. // 0 // Z分量
  309. // ];
  310. // // 设置新的相机参数
  311. // viewport.setCamera({
  312. // ...camera, // 保持其他相机参数不变
  313. // viewUp: newViewUp, // 更新向上向量
  314. // rotation: newRotation // 更新旋转角度(可选,但推荐)
  315. // });
  316. // 计算新的旋转角度(当前角度 + 90度)
  317. const newRotation = (camera.rotation ?? 0) + 90;
  318. // 但计算viewUp向量时,我们应该使用90度的弧度,而不是新角度的弧度!
  319. const ninetyDegreesRadians = (90 * Math.PI) / 180;
  320. // 获取当前的viewUp向量
  321. const currentViewUp = camera.viewUp || [0, 1, 0];
  322. // 计算旋转后的viewUp向量(这才是正确的相对旋转)
  323. const newViewUp: [number, number, number] = [
  324. currentViewUp[0] * Math.cos(ninetyDegreesRadians) -
  325. currentViewUp[1] * Math.sin(ninetyDegreesRadians),
  326. currentViewUp[0] * Math.sin(ninetyDegreesRadians) +
  327. currentViewUp[1] * Math.cos(ninetyDegreesRadians),
  328. 0,
  329. ];
  330. // 设置新的相机参数
  331. viewport.setCamera({
  332. ...camera,
  333. viewUp: newViewUp,
  334. rotation: newRotation % 360, // 确保角度在0-359范围内
  335. });
  336. viewport.render();
  337. console.log('Rotating Image Counterclockwise 90°');
  338. }
  339. function RotateClockwise90(): void {
  340. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  341. .viewport as cornerstone.StackViewport;
  342. const camera = viewport.getCamera();
  343. // 计算新的旋转角度(当前角度 + 90度)
  344. const newRotation = (camera.rotation ?? 0) - 90;
  345. // 但计算viewUp向量时,我们应该使用90度的弧度,而不是新角度的弧度!
  346. const ninetyDegreesRadians = (90 * Math.PI) / 180;
  347. // 获取当前的viewUp向量
  348. const currentViewUp = camera.viewUp || [0, 1, 0];
  349. // 计算旋转后的viewUp向量(这才是正确的相对旋转)
  350. const newViewUp: [number, number, number] = [
  351. currentViewUp[0] * Math.cos(ninetyDegreesRadians) -
  352. currentViewUp[1] * Math.sin(ninetyDegreesRadians),
  353. currentViewUp[0] * Math.sin(ninetyDegreesRadians) +
  354. currentViewUp[1] * Math.cos(ninetyDegreesRadians),
  355. 0,
  356. ];
  357. // 设置新的相机参数
  358. viewport.setCamera({
  359. ...camera,
  360. viewUp: newViewUp,
  361. rotation: newRotation % 360, // 确保角度在0-359范围内
  362. });
  363. viewport.render();
  364. console.log('Rotating Image Clockwise 90°');
  365. }
  366. function ResetImage(): void {
  367. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  368. .viewport as cornerstone.StackViewport;
  369. // Implement the logic to reset the image
  370. // Resets the viewport's camera
  371. viewport.resetCamera();
  372. // Resets the viewport's properties
  373. viewport.resetProperties();
  374. viewport.render();
  375. console.log('Resetting Image');
  376. }
  377. function InvertImage(): void {
  378. const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
  379. .viewport as cornerstone.StackViewport;
  380. // Implement the logic to invert the image
  381. const invert = !viewport.getProperties().invert;
  382. viewport.setProperties({ invert });
  383. viewport.render();
  384. console.log('Inverting Image');
  385. }
  386. function setOriginalSize(viewport) {
  387. // 1) 先正常 fit(或本来就是 fit 状态)
  388. viewport.resetCamera();
  389. // 2) 计算“fit → 1:1”的放大倍数
  390. const { dimensions, spacing } = viewport.getImageData();
  391. console.log(`dimensions:${dimensions}, spacing:${spacing}`);
  392. const canvas = viewport.canvas;
  393. // 水平方向 1:1 需要的倍率
  394. const cssPixelsPerDicomPx = canvas.clientWidth / (dimensions[0] * spacing[0]);
  395. // 垂直方向 1:1 需要的倍率
  396. const cssPixelsPerDicomPy =
  397. canvas.clientHeight / (dimensions[1] * spacing[1]);
  398. // 取两者最小值,保证整张图不会被裁剪
  399. const zoomFactor = Math.min(cssPixelsPerDicomPx, cssPixelsPerDicomPy);
  400. console.log(`zoomFactor:${zoomFactor}`);
  401. console.log(
  402. `canvas.clientWidth:${canvas.clientWidth}, dimensions[0]:${dimensions[0]}, spacing[0]:${spacing[0]}`
  403. );
  404. console.log(
  405. `canvas.clientHeight:${canvas.clientHeight}, dimensions[1]:${dimensions[1]}, spacing[1]:${spacing[1]}`
  406. );
  407. // 3) 直接放大
  408. const zoom = viewport.getZoom();
  409. viewport.setZoom((zoom * 1) / zoomFactor);
  410. viewport.render();
  411. }
  412. const StackViewer = ({
  413. imageIndex = 0,
  414. imageUrls = [],
  415. }: {
  416. imageIndex?: number;
  417. imageUrls?: string[];
  418. }) => {
  419. const elementRef = useRef<HTMLDivElement>(null);
  420. const action = useSelector((state: RootState) => state.functionArea.action);
  421. const dispatch = useDispatch();
  422. useEffect(() => {
  423. const setup = async () => {
  424. // 初始化 Cornerstone
  425. cornerstone.init();
  426. cornerstoneTools.init();
  427. const state = store.getState();
  428. console.log(`当前系统模式:${state.systemMode.mode}`);
  429. const token =
  430. state.systemMode.mode === SystemMode.Emergency
  431. ? state.product.guest
  432. : state.userInfo.token;
  433. console.log(`token stack.image.viewer: ${token}`);
  434. cornerstoneDICOMImageLoader.init({
  435. maxWebWorkers: navigator.hardwareConcurrency || 1,
  436. errorInterceptor: (error) => {
  437. if (error.status === 401) {
  438. console.error('Authentication failed. Please refresh the token.');
  439. }
  440. console.error(`请求dcm文件出错:${error}`);
  441. },
  442. beforeSend: (xhr, imageId, defaultHeaders) => {
  443. return {
  444. ...defaultHeaders,
  445. Authorization: `Bearer ${token}`,
  446. Language: 'en',
  447. Product: 'DROS',
  448. Source: 'Electron',
  449. };
  450. },
  451. });
  452. // Instantiate a rendering engine
  453. const renderingEngineId = 'myRenderingEngine';
  454. const renderingEngine = new cornerstone.RenderingEngine(
  455. renderingEngineId
  456. );
  457. const viewportId = 'CT_AXIAL_STACK';
  458. currentViewportId = viewportId;
  459. const viewportInput: cornerstone.Types.PublicViewportInput = {
  460. viewportId,
  461. element: elementRef.current!,
  462. type: cornerstone.Enums.ViewportType.STACK,
  463. };
  464. renderingEngine.enableElement(viewportInput);
  465. registerTools(viewportId, renderingEngineId);
  466. // Get the stack viewport that was created
  467. const viewport = renderingEngine.getViewport(
  468. viewportId
  469. ) as cornerstone.Types.IStackViewport;
  470. // 给定一个dcm文件路径,加载并显示出来
  471. try {
  472. await viewport.setStack(imageUrls, imageIndex);
  473. } catch (error) {
  474. if (error instanceof Error) {
  475. console.error(
  476. '[stack.image.viewer] Error setting image stack:',
  477. error.message
  478. );
  479. } else {
  480. console.error('[stack.image.viewer] Unknown error:', error);
  481. }
  482. }
  483. viewport.render();
  484. };
  485. setup();
  486. }, [elementRef, imageIndex]);
  487. useEffect(() => {
  488. if (action) {
  489. // Handle the action
  490. switch (action) {
  491. case 'Add L Mark':
  492. addLMark();
  493. break;
  494. case 'Add R Mark':
  495. // Implement the logic to add an R mark
  496. addRLabel(currentViewportId);
  497. console.log('Adding R Mark');
  498. break;
  499. case 'Delete Selected Mark': {
  500. deleteSelectedMark();
  501. break;
  502. }
  503. case 'Horizontal Flip': {
  504. HorizontalFlip();
  505. break;
  506. }
  507. case 'Vertical Flip': {
  508. VerticalFlip();
  509. break;
  510. }
  511. case 'Rotate Counterclockwise 90': {
  512. RotateCounterclockwise90();
  513. break;
  514. }
  515. case 'Rotate Clockwise 90':
  516. RotateClockwise90();
  517. break;
  518. case 'Rotate Any Angle':
  519. // Implement the logic to rotate the image by any angle
  520. {
  521. const planar = toolGroup.getToolInstance(PlanarRotateTool.toolName); // Reset rotation angle
  522. const isActive = planar.mode === csToolsEnums.ToolModes.Active;
  523. console.log(
  524. `PlanarRotateTool is currently ${isActive ? 'active' : 'inactive'}`
  525. );
  526. if (isActive) {
  527. toolGroup.setToolPassive(PlanarRotateTool.toolName, {
  528. removeAllBindings: true,
  529. });
  530. } else {
  531. toolGroup.setToolActive(PlanarRotateTool.toolName, {
  532. bindings: [
  533. {
  534. mouseButton: MouseBindings.Primary, // Left Click
  535. },
  536. ],
  537. });
  538. }
  539. console.log('Rotating Image by Any Angle');
  540. }
  541. break;
  542. case 'Crop Image':
  543. addMask();
  544. break;
  545. case 'Delete Digital Mask':
  546. // Implement the logic to delete the digital mask
  547. console.log('Deleting Digital Mask');
  548. break;
  549. case 'Adjust Brightness and Contrast':
  550. adjustBrightnessAndContrast();
  551. break;
  552. case 'Crop Selected Area':
  553. // Implement the logic to crop the selected area
  554. console.log('Cropping Selected Area');
  555. break;
  556. case 'Delete Mask':
  557. // Implement the logic to delete the mask
  558. console.log('Deleting Mask');
  559. break;
  560. case 'Image Comparison':
  561. // Implement the logic for image comparison
  562. console.log('Comparing Images');
  563. break;
  564. case 'Invert Contrast':
  565. // Implement the logic to invert the contrast
  566. {
  567. const viewport =
  568. cornerstone.getEnabledElementByViewportId(
  569. currentViewportId
  570. ).viewport;
  571. const targetBool = !viewport.getProperties().invert;
  572. viewport.setProperties({
  573. invert: targetBool,
  574. });
  575. viewport.render();
  576. }
  577. console.log('Inverting Contrast');
  578. break;
  579. case '1x1 Layout':
  580. // Implement the logic for 1x1 layout
  581. console.log('Setting 1x1 Layout');
  582. break;
  583. case '1x2 Layout':
  584. // Implement the logic for 1x2 layout
  585. console.log('Setting 1x2 Layout');
  586. break;
  587. case '2x2 Layout':
  588. // Implement the logic for 2x2 layout
  589. console.log('Setting 2x2 Layout');
  590. break;
  591. case '4x4 Layout':
  592. // Implement the logic for 4x4 layout
  593. console.log('Setting 4x4 Layout');
  594. break;
  595. case 'Magnifier': {
  596. // Implement the logic for magnifier
  597. console.log('Activating Magnifier');
  598. const isActive =
  599. toolGroup.getActivePrimaryMouseButtonTool() ===
  600. MagnifyTool.toolName;
  601. if (isActive) {
  602. toolGroup.setToolPassive(MagnifyTool.toolName, {
  603. removeAllBindings: true,
  604. });
  605. } else {
  606. toolGroup.setToolActive(MagnifyTool.toolName, {
  607. bindings: [
  608. {
  609. mouseButton: MouseBindings.Primary, // Left Click
  610. },
  611. ],
  612. });
  613. }
  614. break;
  615. }
  616. case 'Fit Size': {
  617. // Implement the logic to fit the image size
  618. fitImageSize();
  619. console.log('Fitting Image Size');
  620. break;
  621. }
  622. case 'Original Size': {
  623. // Implement the logic to set the image to original size
  624. console.log('Setting Image to Original Size');
  625. setOriginalSize(
  626. cornerstone.getEnabledElementByViewportId(currentViewportId)
  627. .viewport as cornerstone.StackViewport
  628. );
  629. break;
  630. }
  631. case 'Zoom Image':
  632. // Implement the logic to zoom the image
  633. console.log('Zooming Image');
  634. break;
  635. case 'Reset Cursor':
  636. // Implement the logic to reset the cursor
  637. console.log('Resetting Cursor');
  638. break;
  639. case 'Pan':
  640. // Implement the logic to pan the image
  641. console.log('Panning Image');
  642. break;
  643. case 'Invert Image':
  644. InvertImage();
  645. break;
  646. case 'Reset Image':
  647. ResetImage();
  648. break;
  649. case 'Snapshot':
  650. // Implement the logic to take a snapshot
  651. console.log('Taking Snapshot');
  652. break;
  653. case 'Advanced Processing':
  654. // Implement the logic for advanced processing
  655. console.log('Performing Advanced Processing');
  656. break;
  657. case 'Musician':
  658. // Implement the logic for musician
  659. console.log('Activating Musician');
  660. break;
  661. case 'Image Measurement':
  662. // Implement the logic for image measurement
  663. console.log('Measuring Image');
  664. break;
  665. case 'More':
  666. // Implement the logic for more options
  667. console.log('Showing More Options');
  668. break;
  669. case 'Apply Colormap':
  670. ApplyColormap();
  671. break;
  672. default:
  673. break;
  674. }
  675. dispatch(clearAction()); //清理后可连续同一个action触发响应
  676. }
  677. }, [action]);
  678. return (
  679. <div
  680. ref={elementRef}
  681. style={{ width: '100%', height: '100%', backgroundColor: '#000' }}
  682. />
  683. );
  684. };
  685. export default StackViewer;