自定义dicom阅片添加mpr
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-12-02 16:28:42 +08:00
parent 653d3c6228
commit cc5ac4c2a9
4 changed files with 877 additions and 114 deletions

1
src/icons/svg/mpr.svg Normal file
View File

@ -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

View File

@ -95,34 +95,20 @@
<script> <script>
// import * as cornerstonejs from '@cornerstonejs/core' // import * as cornerstonejs from '@cornerstonejs/core'
import { import {
// RenderingEngine,
Enums, Enums,
// imageLoader,
metaData, metaData,
getRenderingEngine, getRenderingEngine,
volumeLoader, cache,
// eventTarget,
utilities as csUtils, utilities as csUtils,
} from '@cornerstonejs/core' } from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools' import * as cornerstoneTools from '@cornerstonejs/tools'
import { utilities as cstUtils } from '@cornerstonejs/tools';
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'; import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps' import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData' 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 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' 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 { BlendModes, OrientationAxis } = Enums;
const { MouseBindings } = csToolsEnums const { getColormap } = csUtils.colormap;
const { getColormap, getColormapNames } = csUtils.colormap;
const { ViewportColorbar } = cstUtils.voi.colorbar;
const { ColorbarRangeTextPosition } = cstUtils.voi.colorbar.Enums;
import { vec3, mat4 } from 'gl-matrix' import { vec3, mat4 } from 'gl-matrix'
export default { export default {
name: 'ImageViewport', name: 'ImageViewport',
@ -360,7 +346,7 @@ export default {
const viewport = renderingEngine.getViewport(this.viewportId) const viewport = renderingEngine.getViewport(this.viewportId)
if (isPlay) { if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: false }) cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
} else { } else {
cornerstoneTools.utilities.cine.stopClip(viewport.element) cornerstoneTools.utilities.cine.stopClip(viewport.element)
} }
@ -435,27 +421,6 @@ export default {
colorMap = getColormap(presetName) colorMap = getColormap(presetName)
} }
if (!colorMap) return false 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 rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById(`colorBar_Canvas_${this.viewportId}`) const canvas = document.getElementById(`colorBar_Canvas_${this.viewportId}`)
if (!canvas) return false if (!canvas) return false
@ -512,7 +477,7 @@ export default {
}, },
async setSeriesInfo(obj, option = {}) { async setSeriesInfo(obj, option = {}) {
try { try {
let { data, volumeId } = obj let { data } = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) { if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) {
data.SliceIndex = this.series.SliceIndex data.SliceIndex = this.series.SliceIndex
} }
@ -523,37 +488,32 @@ export default {
this.isMip = isMip; this.isMip = isMip;
const renderingEngine = getRenderingEngine(this.renderingEngineId) const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId) const viewport = renderingEngine.getViewport(this.viewportId)
this.volumeId = data.SeriesInstanceUid
if (this.isFusion) { if (this.isFusion) {
this.$nextTick(() => { this.$nextTick(() => {
this.renderColorBar(this.presetName) this.renderColorBar(this.presetName)
}) })
let { ct, data, ctVolumeId, ptVolumeId, fusionVolumeId } = obj this.ptVolumeId = `fusion_${this.volumeId}`
let { ct, data } = obj
this.series = { ...data } this.series = { ...data }
this.ctSeries = { ...ct } this.ctSeries = { ...ct }
this.petSeries = { ...data } 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 await viewport
.setVolumes([ .setVolumes([
{ {
volumeId: this.ptVolumeId, callback: (r) => { volumeId: this.volumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r) setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功"); console.log("融合pet渲染成功");
} }
}, },
{ {
volumeId: ctVolumeId, callback: (r) => { volumeId: ct.SeriesInstanceUid, callback: (r) => {
setCtTransferFunctionForVolumeActor(r) setCtTransferFunctionForVolumeActor(r)
console.log("融合ct渲染成功") console.log("融合ct渲染成功")
} }
}, },
{ {
volumeId: this.volumeId, callback: (r) => { volumeId: this.ptVolumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r) setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功"); console.log("融合pet渲染成功");
} }
@ -565,9 +525,8 @@ export default {
}) })
} else { } else {
this.series = { ...data } this.series = { ...data }
this.volumeId = volumeId
if (this.isMip) { if (this.isMip) {
let { volume } = obj let volume = cache.getVolume(this.volumeId)
const ptVolumeDimensions = volume.dimensions; const ptVolumeDimensions = volume.dimensions;
const slabThickness = Math.sqrt( const slabThickness = Math.sqrt(
ptVolumeDimensions[0] * ptVolumeDimensions[0] + ptVolumeDimensions[0] * ptVolumeDimensions[0] +

View File

@ -160,6 +160,11 @@
<svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" /> <svg-icon v-if="forceFitToWindow" icon-class="fitToWindow" class="svg-icon" />
<svg-icon v-else icon-class="fitToImage" class="svg-icon" /> <svg-icon v-else icon-class="fitToImage" class="svg-icon" />
</div> </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')" <div v-if="readingTool === 2" class="tool-item" :title="$t('trials:lugano:button:fusion')"
@click.prevent="openFusion"> @click.prevent="openFusion">
@ -289,10 +294,10 @@
<!-- viewports --> <!-- viewports -->
<div class="viewports-wrapper"> <div class="viewports-wrapper">
<div ref="container" class="grid-container"> <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}`" <div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
:style="cellStyle" :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)"> @dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
<Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`" <Viewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index" :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
@ -300,6 +305,18 @@
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" /> @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
</div> </div>
</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' : '']" <div v-if="readingTool === 2" :class="['viewports-box', !isFusion ? 'viewports-box-down' : '']"
:style="gridStyle"> :style="gridStyle">
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`" <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 StudyList from './StudyList'
import Viewport from './Viewport' import Viewport from './Viewport'
import PetCtViewport from './PetCtViewport' import PetCtViewport from './PetCtViewport'
import VolumeViewport from './VolumeViewport'
import mRecisit from './mRecist/QuestionList' import mRecisit from './mRecist/QuestionList'
import recisit from './Recist/QuestionList' import recisit from './Recist/QuestionList'
import customizeQuestionList from './customize/QuestionList' import customizeQuestionList from './customize/QuestionList'
@ -534,6 +552,7 @@ export default {
StudyList, StudyList,
Viewport, Viewport,
PetCtViewport, PetCtViewport,
VolumeViewport,
mRecisit, mRecisit,
recisit, recisit,
customizeQuestionList, customizeQuestionList,
@ -622,7 +641,6 @@ export default {
fusionVisible: false, fusionVisible: false,
isFusion: false, isFusion: false,
studyList: [], studyList: [],
volumeData: {},
fusionSerieId: {}, fusionSerieId: {},
loadingText: null, loadingText: null,
toolNames: ['Length', 'Bidirectional', 'RectangleROI', 'ArrowAnnotate', 'CircleROI', 'Eraser'], toolNames: ['Length', 'Bidirectional', 'RectangleROI', 'ArrowAnnotate', 'CircleROI', 'Eraser'],
@ -645,7 +663,9 @@ export default {
annotation: null annotation: null
}, },
ManualsClose: false ManualsClose: false,
isMPR: false
} }
}, },
computed: { computed: {
@ -670,7 +690,7 @@ export default {
return Array(this.rows * this.cols).fill(0) return Array(this.rows * this.cols).fill(0)
}, },
viewportKey() { viewportKey() {
return this.isFusion ? 'viewport-fusion' : 'viewport' return this.isMPR ? 'viewport-volume' : this.isFusion ? 'viewport-fusion' : 'viewport'
}, },
openManuals() { openManuals() {
return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
@ -1021,11 +1041,13 @@ export default {
if (!renderingEngine) { if (!renderingEngine) {
renderingEngine = new RenderingEngine(renderingEngineId) 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 element1 = this.$refs['viewport-0'][0].$el
const element2 = this.$refs['viewport-1'][0].$el const element2 = this.$refs['viewport-1'][0].$el
const element3 = this.$refs['viewport-2'][0].$el const element3 = this.$refs['viewport-2'][0].$el
const element4 = this.$refs['viewport-3'][0].$el const element4 = this.$refs['viewport-3'][0].$el
console.log(Enums.OrientationAxis)
let viewportInputArray = [ let viewportInputArray = [
{ {
viewportId: 'viewport-0', viewportId: 'viewport-0',
@ -1048,8 +1070,40 @@ export default {
element: element4 element: element4
} }
] ]
let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3'] if (this.criterionType === 0 && this.readingTool === 0) {
const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3'] 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) { if (this.readingTool === 2) {
const fusionElement1 = this.$refs['viewport-fusion-0'][0].$el const fusionElement1 = this.$refs['viewport-fusion-0'][0].$el
const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el const fusionElement2 = this.$refs['viewport-fusion-1'][0].$el
@ -1450,7 +1504,9 @@ export default {
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) { if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const referencedImageId = annotation.metadata.referencedImageId const referencedImageId = annotation.metadata.referencedImageId
console.log(annotation, 'annotation')
const params = this.getInstanceInfo(referencedImageId) const params = this.getInstanceInfo(referencedImageId)
console.log(params, 'params')
annotation.visitTaskId = series.TaskInfo.VisitTaskId annotation.visitTaskId = series.TaskInfo.VisitTaskId
annotation.studyId = series.StudyId annotation.studyId = series.StudyId
annotation.seriesId = series.Id annotation.seriesId = series.Id
@ -2033,7 +2089,10 @@ export default {
}, },
setToolsPassive() { setToolsPassive() {
if (!this.activeTool) return 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 => { toolGroupIds.forEach(toolGroupId => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolPassive(this.activeTool) toolGroup.setToolPassive(this.activeTool)
@ -2042,7 +2101,10 @@ export default {
}, },
setToolEnabled() { setToolEnabled() {
if (!this.activeTool) return 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 => { toolGroupIds.forEach(toolGroupId => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled(this.activeTool) toolGroup.setToolEnabled(this.activeTool)
@ -2381,7 +2443,10 @@ export default {
}) })
} }
const renderingEngine = getRenderingEngine(renderingEngineId) 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) renderingEngine.renderViewports(viewportIds)
} }
event.stopImmediatePropagation() event.stopImmediatePropagation()
@ -2413,10 +2478,11 @@ export default {
} }
}, },
async toggleTask(taskInfo, taskIndex) { async toggleTask(taskInfo, taskIndex) {
if (this.isFusion) { if (this.isFusion || this.isMPR) {
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack')) const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
if (!confirm) return false if (!confirm) return false
this.isFusion = false this.isFusion = false
this.isMPR = false
this.fullScreenIndex = null this.fullScreenIndex = null
this.setToolsPassive() this.setToolsPassive()
} }
@ -2485,7 +2551,7 @@ export default {
this.setToolsPassive() this.setToolsPassive()
}, },
async activeSeries(obj) { async activeSeries(obj) {
if (this.isFusion) { if (this.isFusion || this.isMPR) {
const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack')) const confirm = await this.$confirm(this.$t('trials:reading:confirm:changeStack'))
if (!confirm) return false if (!confirm) return false
this.isFusion = false this.isFusion = false
@ -2494,6 +2560,7 @@ export default {
this.cols = 1 this.cols = 1
this.activeViewportIndex = 0 this.activeViewportIndex = 0
this.fullScreenIndex = null this.fullScreenIndex = null
this.isMPR = false
return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj) return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
} }
if (!obj.IsDicom) { if (!obj.IsDicom) {
@ -2863,6 +2930,7 @@ export default {
}, },
getInstanceInfo(imageId) { getInstanceInfo(imageId) {
const params = {} const params = {}
if (imageId) {
const searchParams = new URLSearchParams(imageId.split('?')[1]) const searchParams = new URLSearchParams(imageId.split('?')[1])
for (const [key, value] of searchParams.entries()) { for (const [key, value] of searchParams.entries()) {
params[key] = value params[key] = value
@ -2870,6 +2938,7 @@ export default {
if (isNaN(params.frame)) { if (isNaN(params.frame)) {
params.frame = null params.frame = null
} }
}
return params return params
}, },
// //
@ -3021,6 +3090,25 @@ export default {
this.$refs[id][0].voiChange(v) 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) { async handleFusion(data) {
try { try {
this.fusionVisible = false this.fusionVisible = false
@ -3030,9 +3118,6 @@ export default {
const { ct, pt } = data const { ct, pt } = data
this.loading = true this.loading = true
this.loadingText = this.$t('trials:lugano:message:loadVolumes') 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 = [] this.renderedTaskIds = []
if (this.verifyFusionData(ct, pt)) { if (this.verifyFusionData(ct, pt)) {
this.loading = false this.loading = false
@ -3056,21 +3141,14 @@ export default {
this.loading = false this.loading = false
this.loadingText = null this.loadingText = null
const ctData = { const ctData = {
data: ct, data: ct
volumeId: this.volumeData[ct.SeriesInstanceUid].volumeId
} }
const ptData = { const ptData = {
data: pt, data: pt
volumeId: this.volumeData[pt.SeriesInstanceUid].volumeId,
volume: this.volumeData[pt.SeriesInstanceUid].volume
} }
const fusionData = { const fusionData = {
ct, ct,
data: pt, 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-0`][0].setSeriesInfo(ct)
this.$refs[`viewport-1`][0].setSeriesInfo(pt) this.$refs[`viewport-1`][0].setSeriesInfo(pt)
@ -3092,43 +3170,22 @@ export default {
} }
}, },
verifyFusionData(ct, pt) { 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 true
} }
return false return false
}, },
async getVolume(serie, isFusion = false) { async getVolume(serie, isFusion = false) {
return new Promise(async res => { return new Promise(async res => {
let volumeId = null; let volume = null let volumeId = `${isFusion ? 'fusion_' : ''}` + serie.SeriesInstanceUid;
const key = isFusion ? `fusion_${serie.SeriesInstanceUid}` : serie.SeriesInstanceUid let volume = null;
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)) { if (cache.getVolume(volumeId)) {
volume = cache.getVolume(volumeId) volume = cache.getVolume(volumeId)
} else { } else {
await this.$refs[`viewport-fusion-0`][0].createImageIdsAndCacheMetaData(serie) await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds }) volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
volume.load() volume.load()
} }
this.volumeData[key] = {}
this.volumeData[key].volumeId = volumeId
this.volumeData[key].volume = volume
} else {
volumeId = this.volumeData[key].volumeId
volume = this.volumeData[key].volume
}
res({ volumeId, volume }) res({ volumeId, volume })
}) })
}, },
@ -3506,13 +3563,17 @@ export default {
.grid-cell { .grid-cell {
border: 1px dashed #ccc; border: 1px dashed #ccc;
;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 1; z-index: 1;
} }
.grid-cell-3 {
grid-column: 2 / 3;
grid-row: 1 / 3;
}
.cell_active { .cell_active {
border-color: #fafa00 !important; border-color: #fafa00 !important;
} }

View File

@ -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>