454 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
| <template>
 | |
|   <div
 | |
|     class="viewport_container"
 | |
|     :class="['item',activeIndex === index?'item_active':'']"
 | |
|     @mousemove="sliderMousemove"
 | |
|     @mouseup="sliderMouseup"
 | |
|   >
 | |
|     <div :id="`viewport${index}`" style="height: 100%;" />
 | |
|     <!-- 序列信息 -->
 | |
|     <div
 | |
|       v-if="seriesInfo.taskBlindName"
 | |
|       class="seriesInfo_wrapper"
 | |
|       :style="{color:index%2 == 1 ? '#ddd' : '#666'}"
 | |
|     >
 | |
|       <h2 v-if="isReadingShowSubjectInfo" class="taskInfo_container">
 | |
|         {{ subjectCode }} {{ seriesInfo.taskBlindName }}
 | |
|       </h2>
 | |
|       <div v-if="index === 1 || index === 2">
 | |
|         Series: #{{ seriesInfo.seriesNumber }}
 | |
|       </div>
 | |
|       <div v-if="index !== 4">Image: #{{ `${seriesInfo.imageIdIndex + 1} / ${seriesInfo.imageIds.length}` }}</div>
 | |
|       <div v-if="index === 1 || index === 2">{{ seriesInfo.modality }}</div>
 | |
|     </div>
 | |
|     <!-- 描述信息 -->
 | |
|     <div
 | |
|       v-if="seriesInfo.description && (index === 1 || index === 2)"
 | |
|       class="descInfo_wrapper"
 | |
|       :style="{color:index%2 == 1 ? '#ddd' : '#666'}"
 | |
|     >
 | |
|       <div>{{ seriesInfo.description }}</div>
 | |
|     </div>
 | |
| 
 | |
|     <!-- 图像信息  -->
 | |
|     <div
 | |
|       v-if="seriesInfo.description && (index === 1 || index === 2 || index === 3)"
 | |
|       class="imageInfo_wrapper_l"
 | |
|       :style="{color:index%2 == 1 ? '#ddd' : '#666'}"
 | |
|     >
 | |
|       <div v-show="mousePosition.index.length > 0">
 | |
|         Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
 | |
|       </div>
 | |
|       <div v-if="(seriesInfo.modality === 'CT' || seriesInfo.modality === 'DR' || seriesInfo.modality === 'CR') && mousePosition.value">
 | |
|         HU: {{ mousePosition.value }}
 | |
|       </div>
 | |
|       <div v-else-if="(seriesInfo.modality === 'PT' && mousePosition.value)">
 | |
|         SUVbw(g/ml): {{ digitPlaces === -1 ?mousePosition.value.toFixed(3) :mousePosition.value.toFixed(digitPlaces) }}
 | |
|       </div>
 | |
|       <div v-else-if="mousePosition.value">
 | |
|         Density: {{ mousePosition.value }}
 | |
|       </div>
 | |
|       <div v-if="index === 1 || index === 2">
 | |
|         W*H: {{ imageInfo.size }}
 | |
|       </div>
 | |
| 
 | |
|       <div v-if="index === 1 || index === 2">Zoom: {{ imageInfo.zoom }}</div>
 | |
|     </div>
 | |
|     <div
 | |
|       v-if="seriesInfo.description && (index === 1 || index === 2 || index === 3)"
 | |
|       class="imageInfo_wrapper_r"
 | |
|       :style="{color:index%2 == 1 ? '#ddd' : '#666'}"
 | |
|     >
 | |
|       <div v-show="imageInfo.location && index !== 3 ">Location: {{ imageInfo.location }}</div>
 | |
|       <div v-show="seriesInfo.sliceThickness && index !== 3">Slice Thickness: {{ Number(seriesInfo.sliceThickness).toFixed(2) }}mm</div>
 | |
|       <div v-show="imageInfo.wwwc ">WW/WL: {{ imageInfo.wwwc }}</div>
 | |
|     </div>
 | |
|     <div v-if="index!==4" ref="sliderBox" class="slider_box" :style="{background: index === 2?'#ddd':'#333'}" @click.stop="goViewer($event)">
 | |
|       <div :style="{top: sliderBoxHeight + '%'}" class="box" @mousedown.stop="sliderMousedown($event)" />
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| <script>
 | |
| import {
 | |
|   getRenderingEngine,
 | |
|   metaData,
 | |
|   utilities,
 | |
|   // cache,
 | |
|   // StackViewport,
 | |
|   // BaseVolumeViewport,
 | |
|   // getEnabledElement,
 | |
|   // getEnabledElementByIds,
 | |
|   // eventTarget,
 | |
|   Enums } from '@cornerstonejs/core'
 | |
| // import * as cornerstonejs from '@cornerstonejs/core'
 | |
| // import * as cornerstoneTools from '@cornerstonejs/tools'
 | |
| import {
 | |
|   utilities as toosUtilities,
 | |
|   annotation
 | |
| } from '@cornerstonejs/tools'
 | |
| // import FusionEvent from './FusionEvent'
 | |
| const {
 | |
|   VOLUME_NEW_IMAGE
 | |
|   // VOLUME_LOADED,
 | |
|   // IMAGE_VOLUME_MODIFIED,
 | |
|   // VOLUME_VIEWPORT_NEW_VOLUME
 | |
| } = Enums.Events
 | |
| 
 | |
| var renderingEngine
 | |
| var viewport
 | |
| export default {
 | |
|   name: 'Viewport',
 | |
|   props: {
 | |
|     activeIndex: {
 | |
|       type: Number,
 | |
|       default: 0
 | |
|     },
 | |
|     index: {
 | |
|       // 1:ct  2:pet  3:fusion  4:mip
 | |
|       type: Number,
 | |
|       required: true
 | |
|     },
 | |
|     isReadingShowSubjectInfo: {
 | |
|       type: Boolean,
 | |
|       default: false
 | |
|     },
 | |
|     seriesInfo: {
 | |
|       type: Object,
 | |
|       default() {
 | |
|         return {}
 | |
|       }
 | |
|     },
 | |
|     renderingEngineId: {
 | |
|       type: String,
 | |
|       required: true
 | |
|     },
 | |
|     viewportId: {
 | |
|       type: String,
 | |
|       required: true
 | |
|     },
 | |
|     volume: {
 | |
|       type: Object,
 | |
|       default() {
 | |
|         return {}
 | |
|       }
 | |
|     },
 | |
|     measureDatas: {
 | |
|       type: Array,
 | |
|       default() {
 | |
|         return []
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       subjectCode: '',
 | |
|       mousePosition: { index: [], value: null, modalityUnit: '', world: [], ctVal: null, ptVal: null },
 | |
|       digitPlaces: -1,
 | |
|       imageInfo: {
 | |
|         zoom: null,
 | |
|         size: null,
 | |
|         location: null,
 | |
|         sliceThickness: null,
 | |
|         wwwc: null
 | |
|       },
 | |
|       sliderBoxHeight: 0,
 | |
|       sliderInfo: {
 | |
|         oldB: null,
 | |
|         oldM: null,
 | |
|         isMove: false
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     activeIndex: {
 | |
|       handler(v) {
 | |
|         console.log('activeIndex ', v)
 | |
|       }
 | |
|     }
 | |
|     // measureDatas: {
 | |
|     //   handler(v) {
 | |
|     //     this.setAnnotation(v)
 | |
|     //   },
 | |
|     //   deep: true,
 | |
|     //   immediate: true
 | |
|     // }
 | |
|   },
 | |
|   mounted() {
 | |
|     // console.log(toosUtilities)
 | |
|     this.subjectCode = this.$route.query.subjectCode
 | |
|     var element = document.getElementById(`viewport${this.index}`)
 | |
| 
 | |
|     element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage)
 | |
|     element.addEventListener(Enums.Events.CAMERA_MODIFIED, this.handleCameraModified)
 | |
|     element.addEventListener(Enums.Events.VOI_MODIFIED, this.handleVOIModified)
 | |
|     // element.addEventListener(Enums.Events.MOUSE_MOVE, this.handleMouseMove)
 | |
|     // element.addEventListener('mousemove', this.handleMouseMove)
 | |
|     if (this.index !== 4) {
 | |
|       element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.handleMouseMove)
 | |
|     }
 | |
|     element.addEventListener('mouseleave', this.handleMouseLeave)
 | |
|     document.addEventListener('mouseup', () => {
 | |
|       this.sliderMouseup()
 | |
|     })
 | |
|   },
 | |
|   destroyed() {
 | |
| 
 | |
|   },
 | |
|   methods: {
 | |
|     handleVolumeNewImage(e) {
 | |
|       const { imageIndex } = e.detail
 | |
|       this.seriesInfo.imageIdIndex = imageIndex
 | |
|       renderingEngine = getRenderingEngine(this.renderingEngineId)
 | |
|       viewport = renderingEngine.getViewport(this.viewportId)
 | |
|       if (viewport) {
 | |
|         var zoom = viewport.getZoom()
 | |
|         if (zoom) {
 | |
|           this.imageInfo.zoom = zoom.toFixed(4)
 | |
|         }
 | |
|         var imageId = viewport.getCurrentImageId()
 | |
|         if (imageId) {
 | |
|           const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
 | |
|           this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
 | |
|           this.imageInfo.location = imagePlaneModule.sliceLocation
 | |
|           // this.getOrientationMarker(imagePlaneModule)
 | |
|           this.sliderBoxHeight = imageIndex * 100 / (this.seriesInfo.imageIds.length - 1)
 | |
|         }
 | |
|         var properties = viewport.getProperties()
 | |
|         if (properties && properties.voiRange) {
 | |
|           var { lower, upper } = properties.voiRange
 | |
|           const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(lower, upper)
 | |
|           this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
 | |
|         }
 | |
|         // this.setAnnotation(imageId, viewport.element)
 | |
|       }
 | |
|     },
 | |
|     getOrientationMarker(imagePlane) {
 | |
|       // const enabledElement = cornerstone.getEnabledElement(element)
 | |
|       // const imagePlane = cornerstone.metaData.get(
 | |
|       //   'imagePlaneModule',
 | |
|       //   enabledElement.image.imageId
 | |
|       // )
 | |
| 
 | |
|       if (!imagePlane || !imagePlane.rowCosines || !imagePlane.columnCosines) {
 | |
|         return
 | |
|       }
 | |
|       console.log(imagePlane.rowCosines)
 | |
|       const row = toosUtilities.orientation.getOrientationStringLPS(imagePlane.rowCosines)
 | |
|       const column = toosUtilities.orientation.getOrientationStringLPS(imagePlane.columnCosines)
 | |
|       const oppositeRow = toosUtilities.orientation.invertOrientationStringLPS(row)
 | |
|       const oppositeColumn = toosUtilities.orientation.invertOrientationStringLPS(column)
 | |
|       // const markers = {
 | |
|       //   top: oppositeColumn,
 | |
|       //   bottom: column,
 | |
|       //   left: oppositeRow,
 | |
|       //   right: row
 | |
|       // }
 | |
|       // if (!markers) {
 | |
|       //   return
 | |
|       // }
 | |
|       // this.orientationMarkers = [oppositeColumn, row, column, oppositeRow]
 | |
|       // this.originalMarkers = [oppositeColumn, row, column, oppositeRow]
 | |
|       console.log(oppositeColumn, row, column, oppositeRow)
 | |
|       // this.setMarkers()
 | |
|     },
 | |
|     handleCameraModified(e) {
 | |
|       renderingEngine = getRenderingEngine(this.renderingEngineId)
 | |
|       viewport = renderingEngine.getViewport(this.viewportId)
 | |
|       if (!viewport) return
 | |
|       var zoom = viewport.getZoom()
 | |
|       if (!zoom) return
 | |
|       this.imageInfo.zoom = zoom.toFixed(4)
 | |
|     },
 | |
|     handleVOIModified(e) {
 | |
|       renderingEngine = getRenderingEngine(this.renderingEngineId)
 | |
|       viewport = renderingEngine.getViewport(this.viewportId)
 | |
|       if (!viewport) return
 | |
|       var properties = viewport.getProperties()
 | |
|       if (properties && properties.voiRange) {
 | |
|         var { lower, upper } = properties.voiRange
 | |
|         const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(
 | |
|           lower,
 | |
|           upper
 | |
|         )
 | |
|         this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
 | |
|       }
 | |
|     },
 | |
|     handleMouseMove(e) {
 | |
|       const { currentPoints } = e.detail
 | |
|       const worldPoint = currentPoints.world
 | |
|       const { imageData } = this.volume
 | |
|       const index = imageData.worldToIndex(worldPoint)
 | |
| 
 | |
|       index[0] = Math.floor(index[0])
 | |
|       index[1] = Math.floor(index[1])
 | |
|       index[2] = Math.floor(index[2])
 | |
|       this.mousePosition.index = index
 | |
| 
 | |
|       this.mousePosition.value = this.getValue(this.volume, worldPoint)
 | |
|     },
 | |
|     getValue(volume, worldPos) {
 | |
|       const { dimensions, scalarData, imageData } = volume
 | |
| 
 | |
|       const index = imageData.worldToIndex(worldPos)
 | |
| 
 | |
|       index[0] = Math.floor(index[0])
 | |
|       index[1] = Math.floor(index[1])
 | |
|       index[2] = Math.floor(index[2])
 | |
| 
 | |
|       if (!utilities.indexWithinDimensions(index, dimensions)) {
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       const yMultiple = dimensions[0]
 | |
|       const zMultiple = dimensions[0] * dimensions[1]
 | |
| 
 | |
|       const value = scalarData[index[2] * zMultiple + index[1] * yMultiple + index[0]]
 | |
| 
 | |
|       return value
 | |
|     },
 | |
|     handleMouseLeave(e) {
 | |
|       this.mousePosition.index = []
 | |
|       this.mousePosition.value = null
 | |
|     },
 | |
|     goViewer(e) {
 | |
|       console.log(e)
 | |
|       var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
 | |
|       this.sliderBoxHeight = height
 | |
|       var index = Math.trunc(this.seriesInfo.imageIds.length * this.sliderBoxHeight / 100)
 | |
|       if (this.seriesInfo.imageIdIndex !== index) {
 | |
|         this.scroll(index)
 | |
|       }
 | |
|     },
 | |
|     sliderMousedown(e) {
 | |
|       var boxHeight = this.$refs['sliderBox'].clientHeight
 | |
|       this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
 | |
|       this.sliderInfo.oldM = e.clientY
 | |
|       this.sliderInfo.isMove = true
 | |
|       e.stopImmediatePropagation()
 | |
|       e.stopPropagation()
 | |
|       e.preventDefault()
 | |
|     },
 | |
|     sliderMousemove(e) {
 | |
|       if (!this.sliderInfo.isMove) return
 | |
|       var PX = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
 | |
|       var boxHeight = this.$refs['sliderBox'].clientHeight
 | |
|       if (PX < 0) return
 | |
|       if (PX > boxHeight) return
 | |
|       var height = PX * 100 / boxHeight
 | |
|       var index = Math.trunc(this.seriesInfo.imageIds.length * this.sliderBoxHeight / 100)
 | |
|       index = index > this.seriesInfo.imageIds.length ? this.seriesInfo.imageIds.length : index < 0 ? 0 : index
 | |
|       this.sliderBoxHeight = height
 | |
|       if (this.seriesInfo.imageIdIndex !== index) {
 | |
|         this.scroll(index)
 | |
|       }
 | |
|     },
 | |
|     scroll(index) {
 | |
|       renderingEngine = getRenderingEngine(this.renderingEngineId)
 | |
|       viewport = renderingEngine.getViewport(this.viewportId)
 | |
|       const actorEntries = viewport.getActors()
 | |
| 
 | |
|       if (!actorEntries) {
 | |
|         return
 | |
|       }
 | |
|       const delta = index - this.seriesInfo.imageIdIndex
 | |
|       toosUtilities.scroll(viewport, {
 | |
|         delta,
 | |
|         volumeId: actorEntries.uid
 | |
|       })
 | |
|       renderingEngine.render()
 | |
|     },
 | |
|     sliderMouseup(e) {
 | |
|       this.sliderInfo.isMove = false
 | |
|     },
 | |
|     setAnnotation(imageId, element) {
 | |
|       annotation.state.removeAllAnnotations()
 | |
|       this.measureDatas.forEach(item => {
 | |
|         if (item.OtherMeasureData) {
 | |
|           var { metadata } = item.OtherMeasureData
 | |
|           var { referencedImageId } = metadata
 | |
|           if (referencedImageId === imageId) {
 | |
|             annotation.state.addAnnotation(item.OtherMeasureData, element)
 | |
|             // toosUtilities.viewportFilters.getViewportIdsWithToolToRender
 | |
|             // const viewportIdsToRender = toosUtilities.viewportFilters.getViewportIdsWithToolToRender(
 | |
|             //   element,
 | |
|             //   'CircleROI'
 | |
|             // )
 | |
|             // toosUtilities.triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender)
 | |
|           }
 | |
|         }
 | |
|       })
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| <style lang="scss" scoped>
 | |
| .viewport_container{
 | |
|   height: 100%;
 | |
|   .seriesInfo_wrapper {
 | |
|     position: absolute;
 | |
|     left: 10px;
 | |
|     top: 10px;
 | |
|     text-align: left;
 | |
|     font-size: 12px;
 | |
|     z-index: 1;
 | |
|     .taskInfo_container{
 | |
|       color:#f44336;
 | |
|       padding: 5px 0px;
 | |
|       margin: 0;
 | |
|     }
 | |
|   }
 | |
|   .descInfo_wrapper{
 | |
|     position: absolute;
 | |
|     right: 10px;
 | |
|     top: 10px;
 | |
|     text-align: right;
 | |
|     font-size: 12px;
 | |
|     z-index: 1;
 | |
|   }
 | |
|   .imageInfo_wrapper_l{
 | |
|     position: absolute;
 | |
|     left: 10px;
 | |
|     bottom: 10px;
 | |
|     text-align: left;
 | |
|     font-size: 12px;
 | |
|     z-index: 1;
 | |
|   }
 | |
|   .imageInfo_wrapper_r{
 | |
|     position: absolute;
 | |
|     right: 10px;
 | |
|     bottom: 10px;
 | |
|     text-align: right;
 | |
|     font-size: 12px;
 | |
|     z-index: 1;
 | |
|   }
 | |
|   .slider_box{
 | |
|     position: absolute;
 | |
|     right: 1px;
 | |
|     height: calc(100% - 120px);
 | |
|     transform: translateY(-50%);
 | |
|     top: calc(50% - 30px);
 | |
|     width: 10px;
 | |
|     background: #333;
 | |
|     cursor: pointer;
 | |
|     .box{
 | |
|       z-index:10;
 | |
|       background: #9e9e9e;
 | |
|       height: 20px;
 | |
|       width: 100%;
 | |
|       position: absolute;
 | |
|       top: 0;
 | |
|       cursor: move
 | |
|     }
 | |
|   }
 | |
|   // .my_slider_box:after{
 | |
|   //   content: '';
 | |
|   //   position: absolute;
 | |
|   //   bottom: -20px;
 | |
|   //   left: 0;
 | |
|   //   height: 20px;
 | |
|   //   width: 100%;
 | |
|   //   background: #333;
 | |
|   // }
 | |
| }
 | |
| </style>
 |