自定义dicom阅片添加mpr
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
653d3c6228
commit
cc5ac4c2a9
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764646377457" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7057" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M192 170.666667a21.333333 21.333333 0 0 0-21.333333 21.333333v640a21.333333 21.333333 0 0 0 21.333333 21.333333h640a21.333333 21.333333 0 0 0 21.333333-21.333333v-640a21.333333 21.333333 0 0 0-21.333333-21.333333h-640zM85.333333 192A106.666667 106.666667 0 0 1 192 85.333333h640A106.666667 106.666667 0 0 1 938.666667 192v640a106.666667 106.666667 0 0 1-106.666667 106.666667h-640A106.666667 106.666667 0 0 1 85.333333 832v-640z" fill="#e6e6e6" p-id="7058"></path><path d="M173.653333 170.666667c-1.621333 5.034667-2.986667 12.373333-2.986666 21.333333v640c0 8.96 1.365333 16.298667 2.986666 21.333333h292.693334c1.621333-5.034667 2.986667-12.373333 2.986666-21.333333v-640c0-8.96-1.365333-16.298667-2.986666-21.333333H173.653333zM99.157333 127.658667C107.306667 111.530667 126.293333 85.333333 160 85.333333h320c33.706667 0 52.736 26.197333 60.8 42.325334 9.301333 18.688 13.866667 41.472 13.866667 64.341333v640c0 22.869333-4.565333 45.653333-13.866667 64.341333-8.106667 16.128-27.093333 42.325333-60.8 42.325334h-320c-33.706667 0-52.736-26.197333-60.8-42.325334C89.856 877.653333 85.333333 854.869333 85.333333 832v-640c0-22.869333 4.522667-45.653333 13.866667-64.341333z" fill="#e6e6e6" p-id="7059"></path><path d="M170.666667 170.666667v298.666666h298.666666V170.666667H170.666667z m-85.333334-10.666667C85.333333 118.784 118.784 85.333333 160 85.333333h320c41.216 0 74.666667 33.450667 74.666667 74.666667v320A74.666667 74.666667 0 0 1 480 554.666667h-320A74.666667 74.666667 0 0 1 85.333333 480v-320z" fill="#e6e6e6" p-id="7060"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -95,34 +95,20 @@
|
|||
<script>
|
||||
// import * as cornerstonejs from '@cornerstonejs/core'
|
||||
import {
|
||||
// RenderingEngine,
|
||||
Enums,
|
||||
// imageLoader,
|
||||
metaData,
|
||||
getRenderingEngine,
|
||||
volumeLoader,
|
||||
// eventTarget,
|
||||
cache,
|
||||
utilities as csUtils,
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
import { utilities as cstUtils } from '@cornerstonejs/tools';
|
||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
|
||||
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
|
||||
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import setPetTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetTransferFunctionForVolumeActor'
|
||||
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
||||
const {
|
||||
ToolGroupManager,
|
||||
MIPJumpToClickTool,
|
||||
VolumeRotateTool,
|
||||
Enums: csToolsEnums,
|
||||
} = cornerstoneTools
|
||||
const { BlendModes, OrientationAxis } = Enums;
|
||||
const { MouseBindings } = csToolsEnums
|
||||
const { getColormap, getColormapNames } = csUtils.colormap;
|
||||
const { ViewportColorbar } = cstUtils.voi.colorbar;
|
||||
const { ColorbarRangeTextPosition } = cstUtils.voi.colorbar.Enums;
|
||||
const { getColormap } = csUtils.colormap;
|
||||
import { vec3, mat4 } from 'gl-matrix'
|
||||
export default {
|
||||
name: 'ImageViewport',
|
||||
|
|
@ -360,7 +346,7 @@ export default {
|
|||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
if (isPlay) {
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: false })
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
} else {
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
}
|
||||
|
|
@ -435,27 +421,6 @@ export default {
|
|||
colorMap = getColormap(presetName)
|
||||
}
|
||||
if (!colorMap) return false
|
||||
// let colormaps = getColormapNames().map(name => getColormap(name))
|
||||
// console.log(colormaps)
|
||||
// const canvas = document.getElementById('colorBar_Canvas')
|
||||
// this.Colorbar = new ViewportColorbar({
|
||||
// id: this.volumeId,
|
||||
// element: this.element,
|
||||
// container: canvas,
|
||||
// colormaps,
|
||||
// activeColormapName: presetName,
|
||||
// ticks: {
|
||||
// position: ColorbarRangeTextPosition.Left,
|
||||
// style: {
|
||||
// font: '12px Arial',
|
||||
// color: '#fff',
|
||||
// maxNumTicks: 8,
|
||||
// tickSize: 5,
|
||||
// tickWidth: 1,
|
||||
// labelMargin: 3,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const rgbPoints = colorMap.RGBPoints
|
||||
const canvas = document.getElementById(`colorBar_Canvas_${this.viewportId}`)
|
||||
if (!canvas) return false
|
||||
|
|
@ -512,7 +477,7 @@ export default {
|
|||
},
|
||||
async setSeriesInfo(obj, option = {}) {
|
||||
try {
|
||||
let { data, volumeId } = obj
|
||||
let { data } = obj
|
||||
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) {
|
||||
data.SliceIndex = this.series.SliceIndex
|
||||
}
|
||||
|
|
@ -523,37 +488,32 @@ export default {
|
|||
this.isMip = isMip;
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
this.volumeId = data.SeriesInstanceUid
|
||||
if (this.isFusion) {
|
||||
this.$nextTick(() => {
|
||||
this.renderColorBar(this.presetName)
|
||||
})
|
||||
let { ct, data, ctVolumeId, ptVolumeId, fusionVolumeId } = obj
|
||||
this.ptVolumeId = `fusion_${this.volumeId}`
|
||||
let { ct, data } = obj
|
||||
this.series = { ...data }
|
||||
this.ctSeries = { ...ct }
|
||||
this.petSeries = { ...data }
|
||||
// const maxCount = Math.max(this.ctSeries.ImageIds.length, this.petSeries.ImageIds.length)
|
||||
// this.$set(this.ctSeries, 'imageMaxLength', maxCount)
|
||||
// this.$set(this.petSeries, 'imageMaxLength', maxCount)
|
||||
// await this.createImageIdsAndCacheMetaData(this.ctSeries)
|
||||
// await this.createImageIdsAndCacheMetaData(this.petSeries)
|
||||
this.volumeId = ptVolumeId
|
||||
this.ptVolumeId = fusionVolumeId
|
||||
await viewport
|
||||
.setVolumes([
|
||||
{
|
||||
volumeId: this.ptVolumeId, callback: (r) => {
|
||||
volumeId: this.volumeId, callback: (r) => {
|
||||
setPetColorMapTransferFunctionForVolumeActor(r)
|
||||
console.log("融合pet渲染成功");
|
||||
}
|
||||
},
|
||||
{
|
||||
volumeId: ctVolumeId, callback: (r) => {
|
||||
volumeId: ct.SeriesInstanceUid, callback: (r) => {
|
||||
setCtTransferFunctionForVolumeActor(r)
|
||||
console.log("融合ct渲染成功")
|
||||
}
|
||||
},
|
||||
{
|
||||
volumeId: this.volumeId, callback: (r) => {
|
||||
volumeId: this.ptVolumeId, callback: (r) => {
|
||||
setPetColorMapTransferFunctionForVolumeActor(r)
|
||||
console.log("融合pet渲染成功");
|
||||
}
|
||||
|
|
@ -565,9 +525,8 @@ export default {
|
|||
})
|
||||
} else {
|
||||
this.series = { ...data }
|
||||
this.volumeId = volumeId
|
||||
if (this.isMip) {
|
||||
let { volume } = obj
|
||||
let volume = cache.getVolume(this.volumeId)
|
||||
const ptVolumeDimensions = volume.dimensions;
|
||||
const slabThickness = Math.sqrt(
|
||||
ptVolumeDimensions[0] * ptVolumeDimensions[0] +
|
||||
|
|
|
|||
|
|
@ -160,6 +160,11 @@
|
|||
<svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" />
|
||||
<svg-icon v-else icon-class="fitToImage" class="svg-icon" />
|
||||
</div>
|
||||
<!-- MPR -->
|
||||
<div class="tool-item" :title="`${$t('trials:reading:button:mpr')}`" @click.prevent="openMPRViewport"
|
||||
v-if="criterionType === 0 && readingTool === 0">
|
||||
<svg-icon icon-class="mpr" class="svg-icon" />
|
||||
</div>
|
||||
<!--融合-->
|
||||
<div v-if="readingTool === 2" class="tool-item" :title="$t('trials:lugano:button:fusion')"
|
||||
@click.prevent="openFusion">
|
||||
|
|
@ -289,10 +294,10 @@
|
|||
<!-- viewports -->
|
||||
<div class="viewports-wrapper">
|
||||
<div ref="container" class="grid-container">
|
||||
<div :class="['viewports-box', isFusion ? 'viewports-box-down' : '']" :style="gridStyle">
|
||||
<div :class="['viewports-box', isFusion || isMPR ? 'viewports-box-down' : '']" :style="gridStyle">
|
||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
|
||||
:style="cellStyle"
|
||||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
:class="['grid-cell', isMPR && index === 2 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
||||
|
|
@ -300,6 +305,18 @@
|
|||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="criterionType === 0 && readingTool === 0"
|
||||
:class="['viewports-box', !isMPR ? 'viewports-box-down' : '']" :style="gridStyle">
|
||||
<div v-for="(v, index) in 3" :key="`viewport-volume-${index}`" :style="cellStyle"
|
||||
:class="['grid-cell', isMPR && index === 2 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<VolumeViewport :ref="`viewport-volume-${index}`" :data-viewport-uid="`viewport-volume-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-volume-${index}`"
|
||||
:viewport-index="index" @activeViewport="activeViewport"
|
||||
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
|
||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="readingTool === 2" :class="['viewports-box', !isFusion ? 'viewports-box-down' : '']"
|
||||
:style="gridStyle">
|
||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`"
|
||||
|
|
@ -457,6 +474,7 @@ import { getTools, getCustomizeStandardsTools, config } from './toolConfig'
|
|||
import StudyList from './StudyList'
|
||||
import Viewport from './Viewport'
|
||||
import PetCtViewport from './PetCtViewport'
|
||||
import VolumeViewport from './VolumeViewport'
|
||||
import mRecisit from './mRecist/QuestionList'
|
||||
import recisit from './Recist/QuestionList'
|
||||
import customizeQuestionList from './customize/QuestionList'
|
||||
|
|
@ -534,6 +552,7 @@ export default {
|
|||
StudyList,
|
||||
Viewport,
|
||||
PetCtViewport,
|
||||
VolumeViewport,
|
||||
mRecisit,
|
||||
recisit,
|
||||
customizeQuestionList,
|
||||
|
|
@ -622,7 +641,6 @@ export default {
|
|||
fusionVisible: false,
|
||||
isFusion: false,
|
||||
studyList: [],
|
||||
volumeData: {},
|
||||
fusionSerieId: {},
|
||||
loadingText: null,
|
||||
toolNames: ['Length', 'Bidirectional', 'RectangleROI', 'ArrowAnnotate', 'CircleROI', 'Eraser'],
|
||||
|
|
@ -645,7 +663,9 @@ export default {
|
|||
annotation: null
|
||||
},
|
||||
|
||||
ManualsClose: false
|
||||
ManualsClose: false,
|
||||
|
||||
isMPR: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -670,7 +690,7 @@ export default {
|
|||
return Array(this.rows * this.cols).fill(0)
|
||||
},
|
||||
viewportKey() {
|
||||
return this.isFusion ? 'viewport-fusion' : 'viewport'
|
||||
return this.isMPR ? 'viewport-volume' : this.isFusion ? 'viewport-fusion' : 'viewport'
|
||||
},
|
||||
openManuals() {
|
||||
return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
|
||||
|
|
@ -1021,11 +1041,13 @@ export default {
|
|||
if (!renderingEngine) {
|
||||
renderingEngine = new RenderingEngine(renderingEngineId)
|
||||
}
|
||||
let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||||
const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3']
|
||||
const element1 = this.$refs['viewport-0'][0].$el
|
||||
const element2 = this.$refs['viewport-1'][0].$el
|
||||
const element3 = this.$refs['viewport-2'][0].$el
|
||||
const element4 = this.$refs['viewport-3'][0].$el
|
||||
|
||||
console.log(Enums.OrientationAxis)
|
||||
let viewportInputArray = [
|
||||
{
|
||||
viewportId: 'viewport-0',
|
||||
|
|
@ -1048,8 +1070,40 @@ export default {
|
|||
element: element4
|
||||
}
|
||||
]
|
||||
let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
|
||||
const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3']
|
||||
if (this.criterionType === 0 && this.readingTool === 0) {
|
||||
let volumeViewportIds = ['viewport-volume-0', 'viewport-volume-1', 'viewport-volume-2']
|
||||
const volumeElement1 = this.$refs['viewport-volume-0'][0].$el
|
||||
const volumeElement2 = this.$refs['viewport-volume-1'][0].$el
|
||||
const volumeElement3 = this.$refs['viewport-volume-2'][0].$el
|
||||
const arr = [
|
||||
{
|
||||
viewportId: 'viewport-volume-0',
|
||||
type: ViewportType.ORTHOGRAPHIC,
|
||||
element: volumeElement1,
|
||||
defaultOptions: {
|
||||
orientation: Enums.OrientationAxis.AXIAL
|
||||
}
|
||||
},
|
||||
{
|
||||
viewportId: 'viewport-volume-1',
|
||||
type: ViewportType.ORTHOGRAPHIC,
|
||||
element: volumeElement2,
|
||||
defaultOptions: {
|
||||
orientation: Enums.OrientationAxis.CORONAL
|
||||
}
|
||||
},
|
||||
{
|
||||
viewportId: 'viewport-volume-2',
|
||||
type: ViewportType.ORTHOGRAPHIC,
|
||||
element: volumeElement3,
|
||||
defaultOptions: {
|
||||
orientation: Enums.OrientationAxis.SAGITTAL
|
||||
}
|
||||
},
|
||||
]
|
||||
viewportInputArray = [...viewportInputArray, ...arr]
|
||||
viewportIds = viewportIds.concat(volumeViewportIds)
|
||||
}
|
||||
if (this.readingTool === 2) {
|
||||
const fusionElement1 = this.$refs['viewport-fusion-0'][0].$el
|
||||
const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el
|
||||
|
|
@ -1121,7 +1175,7 @@ export default {
|
|||
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId)
|
||||
toolGroup.addViewport(viewportId, renderingEngineId)
|
||||
toolGroup.addTool(StackScrollTool.toolName, {
|
||||
loop: true, // 启用循环滚动
|
||||
loop: true, // 启用循环滚动
|
||||
})
|
||||
toolGroup.addTool(ScaleOverlayTool.toolName)
|
||||
|
||||
|
|
@ -1450,7 +1504,9 @@ export default {
|
|||
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
|
||||
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
|
||||
const referencedImageId = annotation.metadata.referencedImageId
|
||||
console.log(annotation, 'annotation')
|
||||
const params = this.getInstanceInfo(referencedImageId)
|
||||
console.log(params, 'params')
|
||||
annotation.visitTaskId = series.TaskInfo.VisitTaskId
|
||||
annotation.studyId = series.StudyId
|
||||
annotation.seriesId = series.Id
|
||||
|
|
@ -2033,7 +2089,10 @@ export default {
|
|||
},
|
||||
setToolsPassive() {
|
||||
if (!this.activeTool) return
|
||||
const toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
let toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
if (this.isMPR) {
|
||||
toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
|
||||
}
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
toolGroup.setToolPassive(this.activeTool)
|
||||
|
|
@ -2042,7 +2101,10 @@ export default {
|
|||
},
|
||||
setToolEnabled() {
|
||||
if (!this.activeTool) return
|
||||
const toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
let toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
if (this.isMPR) {
|
||||
toolGroupIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
|
||||
}
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
toolGroup.setToolEnabled(this.activeTool)
|
||||
|
|
@ -2381,7 +2443,10 @@ export default {
|
|||
})
|
||||
}
|
||||
const renderingEngine = getRenderingEngine(renderingEngineId)
|
||||
const viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
let viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`, `${this.viewportKey}-3`]
|
||||
if (this.isMPR) {
|
||||
viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
|
||||
}
|
||||
renderingEngine.renderViewports(viewportIds)
|
||||
}
|
||||
event.stopImmediatePropagation()
|
||||
|
|
@ -2413,10 +2478,11 @@ export default {
|
|||
}
|
||||
},
|
||||
async toggleTask(taskInfo, taskIndex) {
|
||||
if (this.isFusion) {
|
||||
if (this.isFusion || this.isMPR) {
|
||||
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
|
||||
if (!confirm) return false
|
||||
this.isFusion = false
|
||||
this.isMPR = false
|
||||
this.fullScreenIndex = null
|
||||
this.setToolsPassive()
|
||||
}
|
||||
|
|
@ -2485,7 +2551,7 @@ export default {
|
|||
this.setToolsPassive()
|
||||
},
|
||||
async activeSeries(obj) {
|
||||
if (this.isFusion) {
|
||||
if (this.isFusion || this.isMPR) {
|
||||
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
|
||||
if (!confirm) return false
|
||||
this.isFusion = false
|
||||
|
|
@ -2494,6 +2560,7 @@ export default {
|
|||
this.cols = 1
|
||||
this.activeViewportIndex = 0
|
||||
this.fullScreenIndex = null
|
||||
this.isMPR = false
|
||||
return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
|
||||
}
|
||||
if (!obj.IsDicom) {
|
||||
|
|
@ -2863,12 +2930,14 @@ export default {
|
|||
},
|
||||
getInstanceInfo(imageId) {
|
||||
const params = {}
|
||||
const searchParams = new URLSearchParams(imageId.split('?')[1])
|
||||
for (const [key, value] of searchParams.entries()) {
|
||||
params[key] = value
|
||||
}
|
||||
if (isNaN(params.frame)) {
|
||||
params.frame = null
|
||||
if (imageId) {
|
||||
const searchParams = new URLSearchParams(imageId.split('?')[1])
|
||||
for (const [key, value] of searchParams.entries()) {
|
||||
params[key] = value
|
||||
}
|
||||
if (isNaN(params.frame)) {
|
||||
params.frame = null
|
||||
}
|
||||
}
|
||||
return params
|
||||
},
|
||||
|
|
@ -3021,6 +3090,25 @@ export default {
|
|||
this.$refs[id][0].voiChange(v)
|
||||
})
|
||||
},
|
||||
async openMPRViewport() {
|
||||
this.isMPR = true
|
||||
this.rows = 2
|
||||
this.cols = 2
|
||||
this.loading = true
|
||||
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
|
||||
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
|
||||
this.activeViewportIndex = 0
|
||||
this.$refs[`viewport-0`][0].setSeriesInfo(series)
|
||||
this.$refs[`viewport-1`][0].setSeriesInfo(series)
|
||||
this.$refs[`viewport-2`][0].setSeriesInfo(series)
|
||||
this.$refs[`viewport-3`][0].setSeriesInfo(series)
|
||||
await this.getVolume(series)
|
||||
this.loading = false
|
||||
this.loadingText = null
|
||||
this.$refs[`viewport-volume-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL' }, series))
|
||||
this.$refs[`viewport-volume-1`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL' }, series))
|
||||
this.$refs[`viewport-volume-2`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL' }, series))
|
||||
},
|
||||
async handleFusion(data) {
|
||||
try {
|
||||
this.fusionVisible = false
|
||||
|
|
@ -3030,9 +3118,6 @@ export default {
|
|||
const { ct, pt } = data
|
||||
this.loading = true
|
||||
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
|
||||
// this.resetAnnotation = true
|
||||
// console.log(cornerstoneTools.annotation.state.getAllAnnotations(),'cornerstoneTools.annotation.state')
|
||||
// cornerstoneTools.annotation.state.removeAllAnnotations()
|
||||
this.renderedTaskIds = []
|
||||
if (this.verifyFusionData(ct, pt)) {
|
||||
this.loading = false
|
||||
|
|
@ -3056,21 +3141,14 @@ export default {
|
|||
this.loading = false
|
||||
this.loadingText = null
|
||||
const ctData = {
|
||||
data: ct,
|
||||
volumeId: this.volumeData[ct.SeriesInstanceUid].volumeId
|
||||
data: ct
|
||||
}
|
||||
const ptData = {
|
||||
data: pt,
|
||||
volumeId: this.volumeData[pt.SeriesInstanceUid].volumeId,
|
||||
volume: this.volumeData[pt.SeriesInstanceUid].volume
|
||||
data: pt
|
||||
}
|
||||
const fusionData = {
|
||||
ct,
|
||||
data: pt,
|
||||
volumeId: this.volumeData[pt.SeriesInstanceUid].volumeId,
|
||||
ctVolumeId: this.volumeData[ct.SeriesInstanceUid].volumeId,
|
||||
ptVolumeId: this.volumeData[pt.SeriesInstanceUid].volumeId,
|
||||
fusionVolumeId: this.volumeData[`fusion_${pt.SeriesInstanceUid}`].volumeId
|
||||
}
|
||||
this.$refs[`viewport-0`][0].setSeriesInfo(ct)
|
||||
this.$refs[`viewport-1`][0].setSeriesInfo(pt)
|
||||
|
|
@ -3092,43 +3170,22 @@ export default {
|
|||
}
|
||||
},
|
||||
verifyFusionData(ct, pt) {
|
||||
if (this.fusionSerieId.ct === ct.SeriesInstanceUid && this.fusionSerieId.pt === pt.SeriesInstanceUid && cache.getVolume(this.volumeData[ct.SeriesInstanceUid].volumeId) && cache.getVolume(this.volumeData[pt.SeriesInstanceUid].volumeId) && cache.getVolume(this.volumeData[`fusion_${pt.SeriesInstanceUid}`].volumeId)) {
|
||||
if (this.fusionSerieId.ct === ct.SeriesInstanceUid && this.fusionSerieId.pt === pt.SeriesInstanceUid && cache.getVolume(ct.SeriesInstanceUid) && cache.getVolume(pt.SeriesInstanceUid) && cache.getVolume(`fusion_${pt.SeriesInstanceUid}`)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
async getVolume(serie, isFusion = false) {
|
||||
return new Promise(async res => {
|
||||
let volumeId = null; let volume = null
|
||||
const key = isFusion ? `fusion_${serie.SeriesInstanceUid}` : serie.SeriesInstanceUid
|
||||
if (!this.volumeData[key] || !cache.getVolume(this.volumeData[key].volumeId)) {
|
||||
// if (serie.Modality === 'PT' && !isFusion) {
|
||||
// serie.ImageIds.forEach(async id => {
|
||||
// const imageLoadObject = cache.getImage(id)
|
||||
// if (imageLoadObject) {
|
||||
// await new Promise(res => {
|
||||
// cache.removeImageLoadObject(id, { force: true }) // 从缓存中删除
|
||||
// res()
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
volumeId = `${isFusion ? 'fusion' : serie.Modality}Volume` + ':' + serie.SeriesInstanceUid
|
||||
if (cache.getVolume(volumeId)) {
|
||||
volume = cache.getVolume(volumeId)
|
||||
} else {
|
||||
await this.$refs[`viewport-fusion-0`][0].createImageIdsAndCacheMetaData(serie)
|
||||
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
|
||||
volume.load()
|
||||
}
|
||||
this.volumeData[key] = {}
|
||||
this.volumeData[key].volumeId = volumeId
|
||||
this.volumeData[key].volume = volume
|
||||
let volumeId = `${isFusion ? 'fusion_' : ''}` + serie.SeriesInstanceUid;
|
||||
let volume = null;
|
||||
if (cache.getVolume(volumeId)) {
|
||||
volume = cache.getVolume(volumeId)
|
||||
} else {
|
||||
volumeId = this.volumeData[key].volumeId
|
||||
volume = this.volumeData[key].volume
|
||||
await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
|
||||
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
|
||||
volume.load()
|
||||
}
|
||||
|
||||
res({ volumeId, volume })
|
||||
})
|
||||
},
|
||||
|
|
@ -3506,13 +3563,17 @@ export default {
|
|||
|
||||
.grid-cell {
|
||||
border: 1px dashed #ccc;
|
||||
;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.grid-cell-3 {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
.cell_active {
|
||||
border-color: #fafa00 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,742 @@
|
|||
<template>
|
||||
<div ref="viewport-volume" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
|
||||
element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
|
||||
@mouseleave="sliderMouseleave" :style="{ color: '#ddd' }">
|
||||
<div v-if="series && taskInfo" class="left-top-text">
|
||||
<div v-if="taskInfo.IsExistsClinicalData" class="cd-info" :title="$t('trials:reading:button:clinicalData')">
|
||||
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
|
||||
@click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
|
||||
</div>
|
||||
<h2 v-if="taskInfo.IsReadingShowSubjectInfo && series.TaskInfo" class="subject-info">
|
||||
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
|
||||
</h2>
|
||||
<div>Series: #{{ series.SeriesNumber }}</div>
|
||||
<div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
|
||||
<div>{{ series.Modality }}</div>
|
||||
</div>
|
||||
<div v-if="series" class="right-top-text">
|
||||
<div>{{ series.Description }}</div>
|
||||
</div>
|
||||
<div v-if="series" class="left-bottom-text">
|
||||
<div v-show="mousePosition.index.length > 0">
|
||||
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
|
||||
</div>
|
||||
<div
|
||||
v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value">
|
||||
HU: {{ mousePosition.value }}
|
||||
</div>
|
||||
<div v-else-if="(series.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-show="imageInfo.size">
|
||||
W*H: {{ imageInfo.size }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="series" class="right-bottom-text">
|
||||
<div v-show="imageInfo.location">Location: {{
|
||||
`${Number(imageInfo.location).toFixed(digitPlaces)} mm`
|
||||
}}</div>
|
||||
<div v-show="series.SliceThickness">Slice Thickness: {{
|
||||
`${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
|
||||
}}</div>
|
||||
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
|
||||
</div>
|
||||
<div class="orientation-top">
|
||||
{{ markers.top }}
|
||||
</div>
|
||||
<div class="orientation-right">
|
||||
{{ markers.right }}
|
||||
</div>
|
||||
|
||||
<div class="orientation-bottom">
|
||||
{{ markers.bottom }}
|
||||
</div>
|
||||
<div class="orientation-left">
|
||||
{{ markers.left }}
|
||||
</div>
|
||||
<div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)">
|
||||
<div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
|
||||
@mousedown.stop="sliderMousedown($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
metaData,
|
||||
getRenderingEngine,
|
||||
utilities as csUtils,
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
||||
import { vec3, mat4 } from 'gl-matrix'
|
||||
export default {
|
||||
name: 'ImageViewport',
|
||||
props: {
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
viewportIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
element: '',
|
||||
series: {},
|
||||
ctSeries: {},
|
||||
petSeries: {},
|
||||
taskInfo: null,
|
||||
sliderInfo: {
|
||||
oldB: null,
|
||||
oldM: null,
|
||||
isMove: false,
|
||||
height: 0
|
||||
},
|
||||
mousePosition: {
|
||||
index: [],
|
||||
value: null,
|
||||
modalityUnit: '',
|
||||
world: []
|
||||
},
|
||||
imageInfo: {
|
||||
zoom: null,
|
||||
size: null,
|
||||
location: null,
|
||||
sliceThickness: null,
|
||||
wwwc: null,
|
||||
total: 0
|
||||
},
|
||||
digitPlaces: 2,
|
||||
orientationMarkers: [],
|
||||
originalMarkers: [],
|
||||
markers: { top: '', right: '', bottom: '', left: '' },
|
||||
playClipState: false,
|
||||
wwwcIdx: 2,
|
||||
presetName: '',
|
||||
volumeId: null,
|
||||
defaultWindowLevel: {},
|
||||
rotateAngle: 0,
|
||||
rotateBarLeft: 0,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
|
||||
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
|
||||
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
|
||||
this.$nextTick(() => {
|
||||
this.initViewport()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
initViewport() {
|
||||
this.element = this.$refs['viewport-volume']
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
if (renderingEngine) {
|
||||
renderingEngine.resize(true, false)
|
||||
}
|
||||
})
|
||||
this.element.oncontextmenu = (e) => e.preventDefault()
|
||||
// resizeObserver.observe(this.element)
|
||||
this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
|
||||
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
|
||||
this.element.addEventListener('wheel', (e) => {
|
||||
console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL')
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
|
||||
const totalImages = this.imageInfo.total;
|
||||
|
||||
// 手动实现循环逻辑
|
||||
if (currentImageIdIndex >= totalImages - 1) {
|
||||
// 滚动到最后一张时跳回第一张
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 });
|
||||
} else if (currentImageIdIndex <= 0) {
|
||||
// 滚动到第一张时跳回最后一张
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 });
|
||||
}
|
||||
});
|
||||
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
|
||||
// this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
|
||||
this.element.addEventListener('mouseleave', () => {
|
||||
this.mousePosition.index = []
|
||||
this.mousePosition.value = null
|
||||
})
|
||||
document.addEventListener('mouseup', () => {
|
||||
this.sliderMouseup()
|
||||
})
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
this.sliderMousemove(e)
|
||||
|
||||
})
|
||||
},
|
||||
stackNewImage(e) {
|
||||
const { detail } = e
|
||||
this.series.SliceIndex = detail.imageIndex
|
||||
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const zoom = viewport.getZoom()
|
||||
this.imageInfo.zoom = zoom.toFixed(4)
|
||||
let imageIds = viewport.getImageIds(this.volumeId)
|
||||
let imageId = imageIds[0]
|
||||
if (this.series.orientation === 'AXIAL') imageId = viewport.getCurrentImageId()
|
||||
if (imageId) {
|
||||
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
|
||||
if (this.series.orientation === 'AXIAL') {
|
||||
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
|
||||
this.imageInfo.location = imagePlaneModule.sliceLocation
|
||||
this.imageInfo.total = imageIds.length
|
||||
}
|
||||
if (this.series.orientation === 'CORONAL') {
|
||||
this.imageInfo.size = `${imagePlaneModule.columns}*${imageIds.length}`
|
||||
this.imageInfo.total = imagePlaneModule.rows
|
||||
}
|
||||
if (this.series.orientation === 'SAGITTAL') {
|
||||
this.imageInfo.size = `${imagePlaneModule.rows}*${imageIds.length}`
|
||||
this.imageInfo.total = imagePlaneModule.columns
|
||||
}
|
||||
|
||||
this.getOrientationMarker()
|
||||
let properties = viewport.getProperties(this.volumeId)
|
||||
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)}`
|
||||
}
|
||||
}
|
||||
const toolGroupId = this.viewportId
|
||||
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
|
||||
toolGroup.setToolEnabled('ScaleOverlay')
|
||||
|
||||
},
|
||||
setFullScreen(index) {
|
||||
setTimeout(() => {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(
|
||||
this.viewportId
|
||||
)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: index })
|
||||
viewport.render()
|
||||
})
|
||||
},
|
||||
voiModified(e) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
let properties = viewport.getProperties()
|
||||
if (properties && properties.voiRange) {
|
||||
var { lower, upper } = properties.voiRange
|
||||
const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
|
||||
lower,
|
||||
upper
|
||||
)
|
||||
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
|
||||
this.$emit('upperRangeChange', Math.round(windowWidth))
|
||||
}
|
||||
},
|
||||
getOrientationMarker() {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
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 = cornerstoneTools.utilities.orientation.getOrientationStringLPS(rowCosines)
|
||||
const columnString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(columnCosines)
|
||||
const oppositeRowString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(rowString)
|
||||
const oppositeColumnString = cornerstoneTools.utilities.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]
|
||||
}
|
||||
},
|
||||
setMarkers() {
|
||||
const markers = [...this.orientationMarkers]
|
||||
for (const key in this.markers) {
|
||||
const v = markers.shift(0)
|
||||
this.markers[key] = v
|
||||
}
|
||||
},
|
||||
resetOrientationMarkers() {
|
||||
if (this.originalMarkers.length > 0) {
|
||||
this.orientationMarkers = [...this.originalMarkers]
|
||||
this.setMarkers()
|
||||
}
|
||||
},
|
||||
rotateOrientationMarkers(type) {
|
||||
if (this.orientationMarkers.length > 0) {
|
||||
if (type === 1) {
|
||||
this.resetOrientationMarkers()
|
||||
return
|
||||
}
|
||||
const markers = [...this.orientationMarkers]
|
||||
if (type === 2) {
|
||||
// 垂直翻转
|
||||
this.orientationMarkers[0] = markers[2]
|
||||
this.orientationMarkers[2] = markers[0]
|
||||
} else if (type === 3) {
|
||||
// 水平翻转
|
||||
this.orientationMarkers[1] = markers[3]
|
||||
this.orientationMarkers[3] = markers[1]
|
||||
} else 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()
|
||||
}
|
||||
},
|
||||
toggleClipPlay(isPlay, framesPerSecond) {
|
||||
this.playClipState = isPlay
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
if (isPlay) {
|
||||
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
|
||||
} else {
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
}
|
||||
},
|
||||
scrollPage(type) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
|
||||
const numImages = viewport.getImageIds().length
|
||||
let newImageIdIndex = null
|
||||
if (type === 0) {
|
||||
newImageIdIndex = 0
|
||||
} else if (type === -1) {
|
||||
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
|
||||
} else if (type === 1) {
|
||||
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
|
||||
} else if (type === 99999) {
|
||||
newImageIdIndex = numImages - 1
|
||||
}
|
||||
// viewport.setImageIdIndex(newImageIdIndex)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
|
||||
cornerstoneTools.utilities.cine.stopClip(viewport.element)
|
||||
},
|
||||
setZoom(ratio) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
|
||||
const zoom = viewport.getZoom()
|
||||
if (ratio > 0) {
|
||||
viewport.setZoom(zoom * 1.05)
|
||||
} else {
|
||||
viewport.setZoom(zoom / 1.05)
|
||||
}
|
||||
viewport.render()
|
||||
},
|
||||
resize(forceFitToWindow) {
|
||||
console.log('resize: ', forceFitToWindow)
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
if (!forceFitToWindow) {
|
||||
viewport.setZoom(0.5)
|
||||
viewport.render()
|
||||
} else {
|
||||
viewport.setZoom(1)
|
||||
viewport.render()
|
||||
}
|
||||
},
|
||||
voiChange(v) {
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const voiRange = { lower: 0, upper: v }
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
if (!viewport) return
|
||||
let volumeId = this.volumeId
|
||||
const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
|
||||
volumeId,
|
||||
viewport.renderingEngineId
|
||||
)
|
||||
|
||||
viewport.setProperties({ voiRange }, volumeId)
|
||||
viewportsContainingVolumeUID.forEach((vp) => {
|
||||
vp.render()
|
||||
// this.$refs[vp.id].voiModified()
|
||||
this.voiModified()
|
||||
})
|
||||
},
|
||||
setPreset(presetName) {
|
||||
this.presetName = presetName
|
||||
},
|
||||
async createImageIdsAndCacheMetaData(obj) {
|
||||
this.loading = true
|
||||
await createImageIdsAndCacheMetaData({
|
||||
modality: obj.Modality,
|
||||
imageIds: obj.ImageIds
|
||||
})
|
||||
this.loading = false
|
||||
},
|
||||
async setSeriesInfo(obj) {
|
||||
try {
|
||||
let data = obj
|
||||
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) {
|
||||
data.SliceIndex = this.series.SliceIndex
|
||||
}
|
||||
this.series = {}
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
this.volumeId = data.SeriesInstanceUid
|
||||
this.series = { ...data }
|
||||
viewport
|
||||
.setVolumes([{
|
||||
volumeId: this.volumeId, callback: (r) => {
|
||||
if (this.series.Modality === 'PT') {
|
||||
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
||||
} else {
|
||||
setCtTransferFunctionForVolumeActor(r)
|
||||
}
|
||||
console.log("渲染成功")
|
||||
}
|
||||
}])
|
||||
viewport.render()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
},
|
||||
cornerstoneToolsMouseMove(e) {
|
||||
const { currentPoints } = e.detail
|
||||
const worldPoint = currentPoints.world
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const imageData = viewport.getImageData()
|
||||
if (!imageData) return
|
||||
const index = imageData.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
|
||||
},
|
||||
toggleTask(evt, visitTaskNum, i) {
|
||||
this.$emit('activeViewport', this.viewportIndex)
|
||||
const num = visitTaskNum + i
|
||||
if (num >= 0 && num <= this.taskInfo.VisitNum) {
|
||||
this.$emit('toggleTaskByViewport', { series: this.series, visitTaskNum: num })
|
||||
}
|
||||
|
||||
evt.stopImmediatePropagation()
|
||||
evt.stopPropagation()
|
||||
evt.preventDefault()
|
||||
},
|
||||
viewCD(taskId) {
|
||||
this.$emit('previewCD', taskId)
|
||||
},
|
||||
setWwwcIdx(idx) {
|
||||
this.wwwcIdx = idx
|
||||
},
|
||||
clickSlider(e) {
|
||||
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
|
||||
this.sliderInfo.height = height
|
||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(
|
||||
this.viewportId
|
||||
)
|
||||
// viewport.setImageIdIndex(sliceIdx)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
|
||||
viewport.render()
|
||||
},
|
||||
sliderMouseup(e) {
|
||||
this.sliderInfo.isMove = false
|
||||
this.$emit('contentMouseup', e)
|
||||
},
|
||||
sliderMousedown(e) {
|
||||
const 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
|
||||
const delta = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
|
||||
const boxHeight = this.$refs['sliderBox'].clientHeight
|
||||
if (delta < 0) return
|
||||
if (delta > boxHeight) return
|
||||
const height = delta * 100 / boxHeight
|
||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
||||
this.sliderInfo.height = height
|
||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
const viewport = renderingEngine.getViewport(
|
||||
this.viewportId
|
||||
)
|
||||
// viewport.setImageIdIndex(sliceIdx)
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
|
||||
viewport.render()
|
||||
},
|
||||
sliderMouseleave(e) {
|
||||
if (!this.sliderInfo.isMove) return
|
||||
this.sliderInfo.isMove = false
|
||||
},
|
||||
rotate(angle) {
|
||||
let renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||
let viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const camera = viewport.getCamera()
|
||||
const { viewUp, position, focalPoint } = camera
|
||||
const [cx, cy, cz] = focalPoint
|
||||
const [ax, ay, az] = [0, 0, 1]
|
||||
const newPosition = [0, 0, 0]
|
||||
const newFocalPoint = [0, 0, 0]
|
||||
const newViewUp = [0, 0, 0]
|
||||
|
||||
const transform = mat4.identity(new Float32Array(16))
|
||||
mat4.translate(transform, transform, [cx, cy, cz])
|
||||
mat4.rotate(transform, transform, angle, [ax, ay, az])
|
||||
mat4.translate(transform, transform, [-cx, -cy, -cz])
|
||||
vec3.transformMat4(newPosition, position, transform)
|
||||
vec3.transformMat4(newFocalPoint, focalPoint, transform)
|
||||
|
||||
mat4.identity(transform)
|
||||
mat4.rotate(transform, transform, angle, [ax, ay, az])
|
||||
vec3.transformMat4(newViewUp, viewUp, transform)
|
||||
|
||||
viewport.setCamera({
|
||||
position: newPosition,
|
||||
viewUp: newViewUp,
|
||||
focalPoint: newFocalPoint
|
||||
})
|
||||
|
||||
viewport.render()
|
||||
},
|
||||
clickRotate(e) {
|
||||
// console.log('clickRotate')
|
||||
const container = document.getElementById('rotateBar')
|
||||
const containerWidth = container.offsetWidth
|
||||
const slider = document.getElementById('rotateSlider')
|
||||
const sliderWidth = slider.offsetWidth
|
||||
const x = Math.trunc(e.offsetX)
|
||||
const deltaX = x - this.rotateBarLeft
|
||||
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
|
||||
this.rotate(angle)
|
||||
this.rotateBarLeft = x
|
||||
},
|
||||
preventDefault(e) {
|
||||
e.stopImmediatePropagation()
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
NSTip() {
|
||||
return `NS: ${this.$store.state.trials.uploadTip}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.viewport-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
cursor: default !important;
|
||||
|
||||
.left-top-text {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
// color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
|
||||
.cd-info {
|
||||
// color: #ddd;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.subject-info {
|
||||
color: #f44336;
|
||||
padding: 5px 0px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.top-center-tool {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 5px;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
|
||||
.toggle-visit-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.arrw_icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #3f3f3f;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
.arrow_text {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
background-color: #00000057;
|
||||
color: #fff;
|
||||
padding: 0 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-top-text {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
// color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.left-bottom-text {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
bottom: 5px;
|
||||
// color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.right-bottom-text {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
// color: #ddd;
|
||||
z-index: 1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.right-slider-box {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
height: calc(100% - 140px);
|
||||
transform: translateY(-50%);
|
||||
top: calc(50% - 30px);
|
||||
width: 10px;
|
||||
background: #333;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.right-slider-box:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -20px;
|
||||
left: 0;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.slider {
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: #9e9e9e;
|
||||
cursor: move
|
||||
}
|
||||
|
||||
.orientation-top {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 30px;
|
||||
color: #f44336;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-bottom {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 15px;
|
||||
color: #f44336;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-left {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 15px;
|
||||
color: #f44336;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orientation-right {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
color: #f44336;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.color_bar {
|
||||
position: absolute;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: right;
|
||||
left: -150px;
|
||||
top: 30%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.rotate_slider_box {
|
||||
position: absolute;
|
||||
width: 380px;
|
||||
height: 10px;
|
||||
bottom: 5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #333;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
|
||||
.box {
|
||||
z-index: 10;
|
||||
background: #9e9e9e;
|
||||
height: 100%;
|
||||
width: 20px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
cursor: move
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue