irc_web/.svn/pristine/c3/c3b0823c38ea5c4832b8398d642...

556 lines
18 KiB
Plaintext

<template>
<div
class="viewport_container"
:class="['item',activeIndex === index?'item_active':'']"
@mousemove="sliderMousemove"
@mouseup="sliderMouseup"
>
<div :id="`viewport${index}`" ref="viewportCanvas" style="height: 100%;width: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 style="position: absolute;left: 50%;top: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.top }}
</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;transform: translateY(-50%);">
{{ markers.right }}
</div>
<div style="position: absolute;left: 50%;bottom: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.bottom }}
</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;transform: translateY(-50%);">
{{ markers.left }}
</div>
<div v-if="presetName" class="color_bar">
<canvas id="colorBar_Canvas" />
</div>
</div>
</template>
<script>
import {
getRenderingEngine,
metaData,
utilities,
// getOrCreateCanvas,
Enums } from '@cornerstonejs/core'
import {
utilities as toolsUtilities,
annotation
// cursors
} from '@cornerstonejs/tools'
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
import { vec3 } from 'gl-matrix'
import html2canvas from 'html2canvas'
const { getColormap } = utilities.colormap
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
},
isFirstRender: true,
defaultWindowLevel: {},
presetName: '',
orientationMarkers: [],
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' }
}
},
watch: {
activeIndex: {
handler(v) {
console.log('activeIndex ', v)
}
}
},
mounted() {
const digitPlaces = parseInt(this.$route.query.digitPlaces)
this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
// console.log(toolsUtilities)
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)
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()
this.sliderBoxHeight = imageIndex * 100 / (this.seriesInfo.imageIds.length - 1)
}
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.defaultWindowLevel.windowWidth = windowWidth
this.defaultWindowLevel.windowCenter = windowCenter
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
if (this.presetName) {
this.renderColorBar(this.presetName)
}
}
},
getOrientationMarker() {
// eslint-disable-next-line no-unused-vars
const { viewUp, viewPlaneNormal } = viewport.getCamera()
const viewRight = vec3.create()
vec3.cross(viewRight, viewUp, viewPlaneNormal)
const columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]]
const rowCosines = viewRight
const rowString = toolsUtilities.orientation.getOrientationStringLPS(rowCosines)
const columnString = toolsUtilities.orientation.getOrientationStringLPS(columnCosines)
const oppositeRowString = toolsUtilities.orientation.invertOrientationStringLPS(rowString)
const oppositeColumnString = toolsUtilities.orientation.invertOrientationStringLPS(columnString)
this.markers.top = oppositeColumnString
this.markers.right = rowString
this.markers.bottom = columnString
this.markers.left = oppositeRowString
this.orientationMarkers = [oppositeColumnString, rowString, columnString, oppositeRowString]
if (this.originalMarkers.length === 0) {
this.originalMarkers = [...this.orientationMarkers]
}
},
rotateMarkers(type) {
var markers = [...this.orientationMarkers]
if (type === 4) {
// 左转90度
this.orientationMarkers = markers.slice(1, 4).concat(markers[0])
} else if (type === 5) {
// 右转90度
this.orientationMarkers = [markers[3]].concat(markers.slice(0, 3))
}
this.setMarkers()
},
resetMarks() {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
},
setMarkers() {
var markers = [...this.orientationMarkers]
for (const key in this.markers) {
var v = markers.shift(0)
this.markers[key] = v
}
},
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
toolsUtilities.scroll(viewport, {
delta,
volumeId: actorEntries.uid
})
renderingEngine.render()
},
sliderMouseup(e) {
this.sliderInfo.isMove = false
},
setAnnotation(imageId, element) {
this.measureDatas.forEach(item => {
if (item.OtherMeasureData) {
var { metadata, annotationUID } = item.OtherMeasureData
var { referencedImageId } = metadata
console.log(annotationUID, annotation.state.getAnnotation(annotationUID))
if (!annotation.state.getAnnotation(annotationUID) && referencedImageId === imageId) {
annotation.state.addAnnotation(item.OtherMeasureData, element)
}
}
})
},
setPreset(presetName) {
this.presetName = presetName
},
renderColorBar(presetName) {
var colorMap = null
if (presetName === 'hsv') {
colorMap = vtkColorMaps.getPresetByName(presetName)
} else {
colorMap = getColormap(presetName)
}
const rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById('colorBar_Canvas')
const ctx = canvas.getContext('2d')
const canvasWidth = 160
const canvasHeight = 5
const rectWidth = 160
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]
}
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)
},
setWwWc() {
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
},
async getScreenshots() {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="FUSION_AXIAL"]`
)
// const divForDownloadViewport = document.querySelector(
// '.viewport_container'
// )
var canvas = await html2canvas(divForDownloadViewport)
var pictureBaseStr = canvas.toDataURL('image/png', 1)
return pictureBaseStr
}
}
}
</script>
<style lang="scss" scoped>
.viewport_container{
width:100%;
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: 20px;
top: 10px;
text-align: right;
font-size: 12px;
z-index: 1;
}
.imageInfo_wrapper_l{
position: absolute;
left: 10px;
bottom: 5px;
text-align: left;
font-size: 12px;
z-index: 1;
}
.imageInfo_wrapper_r{
position: absolute;
right: 40px;
bottom: 5px;
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
}
}
.color_bar{
position: absolute;
// transform:translateY(-50%);
transform: rotate(-90deg);
transform-origin: right;
left: -150px;
top: 30%;
// transform-origin: top left;
z-index: 1;
// background: #f44336;
}
// .my_slider_box:after{
// content: '';
// position: absolute;
// bottom: -20px;
// left: 0;
// height: 20px;
// width: 100%;
// background: #333;
// }
}
</style>