1846 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			1846 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			Plaintext
		
	
	
| <template>
 | ||
|   <div v-loading="loading" class="dicom-viewer-container">
 | ||
|     <!-- 工具条 -->
 | ||
|     <div class="dicom-tools">
 | ||
|       <!-- 窗宽窗位 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:wwwc')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             :class="[activeTool==='WindowLevel'?'tool_active':'']"
 | ||
|             data-tool="Zoom"
 | ||
|           >
 | ||
|             <svg-icon icon-class="reverse" class="svg-icon" @click.prevent="setBasicToolActive('WindowLevel')" />
 | ||
|           </div>
 | ||
|           <!-- 调窗 -->
 | ||
|           <div class="text">{{ $t('trials:reading:button:wwwc') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 反色 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:reverseColor')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             data-tool="reverse"
 | ||
|           >
 | ||
|             <svg-icon icon-class="reversecolor" class="svg-icon" @click.prevent="toggleInvert" />
 | ||
|           </div>
 | ||
|           <!-- 调窗 -->
 | ||
|           <div class="text">{{ $t('trials:reading:button:reverseColor') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 缩放 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:zoom')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             :class="[activeTool==='Zoom'?'tool_active':'']"
 | ||
|             data-tool="Zoom"
 | ||
|           >
 | ||
|             <svg-icon icon-class="magnifier" class="svg-icon" @click.prevent="setBasicToolActive('Zoom')" />
 | ||
|           </div>
 | ||
|           <!-- 缩放 -->
 | ||
|           <div class="text">{{ $t('trials:reading:button:zoom') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 移动 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:move')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             :class="[activeTool==='Pan'?'tool_active':'']"
 | ||
|             data-tool="Pan"
 | ||
|           >
 | ||
|             <svg-icon icon-class="move" class="svg-icon" @click.prevent="setBasicToolActive('Pan')" />
 | ||
|           </div>
 | ||
|           <!-- 移动 -->
 | ||
|           <div class="text">{{ $t('trials:reading:button:move') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 旋转 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:rotate')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper" @click.stop="showPanel($event)" @mouseleave="handleMouseout">
 | ||
|           <div class="dropdown">
 | ||
|             <div
 | ||
|               class="icon"
 | ||
|               :class="[activeTool==='Rotate'?'tool_active':'']"
 | ||
|               data-tool="Pan"
 | ||
|             >
 | ||
|               <svg-icon icon-class="rotate" class="svg-icon" @click.prevent="setBasicToolActive('TrackballRotate')" />
 | ||
|               <i class="el-icon-arrow-down" style="color:#fff;" />
 | ||
|             </div>
 | ||
|             <!-- 移动 -->
 | ||
|             <div class="text">{{ $t('trials:reading:button:rotate') }}</div>
 | ||
|             <div class="dropdown-content">
 | ||
|               <ul style="width:100px;">
 | ||
|                 <li v-for="rotate in rotateArr" :key="rotate.label" style="text-align:left;">
 | ||
|                   <a href="#" @click.prevent="setDicomCanvasRotate(rotate.val)">{{ rotate.label }}</a>
 | ||
|                 </li>
 | ||
|               </ul>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 椭圆oval -->
 | ||
|       <template v-for="tool in measuredTools">
 | ||
|         <el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark" :content="tool.text" placement="bottom">
 | ||
|           <div class="tool-wrapper">
 | ||
|             <div
 | ||
|               class="icon"
 | ||
|               :class="[activeTool===tool.toolName?'tool_active':'']"
 | ||
|             >
 | ||
|               <svg-icon :icon-class="tool.icon" class="svg-icon" @click.prevent="setMeasureToolActive(tool.toolName)" />
 | ||
|             </div>
 | ||
|             <div class="text">{{ tool.text }}</div>
 | ||
|           </div>
 | ||
|         </el-tooltip>
 | ||
|       </template>
 | ||
| 
 | ||
|       <!-- <el-tooltip v-if="isCurrentTask" class="item" effect="dark" content="圆形测量" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             :class="[activeTool==='Probe'?'tool_active':'']"
 | ||
|             data-tool="Probe"
 | ||
|           >
 | ||
|             <svg-icon icon-class="oval" class="svg-icon" @click.prevent="setBasicToolActive('Probe')" />
 | ||
|           </div>
 | ||
|           <div class="text">探针</div>
 | ||
|         </div>
 | ||
|       </el-tooltip> -->
 | ||
|       <!-- 重置 -->
 | ||
|       <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:reset')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             @click.prevent="resetViewport"
 | ||
|           >
 | ||
|             <svg-icon icon-class="refresh" class="svg-icon" />
 | ||
|           </div>
 | ||
|           <div class="text">{{ $t('trials:reading:button:reset') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <el-tooltip class="item" effect="dark" content="伪彩" placement="bottom">
 | ||
|         <div class="tool-wrapper" @click.stop="showPanel($event)" @mouseleave="handleMouseout">
 | ||
|           <!-- <canvas id="colorBarCanvas" /> -->
 | ||
|           <div class="dropdown">
 | ||
|             <div
 | ||
|               class="icon"
 | ||
|               style="display: flex;align-items: center;"
 | ||
|             >
 | ||
|               <!-- <svg-icon icon-class="rotate" class="svg-icon" @click.prevent="setBasicToolActive('TrackballRotate')" />
 | ||
|               <i class="el-icon-arrow-down" style="color:#fff;" /> -->
 | ||
|               <canvas id="colorBarCanvas" />
 | ||
|               <div style="color:#ddd;margin-left:5px;font-size: 10px;width:75px">{{ rgbPresetName }}</div>
 | ||
|             </div>
 | ||
|             <!-- 移动 -->
 | ||
|             <div class="text">伪彩</div>
 | ||
|             <div class="dropdown-content">
 | ||
|               <ul>
 | ||
|                 <li v-for="(colorMap,index) in colorMaps" :key="colorMap" style="display: flex;align-items: center;margin-bottom:5px;padding:0 5px;" @click="setColorMap(colorMap)">
 | ||
|                   <canvas :id="`colorBarCanvas${index}`" />
 | ||
|                   <span style="margin-left:5px;font-size: 10px;">{{ colorMap }}</span>
 | ||
|                 </li>
 | ||
|               </ul>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|       </el-tooltip>
 | ||
|       <!-- 截屏 -->
 | ||
|       <!-- <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:screenShot')}`" placement="bottom">
 | ||
|         <div class="tool-wrapper">
 | ||
|           <div
 | ||
|             class="icon"
 | ||
|             @click.prevent="saveImage"
 | ||
|           >
 | ||
|             <svg-icon icon-class="image" class="svg-icon" />
 | ||
|           </div>
 | ||
|           <div class="text">{{ $t('trials:reading:button:screenShot') }}</div>
 | ||
|         </div>
 | ||
|       </el-tooltip> -->
 | ||
|       <!-- 椭圆oval -->
 | ||
| 
 | ||
|     </div>
 | ||
|     <div class="dicom-datas">
 | ||
|       <!-- 影像 -->
 | ||
|       <div class="dicom-container box box_2_2">
 | ||
|         <Viewport
 | ||
|           ref="CT_AXIAL"
 | ||
|           :index="1"
 | ||
|           :active-index="activeIndex"
 | ||
|           :is-reading-show-subject-info="isReadingShowSubjectInfo"
 | ||
|           :series-info="ctSeries"
 | ||
|           :rendering-engine-id="renderingEngineId"
 | ||
|           viewport-id="CT_AXIAL"
 | ||
|           :volume="ctVolume"
 | ||
|           :measure-datas="measureDatas"
 | ||
|         />
 | ||
|         <Viewport
 | ||
|           ref="PT_AXIAL"
 | ||
|           :index="2"
 | ||
|           :active-index="activeIndex"
 | ||
|           :is-reading-show-subject-info="isReadingShowSubjectInfo"
 | ||
|           :series-info="petSeries"
 | ||
|           :rendering-engine-id="renderingEngineId"
 | ||
|           viewport-id="PT_AXIAL"
 | ||
|           :volume="ptVolume"
 | ||
|           :measure-datas="measureDatas"
 | ||
|         />
 | ||
|         <Viewport
 | ||
|           ref="FUSION_AXIAL"
 | ||
|           :index="3"
 | ||
|           :active-index="activeIndex"
 | ||
|           :is-reading-show-subject-info="isReadingShowSubjectInfo"
 | ||
|           :series-info="petSeries"
 | ||
|           :rendering-engine-id="renderingEngineId"
 | ||
|           viewport-id="FUSION_AXIAL"
 | ||
|           :volume="ptVolume"
 | ||
|           :measure-datas="measureDatas"
 | ||
|           :rgb-preset-name="rgbPresetName"
 | ||
|         />
 | ||
|         <Viewport
 | ||
|           :index="4"
 | ||
|           :active-index="activeIndex"
 | ||
|           :is-reading-show-subject-info="isReadingShowSubjectInfo"
 | ||
|           :series-info="petSeries"
 | ||
|           :rendering-engine-id="renderingEngineId"
 | ||
|           viewport-id="PET_MIP_CORONAL"
 | ||
|           :measure-datas="measureDatas"
 | ||
|         />
 | ||
|       </div>
 | ||
|       <!-- 表单 -->
 | ||
|       <div class="form-container" style="overflow-y: auto;">
 | ||
|         <div style="color:#fff;padding: 0 5px;">
 | ||
|           <h3 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;">
 | ||
|             <span v-if="subjectCode">{{ subjectCode }} </span>
 | ||
|             <span style="margin-left:5px;">{{ taskBlindName }}</span>
 | ||
|           </h3>
 | ||
| 
 | ||
|           <TableQuestions ref="tableQuestions" />
 | ||
|           <Questions ref="questions" @setNonTargetMeasurementStatus="setNonTargetMeasurementStatus" />
 | ||
|         </div>
 | ||
|       </div>
 | ||
|     </div>
 | ||
|   </div>
 | ||
| </template>
 | ||
| <script>
 | ||
| import {
 | ||
|   RenderingEngine,
 | ||
|   Enums,
 | ||
|   // CONSTANTS,
 | ||
|   setVolumesForViewports,
 | ||
|   volumeLoader,
 | ||
|   getRenderingEngine,
 | ||
|   eventTarget,
 | ||
|   cache,
 | ||
|   imageLoader,
 | ||
|   utilities as csUtils
 | ||
| 
 | ||
| } from '@cornerstonejs/core'
 | ||
| import * as cornerstone3D from '@cornerstonejs/core'
 | ||
| import * as cornerstoneTools from '@cornerstonejs/tools'
 | ||
| import initLibraries from './js/initLibraries'
 | ||
| 
 | ||
| import { createImageIdsAndCacheMetaData } from './js/createImageIdsAndCacheMetaData'
 | ||
| import setCtTransferFunctionForVolumeActor from './js/setCtTransferFunctionForVolumeActor'
 | ||
| import setPetTransferFunctionForVolumeActor from './js/setPetTransferFunctionForVolumeActor'
 | ||
| import { setPetColorMapTransferFunctionForVolumeActor, switchColorPreset } from './js/setPetColorMapTransferFunctionForVolumeActor'
 | ||
| import store from '@/store'
 | ||
| import { mapGetters, mapMutations } from 'vuex'
 | ||
| import { changeURLStatic } from '@/utils/history.js'
 | ||
| import Viewport from './Viewport'
 | ||
| import Questions from './Questions'
 | ||
| import TableQuestions from './TableQuestions'
 | ||
| import { getTableAnswerRowInfoList } from '@/api/trials'
 | ||
| import FusionEvent from './FusionEvent'
 | ||
| // import { ColorMaps } from '@kitware/vtk.js/Common/Core/ColorMaps'
 | ||
| import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
 | ||
| import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
 | ||
| import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction'
 | ||
| // import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
 | ||
| // import vtkMath from '@kitware/vtk.js/Common/Core/Math'
 | ||
| // import CircleROITool from './tools/CircleROITool'
 | ||
| const {
 | ||
|   ToolGroupManager,
 | ||
|   Enums: csToolsEnums,
 | ||
|   WindowLevelTool,
 | ||
|   PanTool,
 | ||
|   ZoomTool,
 | ||
|   StackScrollMouseWheelTool,
 | ||
|   synchronizers,
 | ||
|   MIPJumpToClickTool,
 | ||
|   VolumeRotateMouseWheelTool,
 | ||
|   // SynchronizerManager,
 | ||
|   // LengthTool,
 | ||
|   EllipticalROITool,
 | ||
|   CircleROITool,
 | ||
|   CrosshairsTool,
 | ||
|   TrackballRotateTool,
 | ||
|   ProbeTool,
 | ||
|   ScaleOverlayTool,
 | ||
|   utilities,
 | ||
|   // eslint-disable-next-line no-unused-vars
 | ||
|   annotation,
 | ||
|   // eslint-disable-next-line no-unused-vars
 | ||
|   cursors
 | ||
|   // stateManagement
 | ||
| 
 | ||
| } = cornerstoneTools
 | ||
| 
 | ||
| const { MouseBindings } = csToolsEnums
 | ||
| const { ViewportType, BlendModes } = Enums
 | ||
| const { createCameraPositionSynchronizer, createVOISynchronizer } = synchronizers
 | ||
| let renderingEngine
 | ||
| const renderingEngineId = 'myRenderingEngine'
 | ||
| const volumeLoaderScheme = 'cornerstoneStreamingImageVolume' // Loader id which defines which volume loader to use
 | ||
| const ctVolumeName = 'CT_VOLUME_ID' // Id of the volume less loader prefix
 | ||
| const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}` // VolumeId with loader id + volume id
 | ||
| const ptVolumeName = 'PT_VOLUME_ID'
 | ||
| const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`
 | ||
| const ctToolGroupId = 'CT_TOOLGROUP_ID'
 | ||
| const ptToolGroupId = 'PT_TOOLGROUP_ID'
 | ||
| const fusionToolGroupId = 'FUSION_TOOLGROUP_ID'
 | ||
| const mipToolGroupUID = 'MIP_TOOLGROUP_ID'
 | ||
| var axialCameraPositionSynchronizer
 | ||
| var ctVoiSynchronizer
 | ||
| var ptVoiSynchronizer
 | ||
| const viewportIds = {
 | ||
|   CT: { AXIAL: 'CT_AXIAL', SAGITTAL: 'CT_SAGITTAL', CORONAL: 'CT_CORONAL' },
 | ||
|   PT: { AXIAL: 'PT_AXIAL', SAGITTAL: 'PT_SAGITTAL', CORONAL: 'PT_CORONAL' },
 | ||
|   FUSION: {
 | ||
|     AXIAL: 'FUSION_AXIAL',
 | ||
|     SAGITTAL: 'FUSION_SAGITTAL',
 | ||
|     CORONAL: 'FUSION_CORONAL'
 | ||
|   },
 | ||
|   PETMIP: {
 | ||
|     CORONAL: 'PET_MIP_CORONAL'
 | ||
|   }
 | ||
| }
 | ||
| var element_ct
 | ||
| var element_pet
 | ||
| var element_fusion
 | ||
| var element_mip
 | ||
| var ctToolGroup
 | ||
| var ptToolGroup
 | ||
| var fusionToolGroup
 | ||
| var mipToolGroup
 | ||
| 
 | ||
| const axialCameraSynchronizerId = 'AXIAL_CAMERA_SYNCHRONIZER_ID'
 | ||
| const ctVoiSynchronizerId = 'CT_VOI_SYNCHRONIZER_ID'
 | ||
| const ptVoiSynchronizerId = 'PT_VOI_SYNCHRONIZER_ID'
 | ||
| 
 | ||
| export default {
 | ||
|   name: 'Fusion',
 | ||
|   components: {
 | ||
|     Viewport,
 | ||
|     Questions,
 | ||
|     TableQuestions
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       activeIndex: 0,
 | ||
|       activeTool: '',
 | ||
|       loading: false,
 | ||
|       fitType: 0,
 | ||
|       rotateArr: [
 | ||
|         // { label: this.$t('trials:reading:button:rotateDefault'), val: 1 }, // 默认值
 | ||
|         { label: this.$t('trials:reading:button:rotateVertical'), val: 2 }, // 垂直翻转
 | ||
|         { label: this.$t('trials:reading:button:rotateHorizontal'), val: 3 } // 水平翻转
 | ||
|         // { label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4 }, // 左转90度
 | ||
|         // { label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 }// 右转90度
 | ||
|       ],
 | ||
|       isCurrentTask: false,
 | ||
|       ctSeries: {},
 | ||
|       petSeries: {},
 | ||
|       renderingEngineId: renderingEngineId,
 | ||
|       ctVolumeId: `${volumeLoaderScheme}:${ctVolumeName}`,
 | ||
|       ptVolumeId: `${volumeLoaderScheme}:${ptVolumeName}`,
 | ||
|       ctVolume: null,
 | ||
|       ptVolume: null,
 | ||
|       isReadingShowSubjectInfo: false,
 | ||
|       subjectCode: '',
 | ||
|       taskBlindName: '',
 | ||
|       measuredTools: [
 | ||
|         // 直径测量
 | ||
|         { toolName: 'CircleROI', text: '圆形测量', icon: 'oval', isDisabled: false,
 | ||
|           disabledReason: '' }
 | ||
| 
 | ||
|       ],
 | ||
|       measureDatas: [],
 | ||
|       isInitMeasureDatas: false,
 | ||
|       isNonTargetMeasurement: false,
 | ||
|       readingTaskState: 2,
 | ||
|       isLocate: true,
 | ||
|       colorMaps: ['BLUE-WHITE', 'BkBu', 'BkCy', 'BkMa', 'Blues', 'Cool to Warm', 'GBBr', 'Grayscale', 'Greens', 'Haze', 'Haze_green', 'Oranges', 'Purples', 'Warm to Cool', 'X Ray', 'blue2yellow', 'coolwarm', 'hsv', 'jet', 'rainbow', 'magenta', '2hot'],
 | ||
|       rgbPresetName: 'hsv'
 | ||
|     }
 | ||
|   },
 | ||
|   computed: {
 | ||
|     ...mapGetters(['visitTaskList'])
 | ||
|   },
 | ||
|   watch: {
 | ||
|     // activeTool: {
 | ||
|     //   deep: true,
 | ||
|     //   immediate: false,
 | ||
|     //   handler(v) {
 | ||
|     //     if (!v) {
 | ||
|     //       const elements = [element_ct, element_pet, element_fusion]
 | ||
|     //       elements.map(el => {
 | ||
|     //         console.log(cursors)
 | ||
|     //         // cursors.setCursorForElement(el, 'default')
 | ||
|     //         // cursors.elementCursor.hideElementCursor(el)
 | ||
|     //       })
 | ||
|     //     }
 | ||
|     //   }
 | ||
|     // }
 | ||
| 
 | ||
|   },
 | ||
|   mounted() {
 | ||
|     console.log(vtkColorMaps)
 | ||
|     window.addEventListener('message', this.receiveMsg)
 | ||
|     console.log(cornerstoneTools)
 | ||
|     console.log(cornerstone3D)
 | ||
|     this.$i18n.locale = this.$route.query.lang
 | ||
|     this.setLanguage(this.$route.query.lang)
 | ||
|     this.readingTaskState = parseInt(this.$route.query.readingTaskState)
 | ||
|     this.isReadingShowSubjectInfo = this.$route.query.isReadingShowSubjectInfo === 'true'
 | ||
|     this.subjectCode = this.$route.query.subjectCode
 | ||
|     this.taskBlindName = this.$route.query.taskBlindName
 | ||
|     this.isCurrentTask = this.$route.query.isReadingShowSubjectInfo === 'true'
 | ||
|     const digitPlaces = parseInt(this.$route.query.digitPlaces)
 | ||
|     this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
 | ||
|     element_ct = document.getElementById('viewport1')
 | ||
|     element_pet = document.getElementById('viewport2')
 | ||
|     element_fusion = document.getElementById('viewport3')
 | ||
|     element_mip = document.getElementById('viewport4')
 | ||
|     if (this.$router.currentRoute.query.TokenKey) {
 | ||
|       store.dispatch('user/setToken', this.$route.query.TokenKey)
 | ||
|       changeURLStatic('TokenKey', '')
 | ||
|     }
 | ||
|     this.renderColorMaps()
 | ||
|     this.handleElementsClick()
 | ||
|     this.initPage()
 | ||
| 
 | ||
|     FusionEvent.$on('getAnnotations', () => {
 | ||
|       console.log('getAnnotations')
 | ||
|       this.getAnnotations()
 | ||
|     })
 | ||
|     FusionEvent.$on('addOrUpdateAnnotations', (obj) => {
 | ||
|       console.log('addOrUpdateAnnotations')
 | ||
|       this.addOrUpdateAnnotations(obj)
 | ||
|     })
 | ||
|     FusionEvent.$on('removeAnnotation', (obj) => {
 | ||
|       console.log('removeAnnotation')
 | ||
|       this.removeAnnotation(obj)
 | ||
|     })
 | ||
|     FusionEvent.$on('imageLocation', (obj) => {
 | ||
|       console.log('imageLocation')
 | ||
|       this.imageLocation(obj)
 | ||
|     })
 | ||
|   },
 | ||
|   destroyed() {
 | ||
|     cornerstoneTools.destroy()
 | ||
|     eventTarget.reset()
 | ||
|     cache.purgeCache()
 | ||
|     renderingEngine.destroy()
 | ||
|     imageLoader.unregisterAllImageLoaders()
 | ||
|     ToolGroupManager.destroyToolGroup('volume')
 | ||
|     FusionEvent.$off('getAnnotations')
 | ||
|     FusionEvent.$off('addOrUpdateAnnotations')
 | ||
|     FusionEvent.$off('removeAnnotation')
 | ||
|     FusionEvent.$off('imageLocation')
 | ||
|   },
 | ||
|   methods: {
 | ||
|     renderColorMaps() {
 | ||
|       this.createColorBar(this.rgbPresetName, 'colorBarCanvas')
 | ||
|       this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName)
 | ||
|       this.colorMaps.forEach((e, index) => {
 | ||
|         this.createColorBar(e, `colorBarCanvas${index}`)
 | ||
|       })
 | ||
|     },
 | ||
|     async setColorMap(rgbPresetName) {
 | ||
|       this.rgbPresetName = rgbPresetName
 | ||
|       this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName)
 | ||
|       this.createColorBar(this.rgbPresetName, 'colorBarCanvas')
 | ||
|       var imageIdIndex = this.$refs['FUSION_AXIAL'].seriesInfo.imageIdIndex
 | ||
|       switchColorPreset(this.rgbPresetName)
 | ||
| 
 | ||
|       // Set volumes on the viewports
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ctVolumeId,
 | ||
|             callback: setCtTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.CT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.PT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ctVolumeId,
 | ||
|             callback: setCtTransferFunctionForVolumeActor
 | ||
|           },
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetColorMapTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.FUSION.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       // Calculate size of fullBody pet mip
 | ||
|       const ptVolumeDimensions = this.ptVolume.dimensions
 | ||
| 
 | ||
|       // Only make the MIP as large as it needs to be.
 | ||
|       const slabThickness = Math.sqrt(
 | ||
|         ptVolumeDimensions[0] * ptVolumeDimensions[0] +
 | ||
|       ptVolumeDimensions[1] * ptVolumeDimensions[1] +
 | ||
|       ptVolumeDimensions[2] * ptVolumeDimensions[2]
 | ||
|       )
 | ||
| 
 | ||
|       setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetTransferFunctionForVolumeActor,
 | ||
|             blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
 | ||
|             slabThickness
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.PETMIP.CORONAL]
 | ||
|       )
 | ||
| 
 | ||
|       await this.initializeCameraSync(renderingEngine)
 | ||
| 
 | ||
|       // Render the viewports
 | ||
|       renderingEngine.render()
 | ||
|       this.$refs['FUSION_AXIAL'].renderColorBar(this.rgbPresetName)
 | ||
|       this.$refs['FUSION_AXIAL'].scroll(imageIdIndex)
 | ||
|       // const applicableVolumeActorInfo = this.getApplicableVolumeActor(ptVolumeId, 'FUSION_AXIAL')
 | ||
| 
 | ||
|       // if (!applicableVolumeActorInfo) {
 | ||
|       //   return
 | ||
|       // }
 | ||
|       // const { volumeActor } = applicableVolumeActorInfo
 | ||
| 
 | ||
|       // const mapper = volumeActor.getMapper()
 | ||
|       // mapper.setSampleDistance(1.0)
 | ||
| 
 | ||
|       // const cfun = vtkColorTransferFunction.newInstance()
 | ||
|       // csUtils.colormap.getColormapNames()
 | ||
|       // console.log(csUtils.colormap.getColormapNames())
 | ||
|       // let colormapObj = csUtils.colormap.getColormap(rgbPresetName)
 | ||
| 
 | ||
|       // const { name } = colormap
 | ||
| 
 | ||
|       // if (!colormapObj) {
 | ||
|       //   colormapObj = vtkColorMaps.getPresetByName(name)
 | ||
|       // }
 | ||
|       // colormapObj = vtkColorMaps.getPresetByName(rgbPresetName)
 | ||
| 
 | ||
|       // if (!colormapObj) {
 | ||
|       //   throw new Error(`Colormap ${rgbPresetName} not found`)
 | ||
|       // }
 | ||
| 
 | ||
|       // const range = volumeActor
 | ||
|       //   .getProperty()
 | ||
|       //   .getRGBTransferFunction(0)
 | ||
|       //   .getRange()
 | ||
| 
 | ||
|       // cfun.applyColorMap(colormapObj)
 | ||
|       // cfun.setMappingRange(range[0], range[1])
 | ||
|       // volumeActor.getProperty().setRGBTransferFunction(0, cfun)
 | ||
| 
 | ||
|       // const preset = CONSTANTS.VIEWPORT_PRESETS.find((preset) => {
 | ||
|       //   return preset.name === rgbPresetName
 | ||
|       // })
 | ||
| 
 | ||
|       // if (!preset) {
 | ||
|       //   return
 | ||
|       // }
 | ||
| 
 | ||
|       // csUtils.applyPreset(volumeActor, preset)
 | ||
|     },
 | ||
|     setPetColorMap(volumeInfo) {
 | ||
|       const { volumeActor } = volumeInfo
 | ||
|       const mapper = volumeActor.getMapper()
 | ||
|       mapper.setSampleDistance(1.0)
 | ||
| 
 | ||
|       const cfun = vtkColorTransferFunction.newInstance()
 | ||
|       const presetToUse = this.rgbPresetName
 | ||
|       cfun.applyColorMap(presetToUse)
 | ||
|       cfun.setMappingRange(0, 5)
 | ||
| 
 | ||
|       volumeActor.getProperty().setRGBTransferFunction(0, cfun)
 | ||
| 
 | ||
|       // Create scalar opacity function
 | ||
|       const ofun = vtkPiecewiseFunction.newInstance()
 | ||
|       ofun.addPoint(0, 0.0)
 | ||
|       ofun.addPoint(0.1, 0.9)
 | ||
|       ofun.addPoint(5, 1.0)
 | ||
| 
 | ||
|       volumeActor.getProperty().setScalarOpacity(0, ofun)
 | ||
|     },
 | ||
|     getApplicableVolumeActor(volumeId, viewportId) {
 | ||
|       const viewport = renderingEngine.getViewport(viewportId)
 | ||
|       const actorEntries = viewport.getActors()
 | ||
|       console.log(actorEntries)
 | ||
|       // const actorEntries = this.getActors();
 | ||
| 
 | ||
|       if (!actorEntries.length) {
 | ||
|         return
 | ||
|       }
 | ||
| 
 | ||
|       let volumeActor
 | ||
| 
 | ||
|       if (volumeId) {
 | ||
|         const i = actorEntries.findIndex(e => e.uid === volumeId)
 | ||
|         if (i > -1) {
 | ||
|           volumeActor = actorEntries[i].actor
 | ||
|         }
 | ||
|       // volumeActor = this.getActor(volumeId)?.actor as vtkVolume;
 | ||
|       }
 | ||
| 
 | ||
|       // // set it for the first volume (if there are more than one - fusion)
 | ||
|       if (!volumeActor) {
 | ||
|         volumeActor = actorEntries[0].actor
 | ||
|         volumeId = actorEntries[0].uid
 | ||
|       }
 | ||
| 
 | ||
|       return { volumeActor, volumeId }
 | ||
|     },
 | ||
|     createColorBar(rgbPresetName, elId) {
 | ||
|       // const ctf = vtkColorTransferFunction.newInstance()
 | ||
|       const colorMap = vtkColorMaps.getPresetByName(rgbPresetName)
 | ||
|       const rgbPoints = colorMap.RGBPoints
 | ||
|       // const range = ctf.getRange()
 | ||
|       const canvas = document.getElementById(elId)
 | ||
|       const ctx = canvas.getContext('2d')
 | ||
|       const canvasWidth = 110
 | ||
|       const canvasHeight = 15
 | ||
|       const rectWidth = 100
 | ||
|       const rectHeight = canvasHeight
 | ||
|       canvas.width = canvasWidth
 | ||
|       canvas.height = canvasHeight
 | ||
|       const gradient = ctx.createLinearGradient(0, 0, rectWidth, 0)
 | ||
|       for (let i = 0; i < rgbPoints.length; i += 4) {
 | ||
|         let position = 0
 | ||
|         if (rgbPoints[0] === -1) {
 | ||
|           position = (rgbPoints[i] + 1) / 2
 | ||
|         } else {
 | ||
|           position = rgbPoints[i]
 | ||
|         }
 | ||
| 
 | ||
|         // console.log(position)
 | ||
|         const color = `rgb(${parseInt(rgbPoints[i + 1] * 255)}, ${parseInt(rgbPoints[i + 2] * 255)}, ${parseInt(rgbPoints[i + 3] * 255)})`
 | ||
|         gradient.addColorStop(position, color)
 | ||
|       }
 | ||
|       ctx.fillStyle = gradient
 | ||
|       ctx.fillRect(0, 0, rectWidth, rectHeight)
 | ||
|       // ctx.font = 'bold 18px sans-serif'
 | ||
|       // ctx.textAlign = 'right'
 | ||
|       // ctx.fillStyle = '#ddd'
 | ||
|       // ctx.fillText(rgbPresetName, canvasWidth - rectWidth, canvasHeight / 2)
 | ||
|     },
 | ||
|     initPage() {
 | ||
|       const resizeObserver = new ResizeObserver(() => {
 | ||
|         if (element_ct.style.width) {
 | ||
|           console.log('Size changed')
 | ||
| 
 | ||
|           renderingEngine = getRenderingEngine(renderingEngineId)
 | ||
| 
 | ||
|           if (renderingEngine) {
 | ||
|             this.$nextTick(() => {
 | ||
|               // renderingEngine.resize(true, false)
 | ||
|             })
 | ||
|           }
 | ||
|         }
 | ||
|       })
 | ||
| 
 | ||
|       const elements = [
 | ||
|         element_ct,
 | ||
|         element_pet,
 | ||
|         element_fusion
 | ||
|       ]
 | ||
| 
 | ||
|       elements.forEach((element) => {
 | ||
|         element.oncontextmenu = (e) => e.preventDefault()
 | ||
| 
 | ||
|         resizeObserver.observe(element)
 | ||
|       })
 | ||
|       element_mip.oncontextmenu = (e) => e.preventDefault()
 | ||
|       resizeObserver.observe(element_mip)
 | ||
|       this.$nextTick(() => {
 | ||
|         this.run()
 | ||
|       })
 | ||
|     },
 | ||
|     async run() {
 | ||
|       this.loading = true
 | ||
|       // 初始化Cornerstone和相关库
 | ||
|       await initLibraries()
 | ||
|       renderingEngine = new RenderingEngine(renderingEngineId)
 | ||
| 
 | ||
|       this.ctSeries = JSON.parse(sessionStorage.getItem('ctSeriesInfo'))
 | ||
|       this.petSeries = JSON.parse(sessionStorage.getItem('petSeriesInfo'))
 | ||
|       await this.getImages()
 | ||
| 
 | ||
|       // 设置viewport
 | ||
|       await this.setUpDisplay()
 | ||
| 
 | ||
|       // 设置viewportTools and synchronizers
 | ||
|       this.setUpToolGroups()
 | ||
| 
 | ||
|       this.setUpSynchronizers()
 | ||
|       this.$refs['CT_AXIAL'].scroll(0)
 | ||
|       this.$refs['PT_AXIAL'].scroll(0)
 | ||
|       this.$refs['FUSION_AXIAL'].scroll(0)
 | ||
|       await this.getAnnotations()
 | ||
| 
 | ||
|       eventTarget.addEventListener(cornerstoneTools.Enums.Events.ANNOTATION_ADDED, (e) => {
 | ||
|         this.onAnnotationAdded(e)
 | ||
|       })
 | ||
| 
 | ||
|       const debouncedCallback = this.debounce((e) => {
 | ||
|         console.log(e)
 | ||
|         const { annotation } = e.detail
 | ||
|         const { cachedStats } = annotation.data
 | ||
|         var isNotValidAnnotationNum = 0
 | ||
|         for (const volumeId in cachedStats) {
 | ||
|           var statObj = cachedStats[volumeId]
 | ||
|           var arr = Object.keys(statObj)
 | ||
|           if (arr.length < 2) {
 | ||
|             ++isNotValidAnnotationNum
 | ||
|           }
 | ||
|         }
 | ||
|         if (isNotValidAnnotationNum === 0) {
 | ||
|           this.onAnnotationModified(e)
 | ||
|         } else {
 | ||
|           // 移除标记
 | ||
|           this.removeAnnotation({ otherMeasureData: annotation })
 | ||
|           const { remark } = annotation.data
 | ||
|           // 清除病灶上的标记信息
 | ||
|           if (remark === 'Liver' || remark === 'Lung') {
 | ||
|             this.$refs['questions'].clearMeasuredData(remark)
 | ||
|             // 激活工具
 | ||
|             this.setNonTargetMeasurementStatus({ status: true, toolName: 'CircleROI' })
 | ||
|           } else {
 | ||
|             this.$refs['tableQuestions'].clearMeasuredData()
 | ||
|             // 激活工具
 | ||
|             this.setBasicToolActive('CircleROI')
 | ||
|           }
 | ||
|         }
 | ||
|       }, 120)
 | ||
|       eventTarget.addEventListener(cornerstoneTools.Enums.Events.ANNOTATION_MODIFIED, (e) => {
 | ||
|         debouncedCallback(e)
 | ||
|       })
 | ||
|       eventTarget.addEventListener(cornerstoneTools.Enums.Events.ANNOTATION_SELECTION_CHANGE, (e) => {
 | ||
|         console.log(e)
 | ||
|         const { detail } = e
 | ||
|         const { selection } = detail
 | ||
|         if (selection && selection.length > 0) {
 | ||
|           const annotationUID = selection[0]
 | ||
|           const i = this.measureDatas.findIndex(item => item.OtherMeasureData.annotationUID === annotationUID)
 | ||
|           if (i > -1) {
 | ||
|             this.$refs['tableQuestions'].setActiveCollapse(this.measureDatas[i])
 | ||
|           }
 | ||
|         }
 | ||
|       })
 | ||
| 
 | ||
|       this.loading = false
 | ||
|     },
 | ||
|     getAnnotations() {
 | ||
|       return new Promise(resolve => {
 | ||
|         annotation.state.removeAllAnnotations()
 | ||
|         this.isInitMeasureDatas = false
 | ||
|         var visitTaskId = this.$route.query.visitTaskId
 | ||
|         getTableAnswerRowInfoList(visitTaskId).then(res => {
 | ||
|           var arr = []
 | ||
|           res.Result.forEach(el => {
 | ||
|             if (el.OtherMeasureData) {
 | ||
|               el.OtherMeasureData = JSON.parse(el.OtherMeasureData)
 | ||
|               el.OtherMeasureData.invalidated = false
 | ||
|               // el.OtherMeasureData.highlighted = false
 | ||
|               if (this.readingTaskState === 2) {
 | ||
|                 el.OtherMeasureData.isLocked = true
 | ||
|               }
 | ||
|               el.OtherMeasureData.data.remark = el.OrderMarkName
 | ||
| 
 | ||
|               const viewport = renderingEngine.getViewport('PT_AXIAL')
 | ||
| 
 | ||
|               annotation.state.addAnnotation(el.OtherMeasureData, viewport.element)
 | ||
|             }
 | ||
|             arr.push(el)
 | ||
|           })
 | ||
|           this.measureDatas = arr
 | ||
|           this.isInitMeasureDatas = true
 | ||
| 
 | ||
|           resolve()
 | ||
|         })
 | ||
|       })
 | ||
|     },
 | ||
|     addOrUpdateAnnotations(obj) {
 | ||
|       var idx = this.measureDatas.findIndex(item => item.QuestionId === obj.data.QuestionId)
 | ||
|       if (idx > -1) {
 | ||
|         for (const k in this.measureDatas[idx]) {
 | ||
|           if (obj.data[k]) {
 | ||
|             this.measureDatas[idx][k] = obj.data[k]
 | ||
|           }
 | ||
|         }
 | ||
|         console.log('更新标记成功', idx)
 | ||
|       } else {
 | ||
|         this.measureDatas.push(obj.data)
 | ||
|         console.log('新增标记成功')
 | ||
|       }
 | ||
|     },
 | ||
|     removeAnnotation(obj) {
 | ||
|       const { otherMeasureData, type } = obj
 | ||
|       const { annotationUID } = otherMeasureData
 | ||
|       const i = this.measureDatas.findIndex(item => item.OtherMeasureData.annotationUID === annotationUID)
 | ||
|       if (i > -1) {
 | ||
|         if (type === 'delete') {
 | ||
|           this.measureDatas.splice(i, 1)
 | ||
|         } else {
 | ||
|           if (this.measureDatas[i].FristAddTaskId) {
 | ||
|             this.measureDatas[i].OtherMeasureData = ''
 | ||
|           } else {
 | ||
|             this.measureDatas.splice(i, 1)
 | ||
|           }
 | ||
|         }
 | ||
|         annotation.state.removeAnnotation(annotationUID)
 | ||
|         renderingEngine.render()
 | ||
|       }
 | ||
|     },
 | ||
|     onAnnotationAdded(e) {
 | ||
|       this.isLocate = false
 | ||
|       const { detail } = e
 | ||
|       const { annotation } = detail
 | ||
|       const { metadata } = annotation
 | ||
|       const measureData = {}
 | ||
|       measureData.frame = 0
 | ||
|       measureData.data = annotation
 | ||
|       measureData.type = metadata.toolName
 | ||
|       if (this.isNonTargetMeasurement || annotation.data.remark === 'Liver' || annotation.data.remark === 'Lung') {
 | ||
|         this.$refs['questions'].setMeasuredData(measureData)
 | ||
|       } else {
 | ||
|         this.$refs['tableQuestions'].setMeasuredData(measureData)
 | ||
|       }
 | ||
| 
 | ||
|       this.setToolMode('passive', metadata.toolName)
 | ||
|       this.activeTool = ''
 | ||
|     },
 | ||
|     onAnnotationModified(e) {
 | ||
|       if (this.isLocate) {
 | ||
|         this.isLocate = false
 | ||
|         return
 | ||
|       }
 | ||
|       const { detail } = e
 | ||
|       const { annotation } = detail
 | ||
|       const { metadata, data } = annotation
 | ||
|       var idx = this.measureDatas.findIndex(item => item.OtherMeasureData && item.OtherMeasureData.data.remark === data.remark)
 | ||
|       if (idx > -1) {
 | ||
|         var questionInfo = this.measureDatas[idx]
 | ||
|         const measureData = {}
 | ||
|         measureData.frame = 0
 | ||
|         measureData.data = annotation
 | ||
|         measureData.type = metadata.toolName
 | ||
|         measureData.suvMax = data.cachedStats[`volumeId:${ptVolumeId}`] && data.cachedStats[`volumeId:${ptVolumeId}`].max ? data.cachedStats[`volumeId:${ptVolumeId}`].max.toFixed(this.digitPlaces) : null
 | ||
|         measureData.modalityUnit = data.cachedStats[`volumeId:${ptVolumeId}`].modalityUnit
 | ||
|         if (data.remark === 'Liver' || data.remark === 'Lung') {
 | ||
|           this.$refs['questions'].setMeasuredData(measureData)
 | ||
|           this.isNonTargetMeasurement = false
 | ||
|         } else {
 | ||
|           this.$refs['tableQuestions'].modifyMeasuredData({ measureData, questionInfo })
 | ||
|         }
 | ||
| 
 | ||
|         this.setToolMode('passive', metadata.toolName)
 | ||
|         if (this.activeTool) {
 | ||
|           this.activeTool = ''
 | ||
|         }
 | ||
|       }
 | ||
|     },
 | ||
|     setNonTargetMeasurementStatus(obj) {
 | ||
|       this.isNonTargetMeasurement = obj.status
 | ||
|       if (obj.toolName) {
 | ||
|         // this.setMeasureToolActive(obj.toolName)
 | ||
|         this.setBasicToolActive(obj.toolName)
 | ||
|       }
 | ||
|     },
 | ||
|     getImageInfo(viewportId, referencedImageId) {
 | ||
|       if (!viewportId || !referencedImageId) return
 | ||
|       var imageArr = referencedImageId.split('/')
 | ||
|       var instanceId = imageArr[imageArr.length - 1]
 | ||
|       if (viewportId === 'CT_AXIAL') {
 | ||
|         return { studyId: this.ctSeries.studyId, seriesId: this.ctSeries.seriesId, instanceId, viewportId }
 | ||
|       } else if (viewportId === 'PT_AXIAL') {
 | ||
|         return { studyId: this.petSeries.studyId, seriesId: this.petSeries.seriesId, instanceId, viewportId }
 | ||
|       } else {
 | ||
|         return { studyId: this.petSeries.studyId, seriesId: this.petSeries.seriesId, instanceId, viewportId }
 | ||
|       }
 | ||
|     },
 | ||
|     debounce(callback, delay) {
 | ||
|       let timerId
 | ||
|       return function() {
 | ||
|         clearTimeout(timerId)
 | ||
|         timerId = setTimeout(() => {
 | ||
|           callback.apply(this, arguments)
 | ||
|         }, delay)
 | ||
|       }
 | ||
|     },
 | ||
|     async getImages() {
 | ||
|       // .splice(0, 10)
 | ||
|       const ctImageIds = await createImageIdsAndCacheMetaData({
 | ||
|         modality: 'CT',
 | ||
|         imageIds: this.ctSeries.imageIds
 | ||
|       })
 | ||
|       const ptImageIds = await createImageIdsAndCacheMetaData({
 | ||
|         modality: 'PT',
 | ||
|         imageIds: this.petSeries.imageIds
 | ||
|       })
 | ||
|       this.ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
 | ||
|         imageIds: ctImageIds
 | ||
|       })
 | ||
|       this.ptVolume = await volumeLoader.createAndCacheVolume(ptVolumeId, {
 | ||
|         imageIds: ptImageIds
 | ||
|       })
 | ||
|     },
 | ||
|     setUpToolGroups() {
 | ||
|       cornerstoneTools.addTool(WindowLevelTool)
 | ||
|       cornerstoneTools.addTool(ZoomTool)
 | ||
|       cornerstoneTools.addTool(StackScrollMouseWheelTool)
 | ||
|       cornerstoneTools.addTool(MIPJumpToClickTool)
 | ||
|       cornerstoneTools.addTool(VolumeRotateMouseWheelTool)
 | ||
|       cornerstoneTools.addTool(EllipticalROITool)
 | ||
|       cornerstoneTools.addTool(CircleROITool)
 | ||
|       cornerstoneTools.addTool(CrosshairsTool)
 | ||
|       cornerstoneTools.addTool(TrackballRotateTool)
 | ||
|       cornerstoneTools.addTool(ProbeTool)
 | ||
|       cornerstoneTools.addTool(PanTool)
 | ||
|       cornerstoneTools.addTool(ScaleOverlayTool)
 | ||
| 
 | ||
|       ctToolGroup = ctToolGroup = ToolGroupManager.createToolGroup(ctToolGroupId)
 | ||
|       ctToolGroup.addViewport(viewportIds.CT.AXIAL, renderingEngineId)
 | ||
| 
 | ||
|       ptToolGroup = ToolGroupManager.createToolGroup(ptToolGroupId)
 | ||
|       ptToolGroup.addViewport(viewportIds.PT.AXIAL, renderingEngineId)
 | ||
| 
 | ||
|       fusionToolGroup = ToolGroupManager.createToolGroup(fusionToolGroupId)
 | ||
|       fusionToolGroup.addViewport(viewportIds.FUSION.AXIAL, renderingEngineId)
 | ||
| 
 | ||
|       const toolGroups = [ctToolGroup, ptToolGroup]
 | ||
|       toolGroups.forEach((toolGroup) => {
 | ||
|         toolGroup.addTool(PanTool.toolName)
 | ||
|         toolGroup.addTool(ZoomTool.toolName)
 | ||
|         toolGroup.addTool(StackScrollMouseWheelTool.toolName)
 | ||
|         toolGroup.addTool(EllipticalROITool.toolName)
 | ||
|         toolGroup.addTool(CircleROITool.toolName, {
 | ||
|           getTextLines: this.getTextLines
 | ||
|         })
 | ||
|         toolGroup.addTool(WindowLevelTool.toolName)
 | ||
|         toolGroup.addTool(ProbeTool.toolName)
 | ||
|         toolGroup.addTool(ScaleOverlayTool.toolName)
 | ||
|       })
 | ||
| 
 | ||
|       fusionToolGroup.addTool(PanTool.toolName)
 | ||
|       fusionToolGroup.addTool(ZoomTool.toolName)
 | ||
|       fusionToolGroup.addTool(StackScrollMouseWheelTool.toolName)
 | ||
|       fusionToolGroup.addTool(EllipticalROITool.toolName, {
 | ||
|         volumeId: ptVolumeId
 | ||
|       })
 | ||
|       fusionToolGroup.addTool(CircleROITool.toolName, {
 | ||
|         volumeId: ptVolumeId,
 | ||
|         getTextLines: this.getTextLines
 | ||
|       })
 | ||
|       fusionToolGroup.addTool(ProbeTool.toolName)
 | ||
|       fusionToolGroup.addTool(ScaleOverlayTool.toolName)
 | ||
|       // Here is the difference in the toolGroups used, that we need to specify the
 | ||
|       // volume to use for the WindowLevelTool for the fusion viewports
 | ||
| 
 | ||
|       fusionToolGroup.addTool(WindowLevelTool.toolName, {
 | ||
|         volumeId: ptVolumeId
 | ||
|       });
 | ||
| 
 | ||
|       [ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => {
 | ||
|         // toolGroup.setToolActive(ProbeTool.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(StackScrollMouseWheelTool.toolName)
 | ||
|         // toolGroup.setToolPassive(EllipticalROITool.toolName)
 | ||
|         if (this.readingTaskState === 2) {
 | ||
|           toolGroup.setToolEnabled(CircleROITool.toolName)
 | ||
|         } else {
 | ||
|           toolGroup.setToolPassive(CircleROITool.toolName)
 | ||
|         }
 | ||
| 
 | ||
|         // toolGroup.setToolPassive(ProbeTool.toolName)
 | ||
|         // toolGroup.setToolPassive(WindowLevelTool.toolName)
 | ||
|         // toolGroup.setToolEnabled(ScaleOverlayTool.toolName)
 | ||
|       })
 | ||
| 
 | ||
|       // MIP Tool Groups
 | ||
|       mipToolGroup = null
 | ||
|       if (!ToolGroupManager.getToolGroup(mipToolGroupUID)) {
 | ||
|         mipToolGroup = ToolGroupManager.createToolGroup(mipToolGroupUID)
 | ||
|       } else {
 | ||
|         mipToolGroup = ToolGroupManager.getToolGroup(mipToolGroupUID)
 | ||
|       }
 | ||
| 
 | ||
|       mipToolGroup.addTool('VolumeRotateMouseWheel')
 | ||
|       mipToolGroup.addTool('MIPJumpToClickTool', {
 | ||
|         //
 | ||
|         toolGroupId: ptToolGroupId
 | ||
|       })
 | ||
| 
 | ||
|       // Set the initial state of the tools, here we set one tool active on left click.
 | ||
|       // This means left click will draw that tool.
 | ||
|       mipToolGroup.setToolActive('MIPJumpToClickTool', {
 | ||
|         bindings: [
 | ||
|           {
 | ||
|             mouseButton: MouseBindings.Primary // Left ClickR
 | ||
|           }
 | ||
|         ]
 | ||
|       })
 | ||
|       // As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback`
 | ||
|       // hook instead of mouse buttons, it does not need to assign any mouse button.
 | ||
|       mipToolGroup.setToolActive('VolumeRotateMouseWheel')
 | ||
| 
 | ||
|       mipToolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId)
 | ||
|     },
 | ||
|     getTextLines(data, targetId) {
 | ||
|       const cachedVolumeStats = data.cachedStats[targetId]
 | ||
|       const {
 | ||
|         radius,
 | ||
|         radiusUnit,
 | ||
|         area,
 | ||
|         mean,
 | ||
|         stdDev,
 | ||
|         max,
 | ||
|         isEmptyArea,
 | ||
|         // Modality,
 | ||
|         areaUnit,
 | ||
|         modalityUnit
 | ||
|       } = cachedVolumeStats
 | ||
|       var unit = modalityUnit
 | ||
|       if (modalityUnit === 'raw') {
 | ||
|         unit = 'SUV'
 | ||
|       } else {
 | ||
|         unit = modalityUnit
 | ||
|       }
 | ||
|       const textLines = []
 | ||
|       if (data.remark) {
 | ||
|         textLines.push(data.remark)
 | ||
|       }
 | ||
|       if (radius) {
 | ||
|         const radiusLine = isEmptyArea
 | ||
|           ? `Radius: Oblique not supported`
 | ||
|           : `Radius: ${this.roundNumber(radius)} ${radiusUnit}`
 | ||
|         textLines.push(radiusLine)
 | ||
|       }
 | ||
| 
 | ||
|       if (area) {
 | ||
|         const areaLine = isEmptyArea
 | ||
|           ? `Area: Oblique not supported`
 | ||
|           : `Area: ${this.roundNumber(area)} ${areaUnit}`
 | ||
|         textLines.push(areaLine)
 | ||
|       }
 | ||
| 
 | ||
|       if (mean) {
 | ||
|         textLines.push(`Mean: ${this.roundNumber(mean)} ${unit}`)
 | ||
|       }
 | ||
| 
 | ||
|       if (max) {
 | ||
|         textLines.push(`Max: ${this.roundNumber(max)} ${unit}`)
 | ||
|       }
 | ||
| 
 | ||
|       if (stdDev) {
 | ||
|         textLines.push(`Std Dev: ${this.roundNumber(stdDev)} ${unit}`)
 | ||
|       }
 | ||
| 
 | ||
|       return textLines
 | ||
|     },
 | ||
|     roundNumber(value) {
 | ||
|       if (value === undefined || value === null || value === '') {
 | ||
|         return 'NaN'
 | ||
|       }
 | ||
|       value = Number(value)
 | ||
| 
 | ||
|       return value.toFixed(this.digitPlaces)
 | ||
|     },
 | ||
|     // 设置测量工具启用(不会对输入作出反应)
 | ||
|     setBasicToolActive(toolName) {
 | ||
|       var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId]
 | ||
|       toolGroupIds.forEach((toolGroupId) => {
 | ||
|         const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
 | ||
|         if (this.activeTool === toolName) {
 | ||
|           toolGroup.setToolPassive(toolName)
 | ||
|         } else {
 | ||
|           if (this.activeTool) {
 | ||
|             toolGroup.setToolPassive(this.activeTool)
 | ||
|           }
 | ||
|           var bindings = []
 | ||
|           if (toolName === 'Pan') {
 | ||
|             bindings = [
 | ||
|               {
 | ||
|                 mouseButton: MouseBindings.Auxiliary // Middle Click
 | ||
|               },
 | ||
|               {
 | ||
|                 mouseButton: MouseBindings.Primary // Left Click
 | ||
|               }
 | ||
|             ]
 | ||
|           } else if (toolName === 'Zoom') {
 | ||
|             bindings = [
 | ||
|               {
 | ||
|                 mouseButton: MouseBindings.Secondary // Right Click
 | ||
|               },
 | ||
|               {
 | ||
|                 mouseButton: MouseBindings.Primary // Left Click
 | ||
|               }
 | ||
|             ]
 | ||
|           } else {
 | ||
|             bindings = [
 | ||
|               {
 | ||
|                 mouseButton: MouseBindings.Primary // Left Click
 | ||
|               }
 | ||
|             ]
 | ||
|           }
 | ||
|           toolGroup.setToolActive(toolName, {
 | ||
|             bindings: bindings
 | ||
|           })
 | ||
|           // if (toolName === 'CircleROI') {
 | ||
|           //   toolGroup.setToolConfiguration(CircleROITool.toolName, {
 | ||
|           //     // centerPointRadius: 4,
 | ||
|           //     disableCursor: true
 | ||
|           //   })
 | ||
|           // }
 | ||
|         }
 | ||
|       })
 | ||
|       if (this.activeTool === toolName) {
 | ||
|         this.activeTool = ''
 | ||
|       } else {
 | ||
|         this.activeTool = toolName
 | ||
|       }
 | ||
|     },
 | ||
|     setMeasureToolActive(toolName) {
 | ||
|       var toolObj = this.$refs['tableQuestions'].isCanActiveTool(toolName)
 | ||
|       if (!toolObj || toolObj.isDisabled) return
 | ||
|       this.setBasicToolActive(toolName)
 | ||
|     },
 | ||
|     // 鼠标移入测量工具时,判断工具是否可激活
 | ||
|     enter(e, toolName) {
 | ||
|       var i = this.measuredTools.findIndex(item => item.toolName === toolName)
 | ||
|       if (i === -1) return
 | ||
|       var isCurrentTask = this.isCurrentTask
 | ||
|       var readingTaskState = this.readingTaskState
 | ||
|       if (!isCurrentTask || readingTaskState >= 2) {
 | ||
|         this.measuredTools[i].isDisabled = true
 | ||
|         e.target.style.cursor = 'not-allowed'
 | ||
|         if (this.activeTool) {
 | ||
|           this.setToolMode('enabled', toolName)
 | ||
|           this.activeTool = ''
 | ||
|         }
 | ||
|       } else {
 | ||
|         var obj = this.$refs['tableQuestions'].isCanActiveTool(toolName, true)
 | ||
|         this.measuredTools[i].disabledReason = obj.reason
 | ||
|         if (!obj.isCanActiveTool) {
 | ||
|           if (this.activeTool === toolName) {
 | ||
|             this.setToolMode('passive', toolName)
 | ||
|             this.activeTool = ''
 | ||
|           }
 | ||
|           this.measuredTools[i].isDisabled = true
 | ||
|           e.target.style.cursor = 'not-allowed'
 | ||
|         } else {
 | ||
|           this.measuredTools[i].isDisabled = false
 | ||
|           e.target.style.cursor = 'pointer'
 | ||
|         }
 | ||
|       }
 | ||
|     },
 | ||
|     setToolMode(mode, toolName) {
 | ||
|       var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId]
 | ||
|       toolGroupIds.forEach((toolGroupId) => {
 | ||
|         const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
 | ||
|         toolGroup.setToolPassive(toolName)
 | ||
|         if (mode === 'enabled') {
 | ||
|           toolGroup.setToolEnabled(toolName)
 | ||
|         } else if (mode === 'passive') {
 | ||
|           toolGroup.setToolPassive(toolName)
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     // 截屏
 | ||
|     saveImage() {
 | ||
|       // const canvas = this.canvas.querySelector('canvas')
 | ||
|       // var pictureBaseStr = canvas.toDataURL('image/png', 1)
 | ||
|       // return pictureBaseStr
 | ||
|     },
 | ||
|     setUpSynchronizers() {
 | ||
|       // const axialCameraSynchronizerId = 'AXIAL_CAMERA_SYNCHRONIZER_ID'
 | ||
|       // const ctVoiSynchronizerId = 'CT_VOI_SYNCHRONIZER_ID'
 | ||
|       // const ptVoiSynchronizerId = 'PT_VOI_SYNCHRONIZER_ID'
 | ||
| 
 | ||
|       axialCameraPositionSynchronizer = createCameraPositionSynchronizer(
 | ||
|         axialCameraSynchronizerId
 | ||
|       )
 | ||
|       ctVoiSynchronizer = createVOISynchronizer(ctVoiSynchronizerId)
 | ||
|       ptVoiSynchronizer = createVOISynchronizer(ptVoiSynchronizerId);
 | ||
| 
 | ||
|       // Add viewports to camera synchronizers
 | ||
|       [
 | ||
|         viewportIds.CT.AXIAL,
 | ||
|         viewportIds.PT.AXIAL,
 | ||
|         viewportIds.FUSION.AXIAL
 | ||
|       ].forEach((viewportId) => {
 | ||
|         axialCameraPositionSynchronizer.add({
 | ||
|           renderingEngineId,
 | ||
|           viewportId
 | ||
|         })
 | ||
|       });
 | ||
| 
 | ||
|       // Add viewports to VOI synchronizers
 | ||
|       [
 | ||
|         viewportIds.CT.AXIAL
 | ||
|       ].forEach((viewportId) => {
 | ||
|         ctVoiSynchronizer.add({
 | ||
|           renderingEngineId,
 | ||
|           viewportId
 | ||
|         })
 | ||
|       });
 | ||
|       [
 | ||
|         viewportIds.FUSION.AXIAL
 | ||
|       ].forEach((viewportId) => {
 | ||
|         // In this example, the fusion viewports are only targets for CT VOI
 | ||
|         // synchronization, not sources
 | ||
|         ctVoiSynchronizer.addTarget({
 | ||
|           renderingEngineId,
 | ||
|           viewportId
 | ||
|         })
 | ||
|       });
 | ||
|       [
 | ||
|         viewportIds.PT.AXIAL,
 | ||
|         viewportIds.FUSION.AXIAL,
 | ||
|         viewportIds.PETMIP.CORONAL
 | ||
|       ].forEach((viewportId) => {
 | ||
|         ptVoiSynchronizer.add({
 | ||
|           renderingEngineId,
 | ||
|           viewportId
 | ||
|         })
 | ||
|       })
 | ||
|     },
 | ||
| 
 | ||
|     async setUpDisplay() {
 | ||
|       // 创建 viewports
 | ||
|       const viewportInputArray = [
 | ||
|         {
 | ||
|           viewportId: viewportIds.CT.AXIAL,
 | ||
|           type: ViewportType.ORTHOGRAPHIC,
 | ||
|           element: element_ct,
 | ||
|           defaultOptions: {
 | ||
|             orientation: Enums.OrientationAxis.AXIAL
 | ||
|             // background: [0, 0, 0]
 | ||
|           }
 | ||
|         },
 | ||
|         {
 | ||
|           viewportId: viewportIds.PT.AXIAL,
 | ||
|           type: ViewportType.ORTHOGRAPHIC,
 | ||
|           element: element_pet,
 | ||
|           defaultOptions: {
 | ||
|             orientation: Enums.OrientationAxis.AXIAL,
 | ||
|             background: [1, 1, 1]
 | ||
|           }
 | ||
|         },
 | ||
|         {
 | ||
|           viewportId: viewportIds.FUSION.AXIAL,
 | ||
|           type: ViewportType.ORTHOGRAPHIC,
 | ||
|           element: element_fusion,
 | ||
|           defaultOptions: {
 | ||
|             orientation: Enums.OrientationAxis.AXIAL
 | ||
|             // background: [0, 0, 0]
 | ||
|           }
 | ||
|         },
 | ||
|         {
 | ||
|           viewportId: viewportIds.PETMIP.CORONAL,
 | ||
|           type: ViewportType.ORTHOGRAPHIC,
 | ||
|           element: element_mip,
 | ||
|           defaultOptions: {
 | ||
|             orientation: Enums.OrientationAxis.CORONAL,
 | ||
|             background: [1, 1, 1]
 | ||
|           }
 | ||
|         }
 | ||
|       ]
 | ||
| 
 | ||
|       renderingEngine.setViewports(viewportInputArray)
 | ||
| 
 | ||
|       // Set the volumes to load
 | ||
|       this.ptVolume.load()
 | ||
|       this.ctVolume.load()
 | ||
| 
 | ||
|       // Set volumes on the viewports
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ctVolumeId,
 | ||
|             callback: setCtTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.CT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.PT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ctVolumeId,
 | ||
|             callback: setCtTransferFunctionForVolumeActor
 | ||
|           },
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetColorMapTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.FUSION.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       // Calculate size of fullBody pet mip
 | ||
|       const ptVolumeDimensions = this.ptVolume.dimensions
 | ||
| 
 | ||
|       // Only make the MIP as large as it needs to be.
 | ||
|       const slabThickness = Math.sqrt(
 | ||
|         ptVolumeDimensions[0] * ptVolumeDimensions[0] +
 | ||
|       ptVolumeDimensions[1] * ptVolumeDimensions[1] +
 | ||
|       ptVolumeDimensions[2] * ptVolumeDimensions[2]
 | ||
|       )
 | ||
| 
 | ||
|       setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetTransferFunctionForVolumeActor,
 | ||
|             blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
 | ||
|             slabThickness
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.PETMIP.CORONAL]
 | ||
|       )
 | ||
| 
 | ||
|       await this.initializeCameraSync(renderingEngine)
 | ||
| 
 | ||
|       // Render the viewports
 | ||
|       renderingEngine.render()
 | ||
|     },
 | ||
|     initCameraSynchronization(sViewport, tViewport) {
 | ||
|       // Initialise the sync as they viewports will have
 | ||
|       // Different initial zoom levels for viewports of different sizes.
 | ||
| 
 | ||
|       const camera = sViewport.getCamera()
 | ||
| 
 | ||
|       tViewport.setCamera(camera)
 | ||
|     },
 | ||
|     async initializeCameraSync(renderingEngine) {
 | ||
|       return new Promise(resolve => {
 | ||
|       // The fusion scene is the target as it is scaled to both volumes.
 | ||
|       // TODO -> We should have a more generic way to do this,
 | ||
|       // So that when all data is added we can synchronize zoom/position before interaction.
 | ||
| 
 | ||
|         const axialCtViewport = renderingEngine.getViewport(viewportIds.CT.AXIAL)
 | ||
| 
 | ||
|         const axialPtViewport = renderingEngine.getViewport(viewportIds.PT.AXIAL)
 | ||
| 
 | ||
|         const axialFusionViewport = renderingEngine.getViewport(
 | ||
|           viewportIds.FUSION.AXIAL
 | ||
|         )
 | ||
| 
 | ||
|         this.initCameraSynchronization(axialFusionViewport, axialCtViewport)
 | ||
|         this.initCameraSynchronization(axialFusionViewport, axialPtViewport)
 | ||
| 
 | ||
|         renderingEngine.render()
 | ||
|         resolve()
 | ||
|       })
 | ||
|     },
 | ||
|     async resetViewport() {
 | ||
|       if (this.activeTool) {
 | ||
|         this.setToolMode('passive', this.activeTool)
 | ||
|         this.activeTool = ''
 | ||
|       }
 | ||
|       // Set volumes on the viewports
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ctVolumeId,
 | ||
|             callback: setCtTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.CT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await setVolumesForViewports(
 | ||
|         renderingEngine,
 | ||
|         [
 | ||
|           {
 | ||
|             volumeId: ptVolumeId,
 | ||
|             callback: setPetTransferFunctionForVolumeActor
 | ||
|           }
 | ||
|         ],
 | ||
|         [viewportIds.PT.AXIAL]
 | ||
|       )
 | ||
| 
 | ||
|       await this.initializeCameraSync(renderingEngine)
 | ||
|       renderingEngine.render()
 | ||
| 
 | ||
|       // Render the viewports
 | ||
| 
 | ||
|       const viewports = [
 | ||
|         'CT_AXIAL',
 | ||
|         'PT_AXIAL',
 | ||
|         'FUSION_AXIAL'
 | ||
|       ]
 | ||
|       viewports.map(viewportId => {
 | ||
|         const renderingEngine = getRenderingEngine(renderingEngineId)
 | ||
|         const viewport = renderingEngine.getViewport(viewportId)
 | ||
|         viewport.resetCamera(true, true, false)
 | ||
| 
 | ||
|         viewport.render()
 | ||
|       })
 | ||
|     },
 | ||
|     toggleInvert() {
 | ||
|       if (this.activeTool) {
 | ||
|         this.setToolMode('passive', this.activeTool)
 | ||
|         this.activeTool = ''
 | ||
|       }
 | ||
|       const viewports = [
 | ||
|         { viewportId: 'CT_AXIAL', volumeId: ctVolumeId },
 | ||
|         { viewportId: 'PT_AXIAL', volumeId: ptVolumeId }
 | ||
|         // { viewportId: 'FUSION_AXIAL', volumeId: ptVolumeId }
 | ||
|       ]
 | ||
|       viewports.map(v => {
 | ||
|         const { viewportId, volumeId } = v
 | ||
|         const renderingEngine = getRenderingEngine(renderingEngineId)
 | ||
| 
 | ||
|         // Get the volume viewport
 | ||
|         const viewport = (
 | ||
|           renderingEngine.getViewport(viewportId)
 | ||
|         )
 | ||
| 
 | ||
|         // Get the volume actor from the viewport
 | ||
|         const actorEntry = viewport.getActor(volumeId)
 | ||
| 
 | ||
|         const volumeActor = actorEntry.actor
 | ||
|         const rgbTransferFunction = volumeActor
 | ||
|           .getProperty()
 | ||
|           .getRGBTransferFunction(0)
 | ||
| 
 | ||
|         // Todo: implement invert in setProperties
 | ||
|         csUtils.invertRgbTransferFunction(rgbTransferFunction)
 | ||
| 
 | ||
|         viewport.render()
 | ||
|       })
 | ||
|     },
 | ||
|     showPanel(e) {
 | ||
|       // console.log(e.currentTarget.parentNode.parentNode)
 | ||
|       // e.currentTarget.parentNode.parentNode.lastChild.style.display = 'block'
 | ||
|       e.currentTarget.firstChild.lastChild.style.display = 'block'
 | ||
|     },
 | ||
|     handleMouseout(e) {
 | ||
|       e.currentTarget.firstChild.lastChild.style.display = 'none'
 | ||
|     },
 | ||
| 
 | ||
|     setDicomCanvasRotate(v) {
 | ||
|       if (this.activeTool) {
 | ||
|         this.setToolMode('passive', this.activeTool)
 | ||
|         this.activeTool = ''
 | ||
|       }
 | ||
|       // rotateArr: [
 | ||
|       //   { label: this.$t('trials:reading:button:rotateDefault'), val: 1 }, // 默认值
 | ||
|       //   { label: this.$t('trials:reading:button:rotateVertical'), val: 2 }, // 垂直翻转
 | ||
|       //   { label: this.$t('trials:reading:button:rotateHorizontal'), val: 3 }, // 水平翻转
 | ||
|       //   { label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4 }, // 左转90度
 | ||
|       //   { label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 }// 右转90度
 | ||
|       // ],
 | ||
|       // Get the rendering engine
 | ||
|       const renderingEngine = getRenderingEngine(renderingEngineId)
 | ||
|       const viewportIds = [
 | ||
|         'CT_AXIAL',
 | ||
|         'PT_AXIAL',
 | ||
|         'FUSION_AXIAL'
 | ||
|       ]
 | ||
|       viewportIds.forEach(viewportId => {
 | ||
|         const viewport = renderingEngine.getViewport(viewportId)
 | ||
|         console.log(viewport.getCamera())
 | ||
|         const { flipHorizontal, flipVertical } = viewport.getCamera()
 | ||
|         // Flip the viewport horizontally
 | ||
|         if (v === 2) {
 | ||
|           viewport.setCamera({ flipHorizontal: !flipHorizontal })
 | ||
|         } else if (v === 3) {
 | ||
|           viewport.setCamera({ flipVertical: !flipVertical })
 | ||
|         } else if (v === 4) {
 | ||
|           viewport.setCamera({ viewAngle: -90 })
 | ||
|         } else if (v === 5) {
 | ||
|           viewport.setCamera({ viewAngle: 90 })
 | ||
|         }
 | ||
| 
 | ||
|         viewport.render()
 | ||
|       })
 | ||
|     },
 | ||
|     handleElementsClick() {
 | ||
|       var elements = [element_ct, element_pet, element_fusion, element_mip]
 | ||
|       elements.forEach((el, index) => {
 | ||
|         el.addEventListener('click', () => {
 | ||
|           this.activeIndex = index + 1
 | ||
|         })
 | ||
|       })
 | ||
|     },
 | ||
|     imageLocation(obj) {
 | ||
|       this.setToolToTarget(obj)
 | ||
|       if (!obj.otherMeasureData) return
 | ||
|       this.isLocate = true
 | ||
|       const { metadata } = obj.otherMeasureData
 | ||
|       var imageArr = metadata.referencedImageId.split('/')
 | ||
|       var instanceId = imageArr[imageArr.length - 1]
 | ||
|       var viewportId = null
 | ||
|       // var seriesId = data.seriesId
 | ||
|       // var instanceId = data.instanceId
 | ||
|       var index = -1
 | ||
|       index = this.petSeries.instanceList.findIndex(i => i === instanceId)
 | ||
|       if (index > -1) {
 | ||
|         viewportId = 'PT_AXIAL'
 | ||
|         this.$refs[viewportId].scroll(index)
 | ||
|         return
 | ||
|       }
 | ||
|       index = this.ctSeries.instanceList.findIndex(i => i === instanceId)
 | ||
|       if (index > -1) {
 | ||
|         viewportId = 'CT_AXIAL'
 | ||
|         this.$refs[viewportId].scroll(index)
 | ||
|         return
 | ||
|       }
 | ||
|     },
 | ||
|     setToolToTarget(obj) {
 | ||
|       if (this.readingTaskState < 2 && obj.markTool && !obj.isMarked) {
 | ||
|         this.setBasicToolActive(obj.markTool)
 | ||
|       }
 | ||
|     },
 | ||
|     getTargetIdImage(
 | ||
|       targetId,
 | ||
|       renderingEngine
 | ||
|     ) {
 | ||
|       if (targetId.startsWith('imageId:')) {
 | ||
|         const imageId = targetId.split('imageId:')[1]
 | ||
|         const imageURI = utilities.imageIdToURI(imageId)
 | ||
|         let viewports = utilities.getViewportsWithImageURI(
 | ||
|           imageURI,
 | ||
|           renderingEngine.id
 | ||
|         )
 | ||
| 
 | ||
|         if (!viewports || !viewports.length) {
 | ||
|           return
 | ||
|         }
 | ||
| 
 | ||
|         viewports = viewports.filter((viewport) => {
 | ||
|           return viewport.getCurrentImageId() === imageId
 | ||
|         })
 | ||
| 
 | ||
|         if (!viewports || !viewports.length) {
 | ||
|           return
 | ||
|         }
 | ||
| 
 | ||
|         return viewports[0].getImageData()
 | ||
|       } else if (targetId.startsWith('volumeId:')) {
 | ||
|         const volumeId = targetId.split('volumeId:')[1]
 | ||
|         const viewports = utilities.getViewportsWithVolumeId(
 | ||
|           volumeId,
 | ||
|           renderingEngine.id
 | ||
|         )
 | ||
| 
 | ||
|         if (!viewports || !viewports.length) {
 | ||
|           return
 | ||
|         }
 | ||
| 
 | ||
|         return viewports[0].getImageData()
 | ||
|       } else {
 | ||
|         throw new Error(
 | ||
|           'getTargetIdImage: targetId must start with "imageId:" or "volumeId:"'
 | ||
|         )
 | ||
|       }
 | ||
|     },
 | ||
|     receiveMsg(event) {
 | ||
|       if (event.data.type === 'readingPageUpdate') {
 | ||
|         this.$refs['questions'].initList()
 | ||
|         this.$refs['tableQuestions'].initList()
 | ||
|         this.isLocate = true
 | ||
|         this.getAnnotations()
 | ||
|       } else if (event.data.type === 'readingPageUpdate') {
 | ||
|         this.readingTaskState = event.data.data.readingTaskState
 | ||
|       }
 | ||
|     },
 | ||
|     ...mapMutations({ setLanguage: 'lang/setLanguage' })
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| <style lang="scss" scoped>
 | ||
| 
 | ||
| .dicom-viewer-container{
 | ||
|     display:flex;
 | ||
|     flex-direction: column;
 | ||
|     height: 100%;
 | ||
|     background-color: #000;
 | ||
|     padding: 5px 2px;
 | ||
|   }
 | ||
|   ::-webkit-scrollbar {
 | ||
|     width: 5px;
 | ||
|     height: 5px;
 | ||
|   }
 | ||
|   ::-webkit-scrollbar-thumb {
 | ||
|     border-radius: 10px;
 | ||
|     background: #d0d0d0;
 | ||
|   }
 | ||
|   .dicom-tools{
 | ||
|     box-sizing: border-box;
 | ||
|     width: 100%;
 | ||
|     height: 61px;
 | ||
|     padding: 0 5px;
 | ||
|     border: 1px solid #727272;
 | ||
|     display: flex;
 | ||
|     flex-direction: row;
 | ||
|     justify-content: flex-start;
 | ||
|     align-items: center;
 | ||
|     .tool-wrapper{
 | ||
|       display: flex;
 | ||
|       flex-direction: column;
 | ||
|       justify-content: center;
 | ||
|       align-items: center;
 | ||
|       margin-right: 30px;
 | ||
|       .icon{
 | ||
|         padding: 5px;
 | ||
|         border: 1px solid #404040;
 | ||
|         cursor: pointer;
 | ||
|         text-align: center;
 | ||
|         .svg-icon{
 | ||
|           font-size:20px;
 | ||
|           color:#ddd;
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .text{
 | ||
|         position: relative;
 | ||
|         font-size: 12px;
 | ||
|         margin-top: 5px;
 | ||
|         color: #d0d0d0;
 | ||
|         display: none;
 | ||
|       }
 | ||
|     }
 | ||
|     .tool_active{
 | ||
|       background-color: #607d8b;
 | ||
|     }
 | ||
|     .tool_disabled{
 | ||
|       cursor:not-allowed
 | ||
|     }
 | ||
|     .icon:hover{
 | ||
|       background-color: #607d8b;
 | ||
|     }
 | ||
|     .dropdown {
 | ||
|       position: relative;
 | ||
|       display: inline-block;
 | ||
|       .icon-content{
 | ||
|         display: flex;
 | ||
|         align-items: center;
 | ||
|         border: 1px solid #404040;
 | ||
|       }
 | ||
|       .text{
 | ||
|         text-align: center;
 | ||
|       }
 | ||
|       .tool-icon{
 | ||
|         padding: 5px;
 | ||
|         cursor: pointer;
 | ||
|         text-align: center;
 | ||
|         .svg-icon{
 | ||
|           font-size:20px;
 | ||
|           color:#ddd;
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       .arrow-icon{
 | ||
|         cursor: pointer;
 | ||
|         padding: 7px 2px 7px 0px;
 | ||
|       }
 | ||
|       .arrow-icon:hover{
 | ||
|         background-color: #607d8b;
 | ||
|       }
 | ||
|       .icon-content-d:hover{
 | ||
|         background-color: #607d8b;
 | ||
|       }
 | ||
|       .tool-icon-d{
 | ||
|         padding: 5px;
 | ||
|         .svg-icon{
 | ||
|           font-size:20px;
 | ||
|           color:#ddd;
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|     }
 | ||
| 
 | ||
|     .dropdown-content {
 | ||
|       display: none;
 | ||
|       position: absolute;
 | ||
|       background-color: #383838;
 | ||
|       color: #fff;
 | ||
|       box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
 | ||
|       z-index: 9999;
 | ||
|       font-size: 12px;
 | ||
|       ul{
 | ||
|         list-style: none;
 | ||
|         margin: 0;
 | ||
|         padding: 0;
 | ||
|         text-align: center;
 | ||
|         li{
 | ||
|           a{
 | ||
|             display: block;
 | ||
|             padding: 5px;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|       ul li:hover{
 | ||
|         background-color: #727272;
 | ||
|         cursor: pointer;
 | ||
|       }
 | ||
|     }
 | ||
|     .layout-content ul li{
 | ||
|       border-top:1px solid #ddd;
 | ||
|       border-left:1px solid #ddd;
 | ||
|     }
 | ||
|     .layout-content ul  .flex_row{
 | ||
|       // border: 1px solid #ddd;
 | ||
|       display: flex;
 | ||
|       justify-content: space-between;
 | ||
|       flex-direction: row;
 | ||
|       align-items: center;
 | ||
|       // padding: 2px;
 | ||
|       margin-bottom: 2px;
 | ||
|     }
 | ||
|     .layout-content ul .flex_column{
 | ||
|       display: flex;
 | ||
|       justify-content: space-between;
 | ||
|       flex-direction: column;
 | ||
|       align-items: center;
 | ||
|       margin-bottom: 2px;
 | ||
|     }
 | ||
|     .layout_box_1_1{
 | ||
|       flex:1;
 | ||
|       // border: 1px solid #ddd;
 | ||
|       line-height: 30px;
 | ||
|       font-size: 12px;
 | ||
|       text-align: center;
 | ||
|       border-bottom:1px solid #ddd;
 | ||
|       border-right:1px solid #ddd;
 | ||
|       // padding: 0 5px;
 | ||
|     }
 | ||
|     .layout_box_1_2{
 | ||
|       flex:1;
 | ||
|       line-height: 15px;
 | ||
|       font-size: 10px;
 | ||
|       text-align: center;
 | ||
|       border-bottom:1px solid #ddd;
 | ||
|       border-right:1px solid #ddd;
 | ||
|     }
 | ||
|     .layout-content li .layout_box_1_1 :last-child{
 | ||
|       color: red;
 | ||
|     }
 | ||
|     .layout-content li:hover {
 | ||
|       cursor: pointer;
 | ||
|       background-color: #727272;
 | ||
|     }
 | ||
| 
 | ||
|   }
 | ||
|   .dicom-datas{
 | ||
|     box-sizing: border-box;
 | ||
|     flex: 1;
 | ||
|     margin-top: 5px;
 | ||
|     height: 100%;
 | ||
|     display: flex;
 | ||
|     flex-direction: row;
 | ||
|     justify-content: flex-start;
 | ||
|     overflow: hidden;
 | ||
|     .form-container{
 | ||
|       width: 350px;
 | ||
|       height: 100%;
 | ||
|       border: 1px solid #727272;
 | ||
|     }
 | ||
|     .dicom-container{
 | ||
|       box-sizing: border-box;
 | ||
|       flex: 1;
 | ||
|       height: 100%;
 | ||
|       border: 1px solid #727272;
 | ||
|     }
 | ||
| 
 | ||
|     .measurement-container{
 | ||
|       overflow-y: auto;
 | ||
|     }
 | ||
|     .box{
 | ||
|       display: grid;
 | ||
|       box-sizing: border-box;
 | ||
|       height: 100%;
 | ||
|       padding: 0;
 | ||
|       .item{
 | ||
|         box-sizing: border-box;
 | ||
|         position: relative;
 | ||
|         border: 1px solid rgba(255, 255, 255, 0.21);
 | ||
|         position: relative;
 | ||
|         &_active{
 | ||
|           // border: 2px solid #ffeb3b;fff
 | ||
|           border: 1px dashed #428bca;
 | ||
| 
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|     .box_2_2{
 | ||
|       grid-template-columns: repeat(2, 50%); //1列,占50%
 | ||
|       grid-template-rows: repeat(2, 50%); //1行,占50%
 | ||
|     }
 | ||
| 
 | ||
|   }
 | ||
| </style>
 |