123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- import React, { useEffect, useRef } from 'react';
- import * as cornerstone from '@cornerstonejs/core';
- import type { Types } from '@cornerstonejs/core';
- import * as cornerstoneTools from '@cornerstonejs/tools';
- import * as cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
- import { useSelector } from 'react-redux';
- import { RootState } from '@/states/store';
- import { SystemMode } from '@/states/systemModeSlice';
- import store from '@/states/store';
- import { clearAction } from '@/states/view/functionAreaSlice';
- import { useDispatch } from 'react-redux';
- const {
- PanTool,
- WindowLevelTool,
- StackScrollTool,
- ZoomTool,
- LabelTool,
- ToolGroupManager,
- Enums: csToolsEnums,
- PlanarRotateTool,
- } = cornerstoneTools;
- const { MouseBindings } = csToolsEnums;
- let toolGroup: cornerstoneTools.Types.IToolGroup;
- let currentViewportId: string;
- function registerTools(viewportId, renderingEngineId) {
- // Add tools to Cornerstone3D
- cornerstoneTools.addTool(PanTool);
- cornerstoneTools.addTool(WindowLevelTool);
- cornerstoneTools.addTool(StackScrollTool);
- cornerstoneTools.addTool(ZoomTool);
- cornerstoneTools.addTool(LabelTool);
- cornerstoneTools.addTool(PlanarRotateTool);
- // Define a tool group
- const toolGroupId = 'STACK_TOOL_GROUP_ID';
- const toolGroupTmp = ToolGroupManager.createToolGroup(toolGroupId);
- if (!toolGroupTmp) {
- return;
- }
- toolGroup = toolGroupTmp;
- // Add tools to the tool group
- toolGroup.addTool(PanTool.toolName);
- toolGroup.addTool(WindowLevelTool.toolName);
- toolGroup.addTool(StackScrollTool.toolName);
- toolGroup.addTool(ZoomTool.toolName);
- toolGroup.addTool(LabelTool.toolName);
- toolGroup.addTool(PlanarRotateTool.toolName);
- // Set the LabelTool as active
- // toolGroup.setToolActive(LabelTool.toolName, {
- // bindings: [
- // {
- // mouseButton: MouseBindings.Primary, // Left Click
- // },
- // ],
- // });
- // Set the initial state of the tools
- // toolGroup.setToolActive(WindowLevelTool.toolName, {
- // bindings: [
- // {
- // mouseButton: MouseBindings.Primary, // Left Click
- // },
- // ],
- // });
- toolGroup.setToolActive(PanTool.toolName, {
- bindings: [
- {
- mouseButton: MouseBindings.Auxiliary, // Middle Click
- },
- ],
- });
- toolGroup.setToolActive(ZoomTool.toolName, {
- bindings: [
- {
- mouseButton: MouseBindings.Secondary, // Right Click
- },
- ],
- });
- toolGroup.setToolActive(StackScrollTool.toolName, {
- bindings: [
- {
- mouseButton: MouseBindings.Wheel, // Mouse Wheel
- },
- ],
- });
- toolGroup.addViewport(viewportId, renderingEngineId);
- }
- function addLMark(): void {
- // Implement the logic to add an L mark
- console.log('Adding L Mark');
- toolGroup.setToolActive(LabelTool.toolName, {
- bindings: [
- // {
- // mouseButton: MouseBindings.Primary, // Left Click
- // },
- ],
- });
- const position: Types.Point3 = [100, 100, 0]; // Example position
- const text = 'L'; // Predefined text
- LabelTool.hydrate(currentViewportId, position, text);
- toolGroup.setToolPassive(LabelTool.toolName, {
- removeAllBindings: true,
- });
- // const enabledElement = cornerstone.getEnabledElementByViewportId(currentViewportId);
- // cursors.elementCursor.resetElementCursor(elementRef.current as HTMLDivElement);
- }
- function addRLabel(viewportId) {
- toolGroup.setToolActive(LabelTool.toolName, {
- bindings: [],
- });
- const element = document.getElementById(viewportId);
- const elementHeight = element ? element.getBoundingClientRect().height : 0;
- const position: Types.Point3 = [100, elementHeight / 2, 0]; // Example position
- const text = 'R'; // Predefined text
- LabelTool.hydrate(viewportId, position, text);
- toolGroup.setToolPassive(LabelTool.toolName, { removeAllBindings: true });
- }
- function deleteSelectedMark(): void {
- const viewport =
- cornerstone.getEnabledElementByViewportId(currentViewportId).viewport;
- const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
- for (const annotation of allAnnotations) {
- if (annotation.data.text === 'L' || annotation.data.text === 'R') {
- cornerstoneTools.annotation.state.removeAnnotation(
- annotation.annotationUID!
- );
- }
- }
- viewport.render();
- }
- function HorizontalFlip(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // 切换水平翻转状态
- const { flipHorizontal } = viewport.getCamera();
- viewport.setCamera({ flipHorizontal: !flipHorizontal });
- console.log('Flipping Image Horizontally');
- }
- function VerticalFlip(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // 切换竖直翻转状态
- const { flipVertical } = viewport.getCamera();
- viewport.setCamera({ flipVertical: !flipVertical });
- }
- function ApplyColormap(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // Implement the logic to apply colormap
- viewport.setProperties({ colormap: { name: 'hsv' } });
- viewport.render();
- console.log('Applying Colormap');
- }
- function RotateCounterclockwise90(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // let { rotation } = viewport.getViewPresentation();
- // const currentRotataion = viewport.getRotation();
- // viewport.rotation(22)
- // console.log(`rotation:${rotation}`)
- // if(!!rotation){
- // rotation=0;
- // }
- // viewport.setViewPresentation({ rotation: rotation ?? 0 + 30 });
- // // // Implement the logic to rotate the image counterclockwise
- // // viewport.setCamera({ rotation: -90 });
- // viewport.render();
- // // 1. 获取当前的视图表现状态(一个完整的对象)
- // const currentViewPresentation = viewport.getViewPresentation();
- // // 2. 创建一个新的对象,基于当前状态,只更新你需要修改的属性
- // // 使用展开运算符 (...) 来复制所有现有属性
- // const newViewPresentation = {
- // ...currentViewPresentation, // 保留所有原有属性,如 scale, translation, flip 等
- // rotation: currentViewPresentation.rotation ?? + 30, // 只覆盖 rotation 属性
- // };
- // // 3. 将完整的新状态对象设置回 viewport
- // viewport.setViewPresentation(newViewPresentation);
- // viewport.render();
- //----------------------------------------
- // // 获取当前的完整视图状态
- // const currentViewState = viewport.getViewPresentation();
- // // 创建新的视图状态对象,保持所有其他属性不变
- // const newViewState = {
- // ...currentViewState, // 保留所有现有属性
- // rotation: (currentViewState.rotation ?? 0 + 30) % 360 // 只修改rotation
- // };
- // // 设置新的视图状态
- // viewport.setViewPresentation(newViewState);
- // viewport.render();
- //-----------------------------------------
- // 获取当前相机
- const camera = viewport.getCamera();
- // // 计算新的旋转角度(当前角度 + 90度)
- // const newRotation = (camera.rotation ?? 0) + 90;
- // // 转换为弧度
- // const radians = newRotation * Math.PI / 180;
- // // 计算新的viewUp向量(顺时针旋转90度)
- // const newViewUp:[number, number, number] = [
- // Math.cos(radians), // X分量
- // Math.sin(radians), // Y分量
- // 0 // Z分量
- // ];
- // // 设置新的相机参数
- // viewport.setCamera({
- // ...camera, // 保持其他相机参数不变
- // viewUp: newViewUp, // 更新向上向量
- // rotation: newRotation // 更新旋转角度(可选,但推荐)
- // });
- // 计算新的旋转角度(当前角度 + 90度)
- const newRotation = (camera.rotation ?? 0) + 90;
- // 但计算viewUp向量时,我们应该使用90度的弧度,而不是新角度的弧度!
- const ninetyDegreesRadians = (90 * Math.PI) / 180;
- // 获取当前的viewUp向量
- const currentViewUp = camera.viewUp || [0, 1, 0];
- // 计算旋转后的viewUp向量(这才是正确的相对旋转)
- const newViewUp: [number, number, number] = [
- currentViewUp[0] * Math.cos(ninetyDegreesRadians) -
- currentViewUp[1] * Math.sin(ninetyDegreesRadians),
- currentViewUp[0] * Math.sin(ninetyDegreesRadians) +
- currentViewUp[1] * Math.cos(ninetyDegreesRadians),
- 0,
- ];
- // 设置新的相机参数
- viewport.setCamera({
- ...camera,
- viewUp: newViewUp,
- rotation: newRotation % 360, // 确保角度在0-359范围内
- });
- viewport.render();
- console.log('Rotating Image Counterclockwise 90°');
- }
- function RotateClockwise90(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- const camera = viewport.getCamera();
- // 计算新的旋转角度(当前角度 + 90度)
- const newRotation = (camera.rotation ?? 0) - 90;
- // 但计算viewUp向量时,我们应该使用90度的弧度,而不是新角度的弧度!
- const ninetyDegreesRadians = (90 * Math.PI) / 180;
- // 获取当前的viewUp向量
- const currentViewUp = camera.viewUp || [0, 1, 0];
- // 计算旋转后的viewUp向量(这才是正确的相对旋转)
- const newViewUp: [number, number, number] = [
- currentViewUp[0] * Math.cos(ninetyDegreesRadians) -
- currentViewUp[1] * Math.sin(ninetyDegreesRadians),
- currentViewUp[0] * Math.sin(ninetyDegreesRadians) +
- currentViewUp[1] * Math.cos(ninetyDegreesRadians),
- 0,
- ];
- // 设置新的相机参数
- viewport.setCamera({
- ...camera,
- viewUp: newViewUp,
- rotation: newRotation % 360, // 确保角度在0-359范围内
- });
- viewport.render();
- console.log('Rotating Image Clockwise 90°');
- }
- function ResetImage(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // Implement the logic to reset the image
- // Resets the viewport's camera
- viewport.resetCamera();
- // Resets the viewport's properties
- viewport.resetProperties();
- viewport.render();
- console.log('Resetting Image');
- }
- function InvertImage(): void {
- const viewport = cornerstone.getEnabledElementByViewportId(currentViewportId)
- .viewport as cornerstone.StackViewport;
- // Implement the logic to invert the image
- const invert = !viewport.getProperties().invert;
- viewport.setProperties({ invert });
- viewport.render();
- console.log('Inverting Image');
- }
- const StackViewer = ({
- imageIndex = 0,
- imageUrls = [],
- }: {
- imageIndex?: number;
- imageUrls?: string[];
- }) => {
- const elementRef = useRef<HTMLDivElement>(null);
- const action = useSelector((state: RootState) => state.functionArea.action);
- const dispatch = useDispatch();
- useEffect(() => {
- const setup = async () => {
- // 初始化 Cornerstone
- cornerstone.init();
- cornerstoneTools.init();
- const state = store.getState();
- console.log(`当前系统模式:${state.systemMode.mode}`);
- const token =
- state.systemMode.mode === SystemMode.Emergency
- ? state.product.guest
- : state.userInfo.token;
- console.log(`token stack.image.viewer: ${token}`);
- cornerstoneDICOMImageLoader.init({
- maxWebWorkers: navigator.hardwareConcurrency || 1,
- errorInterceptor: (error) => {
- if (error.status === 401) {
- console.error('Authentication failed. Please refresh the token.');
- }
- console.error(`请求dcm文件出错:${error}`);
- },
- beforeSend: (xhr, imageId, defaultHeaders) => {
- return {
- ...defaultHeaders,
- Authorization: `Bearer ${token}`,
- Language: 'en',
- Product: 'DROS',
- Source: 'Electron',
- };
- },
- });
- // Instantiate a rendering engine
- const renderingEngineId = 'myRenderingEngine';
- const renderingEngine = new cornerstone.RenderingEngine(
- renderingEngineId
- );
- const viewportId = 'CT_AXIAL_STACK';
- currentViewportId = viewportId;
- const viewportInput: cornerstone.Types.PublicViewportInput = {
- viewportId,
- element: elementRef.current!,
- type: cornerstone.Enums.ViewportType.STACK,
- };
- renderingEngine.enableElement(viewportInput);
- registerTools(viewportId, renderingEngineId);
- // Get the stack viewport that was created
- const viewport = renderingEngine.getViewport(
- viewportId
- ) as cornerstone.Types.IStackViewport;
- // 给定一个dcm文件路径,加载并显示出来
- await viewport.setStack(imageUrls, imageIndex);
- viewport.render();
- };
- setup();
- }, [elementRef, imageIndex]);
- useEffect(() => {
- if (action) {
- // Handle the action
- switch (action) {
- case 'Add L Mark':
- addLMark();
- break;
- case 'Add R Mark':
- // Implement the logic to add an R mark
- addRLabel(currentViewportId);
- console.log('Adding R Mark');
- break;
- case 'Delete Selected Mark': {
- deleteSelectedMark();
- break;
- }
- case 'Horizontal Flip': {
- HorizontalFlip();
- break;
- }
- case 'Vertical Flip': {
- VerticalFlip();
- break;
- }
- case 'Rotate Counterclockwise 90': {
- RotateCounterclockwise90();
- break;
- }
- case 'Rotate Clockwise 90':
- RotateClockwise90();
- break;
- case 'Rotate Any Angle':
- // Implement the logic to rotate the image by any angle
- {
- const planar = toolGroup.getToolInstance(PlanarRotateTool.toolName); // Reset rotation angle
- const isActive = planar.mode === csToolsEnums.ToolModes.Active;
- console.log(
- `PlanarRotateTool is currently ${isActive ? 'active' : 'inactive'}`
- );
- if (isActive) {
- toolGroup.setToolPassive(PlanarRotateTool.toolName, {
- removeAllBindings: true,
- });
- } else {
- toolGroup.setToolActive(PlanarRotateTool.toolName, {
- bindings: [
- {
- mouseButton: MouseBindings.Primary, // Left Click
- },
- ],
- });
- }
- console.log('Rotating Image by Any Angle');
- }
- break;
- case 'Crop Image':
- // Implement the logic to crop the image
- console.log('Cropping Image');
- break;
- case 'Delete Digital Mask':
- // Implement the logic to delete the digital mask
- console.log('Deleting Digital Mask');
- break;
- case 'Adjust Brightness and Contrast':
- // Implement the logic to adjust brightness and contrast
- console.log('Adjusting Brightness and Contrast');
- break;
- case 'Crop Selected Area':
- // Implement the logic to crop the selected area
- console.log('Cropping Selected Area');
- break;
- case 'Delete Mask':
- // Implement the logic to delete the mask
- console.log('Deleting Mask');
- break;
- case 'Image Comparison':
- // Implement the logic for image comparison
- console.log('Comparing Images');
- break;
- case 'Invert Contrast':
- // Implement the logic to invert the contrast
- {
- const viewport =
- cornerstone.getEnabledElementByViewportId(
- currentViewportId
- ).viewport;
- const targetBool = !viewport.getProperties().invert;
- viewport.setProperties({
- invert: targetBool,
- });
- viewport.render();
- }
- console.log('Inverting Contrast');
- break;
- case '1x1 Layout':
- // Implement the logic for 1x1 layout
- console.log('Setting 1x1 Layout');
- break;
- case '1x2 Layout':
- // Implement the logic for 1x2 layout
- console.log('Setting 1x2 Layout');
- break;
- case '2x2 Layout':
- // Implement the logic for 2x2 layout
- console.log('Setting 2x2 Layout');
- break;
- case '4x4 Layout':
- // Implement the logic for 4x4 layout
- console.log('Setting 4x4 Layout');
- break;
- case 'Magnifier':
- // Implement the logic for magnifier
- console.log('Activating Magnifier');
- break;
- case 'Fit Size':
- // Implement the logic to fit the image size
- console.log('Fitting Image Size');
- break;
- case 'Original Size':
- // Implement the logic to set the image to original size
- console.log('Setting Image to Original Size');
- break;
- case 'Zoom Image':
- // Implement the logic to zoom the image
- console.log('Zooming Image');
- break;
- case 'Reset Cursor':
- // Implement the logic to reset the cursor
- console.log('Resetting Cursor');
- break;
- case 'Pan':
- // Implement the logic to pan the image
- console.log('Panning Image');
- break;
- case 'Invert Image':
- InvertImage();
- break;
- case 'Reset Image':
- ResetImage();
- break;
- case 'Snapshot':
- // Implement the logic to take a snapshot
- console.log('Taking Snapshot');
- break;
- case 'Advanced Processing':
- // Implement the logic for advanced processing
- console.log('Performing Advanced Processing');
- break;
- case 'Musician':
- // Implement the logic for musician
- console.log('Activating Musician');
- break;
- case 'Image Measurement':
- // Implement the logic for image measurement
- console.log('Measuring Image');
- break;
- case 'More':
- // Implement the logic for more options
- console.log('Showing More Options');
- break;
- case 'Apply Colormap':
- ApplyColormap();
- break;
- default:
- break;
- }
- dispatch(clearAction()); //清理后可连续同一个action触发响应
- }
- }, [action]);
- return (
- <div
- ref={elementRef}
- style={{ width: '100%', height: '100%', backgroundColor: '#000' }}
- />
- );
- };
- export default StackViewer;
|