<template>
  <div ref="viewport-fusion" 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: series.Modality === 'PT' || series.Modality === 'NM' || isMip ? '#666' : '#ddd' }">
    <div v-if="isFusion" class="opacity-slider-wrapper" @mousedown.stop @mousemove.stop @mouseup.stop @wheel.stop>
      <div class="slider-title">{{ Math.round(fusionOpacity * 100) }}%</div>
      <input type="range" min="0" max="1" step="0.01" v-model.number="fusionOpacity" @input="applyFusionOpacity"
        class="opacity-slider" />
    </div>
    <div v-if="series && taskInfo" class="left-top-text">
      <div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" 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 v-if="!isMip && !isFusion">Series: #{{ series.SeriesNumber }}</div>
      <div v-if="series.Stack && !isMip">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total || series.Stack.length}` }}</div>
      <div v-if="!isMip && !isFusion">{{ series.Modality }}</div>
      <div v-if="isFusion">{{ series.Modality }} / {{ ctSeries.Modality }}</div>
      <div v-if="isMip">MIP</div>
    </div>
    <div v-if="series && !isMip && !isFusion" class="right-top-text">
      <div>{{ series.Description }}</div>
    </div>
    <!-- <div v-if="isFusion && !isMip" class="fusion-order-toggle" @mousedown.stop @mouseup.stop
      @click.stop="toggleFusionRenderOrder">
      {{ fusionCtOnTop ? `${ctSeries.Modality}/${series.Modality}` : `${series.Modality}/${ctSeries.Modality}` }}
    </div> -->
    <div v-if="series" class="left-bottom-text">
      <div v-show="mousePosition.index.length > 0 && !isMip">
        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 && !isMip && !isFusion">
        HU: {{ mousePosition.value }}
      </div>
      <div v-else-if="(series.Modality === 'PT' && mousePosition.value) && !isMip && !isFusion">
        SUVbw(g/ml): {{ digitPlaces === -1 ? mousePosition.value.toFixed(3) : mousePosition.value.toFixed(digitPlaces)
        }}
      </div>
      <div v-else-if="mousePosition.value && !isMip && !isFusion">
        Density: {{ mousePosition.value }}
      </div>
      <div v-show="imageInfo.size && !isMip && !isFusion">
        W*H: {{ imageInfo.size }}
      </div>
    </div>
    <div v-if="series && !isMip" class="right-bottom-text">
      <div v-show="imageInfo.location && !isFusion">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm`
      }}</div>
      <div v-show="series.SliceThickness && !isFusion">Slice Thickness: {{
        `${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
      }}</div>
      <div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
    </div>
    <div class="orientation-top" v-if="!isMip">
      {{ markers.top }}
    </div>
    <div class="orientation-right" v-if="!isMip">
      {{ markers.right }}
    </div>

    <div class="orientation-bottom" v-if="!isMip">
      {{ markers.bottom }}
    </div>
    <div class="orientation-left" v-if="!isMip">
      {{ markers.left }}
    </div>
    <div v-if="isFusion" class="color_bar">
      <canvas :id="`colorBar_Canvas_${viewportId}`" />
      <!-- <div id="colorBar_Canvas" />  -->
    </div>
    <div v-if="isMip" id="rotateBar" ref="rotateBar" class="rotate_slider_box" @click.stop="clickRotate($event)">
      <div id="rotateSlider" :style="{ left: rotateBarLeft + 'px' }" class="box" @click.stop.prevent="() => { return }"
        @mousedown.stop="rotateBarMousedown($event)" />
    </div>
    <div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)" v-show="!isMip">
      <div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
        @mousedown.stop="sliderMousedown($event)" />
    </div>
    <div
      v-if="annotationContextMenu.visible"
      ref="annotation-context-menu"
      class="annotation-context-menu"
      :style="{ left: `${annotationContextMenu.x}px`, top: `${annotationContextMenu.y}px` }"
      @mousedown.stop
      @mouseup.stop
      @click.stop
      @contextmenu.prevent
    >
      <div class="annotation-context-menu__item" @click.stop="copyCircleAnnotation()">
        <!-- 复制 -->
        {{ $t('trials:reading:menu:copy') }}
      </div>
    </div>
  </div>
</template>
<script>
// import * as cornerstonejs from '@cornerstonejs/core'
import {
  Enums,
  metaData,
  getRenderingEngine,
  cache,
  utilities as csUtils,
} from '@cornerstonejs/core'
import * as cornerstoneTools 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, { setCtMappingRange } 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 {
  setMipTransferFunctionForVolumeActor,
  setPetTransferFunctionForVolumeActor
} from './helpers/index.js'
import setNmFusionColorMapTransferFunctionForVolumeActor from './helpers/setNmFusionColorMapTransferFunctionForVolumeActor'
const { BlendModes, OrientationAxis } = Enums; 
const { getColormap } = csUtils.colormap;
import { vec3, mat4 } from 'gl-matrix'
const DEFAULT_COLOR_MAP_UPPER = 6

function getVoiValue(value) {
  if (Array.isArray(value)) {
    return Number(value[0])
  }
  return Number(value)
}

export default {
  name: 'ImageViewport',
  props: {
    renderingEngineId: {
      type: String,
      required: true
    },
    viewportId: {
      type: String,
      required: true
    },
    viewportIndex: {
      type: Number,
      required: true
    },
    activeTool: {
      type: String,
      default: ''
    },
  },
  data() {
    return {
      element: '',
      series: {},
      ctSeries: {},
      petSeries: {},
      isFusion: false,
      isMip: false,
      fusionCtOnTop: false,
      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: null
      },
      digitPlaces: 2,
      orientationMarkers: [],
      originalMarkers: [],
      markers: { top: '', right: '', bottom: '', left: '' },
      playClipState: false,
      clipFramesPerSecond: null,
      wwwcIdx: 2,
      presetName: '',
      volumeId: null,
      defaultWindowLevel: {},
      Colorbar: null,
      rotateAngle: 0,
      rotateBarLeft: 0,
      rotateBarInfo: {
        initX: null,
        initLeft: null,
        isMove: false
      },
      ptVolumeId: null,
      loading: false,
      Colorbar: null,
      fusionOpacity: 0.95,
      topFusionVolumeActor: null,
      currentVoiUpper: null,
      annotationContextMenu: {
        visible: false,
        x: 0,
        y: 0,
        annotation: null,
      }
    }
  },
  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-fusion']
      const resizeObserver = new ResizeObserver(() => {
        const renderingEngine = getRenderingEngine(this.renderingEngineId)
        if (renderingEngine) {
          renderingEngine.resize(true, false)
        }
      })
      this.element.oncontextmenu = this.handleElementContextMenu
      // resizeObserver.observe(this.element)
      this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
      this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)

      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.handleDocumentMouseUp)
      document.addEventListener('mousemove', this.handleDocumentMouseMove)
      document.addEventListener('mousedown', this.handleDocumentMouseDown)
      // console.log(cornerstoneTools)
      // element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
    },
    getViewportInstance() {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      return renderingEngine?.getViewport(this.viewportId)
    },
    hideAnnotationContextMenu() {
      if (!this.annotationContextMenu.visible) return
      this.annotationContextMenu.visible = false
      this.annotationContextMenu.annotation = null
    },
    handleDocumentMouseDown(e) {
      const menu = this.$refs['annotation-context-menu']
      if (menu && menu.contains(e.target)) return
      this.hideAnnotationContextMenu()
    },
    handleElementContextMenu(e) {
      e.preventDefault()
      e.stopPropagation()
      if (this.series.Modality !== 'NM') return false
      const annotation = this.getCircleAnnotationFromContextMenuEvent(e)
      if (!annotation) {
        this.hideAnnotationContextMenu()
        return false
      }

      if (cornerstoneTools.annotation.locking.isAnnotationLocked(annotation.annotationUID)) {
        this.hideAnnotationContextMenu()
        return false
      }

      this.$emit('activeViewport', this.viewportIndex)

      const { x, y } = this.getContextMenuPosition(e)
      this.annotationContextMenu.visible = true
      this.annotationContextMenu.x = x
      this.annotationContextMenu.y = y
      this.annotationContextMenu.annotation = annotation
      return false
    },
    getContextMenuPosition(e) {
      const rect = this.element?.getBoundingClientRect?.()
      if (!rect) {
        return { x: 0, y: 0 }
      }

      const menuWidth = 128
      const menuHeight = 40
      const padding = 8
      const maxX = Math.max(padding, rect.width - menuWidth - padding)
      const maxY = Math.max(padding, rect.height - menuHeight - padding)

      return {
        x: Math.min(Math.max(e.clientX - rect.left, padding), maxX),
        y: Math.min(Math.max(e.clientY - rect.top, padding), maxY),
      }
    },
    getInstanceInfo(imageId) {
      const params = {}
      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
    },
    getCircleAnnotationFromContextMenuEvent(e) {
      const viewport = this.getViewportInstance()
      if (!viewport || !this.element) return null

      const rect = this.element.getBoundingClientRect()
      const canvasPoint = [e.clientX - rect.left, e.clientY - rect.top]
      const currentImageId = viewport.getCurrentImageId?.()
      const currentInstanceId = this.getInstanceInfo(currentImageId).instanceId
      const toolNames = ['CircleROI', 'FixedRadiusCircleROI']
      for (const toolName of toolNames) {
        const annotations = cornerstoneTools.annotation.state.getAnnotations(toolName, this.element) || []
        const selectedAnnotation = annotations.find((annotation) => {
          if (!annotation?.highlighted) return false
          if (!annotation?.data?.handles?.points?.length) return false
          const annotationInstanceId = annotation.instanceId || this.getInstanceInfo(annotation.metadata?.referencedImageId).instanceId
          if (currentInstanceId && annotationInstanceId && annotationInstanceId !== currentInstanceId) return false

          const hitInfo = this.getCircleAnnotationHitInfo(annotation, canvasPoint, viewport)
          return hitInfo.hit
        })

        if (selectedAnnotation) {
          return selectedAnnotation
        }
      }

      return null
    },
    getCircleAnnotationHitInfo(annotation, canvasPoint, viewport) {
      const points = annotation?.data?.handles?.points || []
      if (points.length < 2) {
        return { hit: false, score: null }
      }

      const centerCanvas = viewport.worldToCanvas(points[0]) // 圆心
      const edgeCanvas = viewport.worldToCanvas(points[1]) // 边缘点
      const radius = this.distanceToPoint(centerCanvas, edgeCanvas)
      if (radius <= 0) {
        return { hit: false, score: null }
      }

      const dx = canvasPoint[0] - centerCanvas[0]
      const dy = canvasPoint[1] - centerCanvas[1]
      const distance = (dx * dx + dy * dy) / (radius * radius)
      const hit = distance <= 1.2

      return {
        hit,
        score: hit ? distance : null,
      }
    },
    createCopiedCachedStats(sourceCachedStats = {}) {
      const cloned = JSON.parse(JSON.stringify(sourceCachedStats || {}))
      const keys = Object.keys(cloned)

      if (!keys.length) {
        const fallbackKey = this.isFusion ? (this.ptVolumeId || this.volumeId) : this.volumeId
        if (fallbackKey) {
          cloned[fallbackKey] = {}
        }
      }

      Object.keys(cloned).forEach((key) => {
        cloned[key] = {
          ...cloned[key],
          area: null,
          count: null,
          isEmptyArea: null,
          max: null,
          mean: null,
          min: null,
          perimeter: null,
          pointsInShape: null,
          radius: null,
          statsArray: [],
          stdDev: null,
          total: null,
        }
      })

      return cloned
    },
    getCopySide(sourceCenterCanvas, radius) {
      const canvasWidth = this.element.clientWidth || 0
      const padding = Math.max(radius * 0.5, 8) 
      const requiredSpace = Math.max(radius * 2.1, 16) + padding // 放置新标注所需要的空间
      const leftSpace = sourceCenterCanvas[0] - padding
      const rightSpace = canvasWidth - sourceCenterCanvas[0] - padding

      const canPlaceLeft = leftSpace >= requiredSpace
      const canPlaceRight = rightSpace >= requiredSpace

      if (canPlaceLeft && canPlaceRight) {
        return rightSpace >= leftSpace ? 'right' : 'left'
      }

      if (canPlaceRight) {
        return 'right'
      }

      if (canPlaceLeft) {
        return 'left'
      }

      return rightSpace >= leftSpace ? 'right' : 'left'
    },
    buildCopiedCircleAnnotation(sourceAnnotation, side) {
      const viewport = this.getViewportInstance()
      if (!viewport) return null

      const points = sourceAnnotation.data.handles.points || []
      if (points.length < 2) return null

      const sourceCenterCanvas = viewport.worldToCanvas(points[0])
      const sourceEdgeCanvas = viewport.worldToCanvas(points[1])

      const radiusVector = [
        sourceEdgeCanvas[0] - sourceCenterCanvas[0],
        sourceEdgeCanvas[1] - sourceCenterCanvas[1],
      ]
      const radius = this.distanceToPoint(sourceCenterCanvas, sourceEdgeCanvas)
      if (radius <= 0) return null

      const offsetDirection = side === 'left' ? -1 : 1
      const offsetDistance = Math.max(radius * 2.1, 16)
      const padding = Math.max(radius * 0.5, 8)
      const canvasWidth = this.element?.clientWidth || 0
      const canvasHeight = this.element?.clientHeight || 0
      const targetCenterCanvas = [
        sourceCenterCanvas[0] + offsetDirection * offsetDistance,
        sourceCenterCanvas[1],
      ]

      if (canvasWidth > 0) {
        targetCenterCanvas[0] = Math.min(Math.max(targetCenterCanvas[0], padding), canvasWidth - padding)
      }
      if (canvasHeight > 0) {
        targetCenterCanvas[1] = Math.min(Math.max(targetCenterCanvas[1], padding), canvasHeight - padding)
      }

      const targetEdgeCanvas = [
        targetCenterCanvas[0] + radiusVector[0],
        targetCenterCanvas[1] + radiusVector[1],
      ]
      const centerWorld = viewport.canvasToWorld(targetCenterCanvas)
      const edgeWorld = viewport.canvasToWorld(targetEdgeCanvas)
      const camera = viewport.getCamera()

      return {
        highlighted: true,
        invalidated: true,
        metadata: {
          toolName: sourceAnnotation.metadata?.toolName || 'CircleROI',
          viewPlaneNormal: [...(camera?.viewPlaneNormal || sourceAnnotation.metadata?.viewPlaneNormal || [])],
          viewUp: [...(camera?.viewUp || sourceAnnotation.metadata?.viewUp || [])],
          FrameOfReferenceUID: viewport.getFrameOfReferenceUID(),
          referencedImageId: sourceAnnotation.metadata?.referencedImageId || viewport.getCurrentImageId?.(),
          ...viewport.getViewReference({ points: [centerWorld] }),
        },
        data: {
          label: '',
          handles: {
            textBox: {
              hasMoved: false,
              worldPosition: [0, 0, 0],
              worldBoundingBox: {
                topLeft: [0, 0, 0],
                topRight: [0, 0, 0],
                bottomLeft: [0, 0, 0],
                bottomRight: [0, 0, 0],
              },
            },
            points: [[...centerWorld], [...edgeWorld]],
            activeHandleIndex: null,
          },
          cachedStats: this.createCopiedCachedStats(sourceAnnotation.data?.cachedStats),
        },
      }
    },
    copyCircleAnnotation(side) {
      const sourceAnnotation = this.annotationContextMenu.annotation
      this.hideAnnotationContextMenu()

      if (!sourceAnnotation || !this.element) return

      let targetSide = side
      if (!targetSide) {
        const viewport = this.getViewportInstance()
        const points = sourceAnnotation.data.handles.points || []
        if (!viewport || points.length < 2) return

        const sourceCenterCanvas = viewport.worldToCanvas(points[0])
        const sourceEdgeCanvas = viewport.worldToCanvas(points[1])
        if (
          !Array.isArray(sourceCenterCanvas) ||sourceCenterCanvas.length < 2 || !Array.isArray(sourceEdgeCanvas) || sourceEdgeCanvas.length < 2
        ) {
          return
        }

        const radius = this.distanceToPoint(sourceCenterCanvas, sourceEdgeCanvas)
        if (radius <= 0) return

        targetSide = this.getCopySide(sourceCenterCanvas, radius)
      }

      const annotation = this.buildCopiedCircleAnnotation(sourceAnnotation, targetSide)
      if (!annotation) return

      cornerstoneTools.annotation.state.addAnnotation(annotation, this.element)

      const viewportIdsToRender = cornerstoneTools.utilities.viewportFilters.getViewportIdsWithToolToRender(
        this.element,
        annotation.metadata.toolName
      )
      cornerstoneTools.utilities.triggerAnnotationRenderForViewportIds(viewportIdsToRender)

      setTimeout(() => {
        cornerstoneTools.annotation.state.triggerAnnotationCompleted(annotation)
      }, 0)
    },
    distanceToPoint(p1, p2) {
      return Math.sqrt(this.distanceToPointSquared(p1, p2))
    },
    distanceToPointSquared(p1, p2) {
      if (p1.length !== p2.length) {
        throw Error('Both points should have the same dimensionality')
      }
      const [x1, y1, z1 = 0] = p1
      const [x2, y2, z2 = 0] = p2
      const dx = x2 - x1
      const dy = y2 - y1
      const dz = z2 - z1
    
      return dx * dx + dy * dy + dz * dz
    },
    handleDocumentMouseUp(e) {
      this.sliderMouseup(e)
      if (this.isMip) {
        this.rotateBarMouseup(e)
      }
    },
    handleDocumentMouseMove(e) {
      this.sliderMousemove(e)
      if (this.isMip) {
        this.rotateBarMousemove(e)
      }
    },
    stackNewImage(e) {
      const { detail } = e
      if (this.series) {
        this.series.SliceIndex = detail.imageIndex
      }
      this.imageInfo.total = detail.numberOfSlices
      const maxIndex = detail.numberOfSlices > 1 ? detail.numberOfSlices - 1 : 1
      this.sliderInfo.height = detail.imageIndex * 100 / maxIndex
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      const zoom = viewport.getZoom()
      this.imageInfo.zoom = zoom.toFixed(4)
      let imageId = viewport.getCurrentImageId()
      if (imageId) {
        const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
        this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
        this.imageInfo.location = imagePlaneModule.sliceLocation
        this.getOrientationMarker()
        // this.$emit('renderAnnotations', this.series)
        let properties = viewport.getProperties()
        if (this.isFusion) {
          properties = viewport.getProperties(this.ptVolumeId)
        }

        if (properties && properties.voiRange) {
          var { lower, upper } = properties.voiRange
          const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
            lower,
            upper
          )
          this.defaultWindowLevel.windowWidth = windowWidth
          this.defaultWindowLevel.windowCenter = windowCenter
          this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
        }
        const toolGroup =
          cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
            this.viewportId,
            this.renderingEngineId
          ) || cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
        if (toolGroup) {
          toolGroup.setToolEnabled('ScaleOverlay')
        }
      }

      if (!this.isMip && viewport) {
        const width = this.element?.clientWidth
        const height = this.element?.clientHeight
        let worldPoint = null
        if (width && height && viewport.canvasToWorld) {
          worldPoint = viewport.canvasToWorld([width / 2, height / 2])
        }
        if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
          worldPoint = viewport.getCamera()?.focalPoint
        }
        if (Array.isArray(worldPoint) && worldPoint.length >= 3) {
          this.$emit('fusionSlicePointChange', {
            viewportId: this.viewportId,
            worldPoint: [...worldPoint],
          })
        }
      }

    },
    syncViewportStaticState() {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      if (!renderingEngine) return
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      this.getOrientationMarker()
      const toolGroup =
        cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
          this.viewportId,
          this.renderingEngineId
        ) || cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
      if (toolGroup) {
        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 (this.isFusion) {
        properties = viewport.getProperties(this.ptVolumeId || this.volumeId)
      }
      if (properties && properties.voiRange) {
        var { lower, upper } = properties.voiRange
        if ((!upper || upper < 1) && this.currentVoiUpper >= 1) {
          upper = this.currentVoiUpper
        } else if (upper) {
          this.currentVoiUpper = upper
        }

        if (!upper || upper < 1) return

        const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
          lower,
          upper
        )
        this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
        if (this.series.Modality === 'PT' || this.series.Modality === 'NM' || this.isFusion) {
          this.$emit('upperRangeChange', Math.round(upper))
        }
        if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) {
          this.applyFusionOpacity()
        }
      }
    },
    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) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      if (!renderingEngine) return
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport?.element) return

      const parsedFramesPerSecond = Number(framesPerSecond)
      const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
        ? parsedFramesPerSecond
        : (this.clipFramesPerSecond || 15)
      const isSameSpeedWhilePlaying = this.playClipState &&
        isPlay &&
        this.clipFramesPerSecond === nextFramesPerSecond

      if (isPlay) {
        if (isSameSpeedWhilePlaying) return

        cornerstoneTools.utilities.cine.playClip(viewport.element, {
          framesPerSecond: nextFramesPerSecond,
          loop: true,
          waitForRendered: 1
        })
        this.clipFramesPerSecond = nextFramesPerSecond
        this.playClipState = true
      } else {
        cornerstoneTools.utilities.cine.stopClip(viewport.element)
        this.clipFramesPerSecond = null
        this.playClipState = false
      }
    },
    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) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      if (this.isMip) {
        viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
        viewport.render()
        return
      }

      if (forceFitToWindow) {
        viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
        viewport.render()
        return
      }

      viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
      const canvas = viewport.getCanvas() || this.element.querySelector('canvas')
      const imageData = viewport.getImageData()?.imageData
      const dimensions = imageData?.getDimensions?.()
      const imageWidth = dimensions?.[0]
      const imageHeight = dimensions?.[1]
      const canvasWidth = canvas?.clientWidth
      const canvasHeight = canvas?.clientHeight

      if (!imageWidth || !imageHeight || !canvasWidth || !canvasHeight) {
        viewport.render()
        return
      }

      const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight)
      if (fitScale > 0) {
        viewport.setZoom(1 / fitScale)
      }

      viewport.render()
    },
    voiChange(v) {
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const voiRange = { lower: 0, upper: v }
      this.currentVoiUpper = v
      
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return
      let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId
      const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
        volumeId,
        viewport.renderingEngineId
      )

      viewport.setProperties({ voiRange }, volumeId)

      // if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) {
      //   this.applyFusionOpacity()
      // }

      viewportsContainingVolumeUID.forEach((vp) => {
        vp.render()
        // this.$refs[vp.id].voiModified()
        this.voiModified()
      })
    },
    renderColorBar(presetName) {
      var colorMap = null
      this.Colorbar = null
      if (presetName === 'hsv') {
        colorMap = vtkColorMaps.getPresetByName(presetName)
      } else {
        colorMap = getColormap(presetName)
      }
      if (!colorMap) return false
      const rgbPoints = colorMap.RGBPoints
      const canvas = document.getElementById(`colorBar_Canvas_${this.viewportId}`)
      if (!canvas) return false
      const ctx = canvas.getContext('2d')
      const canvasWidth = 160
      const canvasHeight = 5
      const rectWidth = 160
      const rectHeight = canvasHeight
      canvas.width = canvasWidth
      canvas.height = canvasHeight
      const gradient = ctx.createLinearGradient(0, 0, rectWidth, 0)
      for (let i = 0; i < rgbPoints.length; i += 4) {
        let position = 0
        if (rgbPoints[0] === -1) {
          position = (rgbPoints[i] + 1) / 2
        } else {
          position = rgbPoints[i]
        }
        const color = `rgb(${parseInt(rgbPoints[i + 1] * 255)}, ${parseInt(rgbPoints[i + 2] * 255)}, ${parseInt(rgbPoints[i + 3] * 255)})`
        gradient.addColorStop(position, color)
      }
      ctx.fillStyle = gradient
      ctx.fillRect(0, 0, rectWidth, rectHeight)
    },
    opacityChange(opacity) {
      this.fusionOpacity = opacity
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      if (this.isFusion) {
        // const ctVolumeId = this.ctSeries?.SeriesInstanceUid
        const ptVolumeId = this.ptVolumeId
        viewport.setProperties({ colormap: { opacity: Number(opacity) } }, ptVolumeId)
        // const topVolumeId = this.fusionCtOnTop ? ctVolumeId : ptVolumeId
        // const bottomVolumeId = this.fusionCtOnTop ? ptVolumeId : ctVolumeId
        // const topOpacity = Number(opacity)
        // const bottomOpacity = 1 - topOpacity
        // if (bottomVolumeId) {
        //   viewport.setProperties({ colormap: { opacity: bottomOpacity } }, bottomVolumeId)
        // }
        // if (topVolumeId) {
        //   viewport.setProperties({ colormap: { opacity: topOpacity } }, topVolumeId)
        // }
      }
      viewport.render()
    },
    applyFusionOpacity() {
      this.opacityChange(this.fusionOpacity)
    },
    setPreset(presetName) {
      this.presetName = presetName
    },
    setColorMap(presetName) {
      if (this.Colorbar) {
        this.Colorbar.activeColormapName = presetName;
      }
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId
      viewport.setProperties({ colormap: { name: presetName } }, volumeId)
      viewport.render()
    },
    async prefetchMetadataInformation(imageIdsToPrefetch) {
      let taskPromises = []
      for (let i = 0; i < imageIdsToPrefetch.length; i++) {
        taskPromises.push(cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]).promise)
        if (taskPromises.length >= 6 || i === imageIdsToPrefetch.length - 1) {
          await Promise.all(taskPromises)
          taskPromises = []
        }
      }
    },
    async createImageIdsAndCacheMetaData(obj) {
      this.loading = true
      try {
        return await createImageIdsAndCacheMetaData({
          modality: obj.Modality,
          imageIds: obj.ImageIds
        })
      } finally {
        this.loading = false
      }
    }, 
    getInitialWindowLevelFromVolumeId(volumeId) {
      if (!volumeId) return null
      const volume = cache.getVolume(volumeId)
      const imageId = volume?.imageIds?.[0]
      if (!imageId) return null

      const voiLutModule = metaData.get('voiLutModule', imageId)
      const windowCenter = getVoiValue(voiLutModule?.windowCenter)
      const windowWidth = getVoiValue(voiLutModule?.windowWidth)

      if (windowWidth <= 0) {
        return null
      }

      return {
        windowCenter,
        windowWidth,
      }
    },
    getFusionVolumes() {
      const ctVolumeId = this.ctSeries.SeriesInstanceUid
      const ptFusionVolumeId = this.ptVolumeId
      if (!ctVolumeId || !ptFusionVolumeId) {
        return []
      }

      const ctEntry = {
        volumeId: ctVolumeId,
        callback: (r) => {
          const ctWindowLevel = this.getInitialWindowLevelFromVolumeId(ctVolumeId)
          if (ctWindowLevel) {
            setCtMappingRange(ctWindowLevel.windowWidth, ctWindowLevel.windowCenter)
          }
          setCtTransferFunctionForVolumeActor({ ...r, volumeId: ctVolumeId })
          if (this.fusionCtOnTop) {
            this.topFusionVolumeActor = r.volumeActor
          }
          this.applyFusionOpacity()
          console.log("融合ct渲染成功")
        }
      }

      const ptFusionEntry = {
        volumeId: ptFusionVolumeId,
        callback: (r) => {
          if (this.series.Modality === 'NM') {
            setNmFusionColorMapTransferFunctionForVolumeActor({
              ...r,
              volumeId: ptFusionVolumeId,
            })
          } else {
            setPetColorMapTransferFunctionForVolumeActor({
              ...r,
              volumeId: ptFusionVolumeId,
            })
          }
          if (!this.fusionCtOnTop) {
            this.topFusionVolumeActor = r.volumeActor
          }
          this.applyFusionOpacity()
          console.log("融合pet渲染成功")
        }
      }
 
      const volumes = []
      // if (this.series.Modality !== 'NM') {
      //   volumes.push({
      //     volumeId: this.volumeId,
      //     callback: (r) => {
      //       console.log("融合pet渲染成功");
      //     }
      //   })
      // }
      volumes.push({
        volumeId: this.volumeId,
        callback: (r) => {
          console.log("融合pet渲染成功");
        }
      })

      if (this.fusionCtOnTop) {
        volumes.push(ptFusionEntry, ctEntry)
      } else {
        volumes.push(ctEntry, ptFusionEntry)
      }

      return volumes
    },
    async applyFusionRenderOrder() {
      if (!this.isFusion) return
      const renderingEngine = getRenderingEngine(this.renderingEngineId)
      const viewport = renderingEngine.getViewport(this.viewportId)
      if (!viewport) return

      const volumes = this.getFusionVolumes()
      if (!volumes.length) return

      const camera = viewport.getCamera()

      const savedVoiUpper = this.currentVoiUpper

      await viewport.setVolumes(volumes)
      if (camera) {
        viewport.setCamera(camera)
      }

      if (savedVoiUpper) {
        viewport.setProperties({ voiRange: { lower: 0, upper: savedVoiUpper } }, this.ptVolumeId)
      }

      viewport.render()
    },
    toggleFusionRenderOrder() {
      if (!this.isFusion) return
      this.fusionCtOnTop = !this.fusionCtOnTop
      this.applyFusionRenderOrder()
      this.$emit('upperRangeChange', Math.round(this.currentVoiUpper))
    },
    async setSeriesInfo(obj, isLocate = false, option = {}) {
      try {
        let { data } = obj
        if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate) {
          data.SliceIndex = this.series.SliceIndex
        }
        const renderingEngine = getRenderingEngine(this.renderingEngineId)
        const viewport = renderingEngine.getViewport(this.viewportId)
        if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
        this.volumeId = data.SeriesInstanceUid
        this.ptVolumeId = null
        this.currentVoiUpper = null
        this.series = {}
        this.topFusionVolumeActor = null
        let { isFusion, isMip, colorMap } = option
        this.isFusion = isFusion;
        this.isMip = isMip;
        if (this.isFusion) {
          this.fusionCtOnTop = false
          this.$nextTick(() => {
            this.renderColorBar(this.presetName)
          })
          this.ptVolumeId = `fusion_${this.volumeId}`
          this.currentVoiUpper = DEFAULT_COLOR_MAP_UPPER
          let { ct, data } = obj
          this.series = { ...data }
          this.ctSeries = { ...ct }



          await viewport.setVolumes(this.getFusionVolumes())
        } else {
          this.series = { ...data }
          if (this.isMip) {
            this.currentVoiUpper = DEFAULT_COLOR_MAP_UPPER
            let volume = cache.getVolume(this.volumeId)
            const ptVolumeDimensions = volume.dimensions
            const slabThickness = Math.sqrt(
              ptVolumeDimensions[0] * ptVolumeDimensions[0] +
              ptVolumeDimensions[1] * ptVolumeDimensions[1] +
              ptVolumeDimensions[2] * ptVolumeDimensions[2]
            )

            await viewport.setVolumes([{
              volumeId: this.volumeId,
              callback: (r) => {
                if (this.series.Modality === 'NM') {
                  setMipTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
                } else {
                  setPetTransferFunctionForVolumeActor(r)
                }
                console.log("mip渲染成功")
              },
              slabThickness,
              blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
              defaultOptions: {
                orientation: OrientationAxis.CORONAL
              }
            }])

            // viewport.setBlendMode(BlendModes.MAXIMUM_INTENSITY_BLEND)
            // viewport.setSlabThickness(slabThickness)
          } else {
            viewport
              .setVolumes([{
                volumeId: this.volumeId, callback: (r) => {
                  if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
                    this.currentVoiUpper = DEFAULT_COLOR_MAP_UPPER
                    // setPetColorMapTransferFunctionForVolumeActor(r, true)
                    setPetTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
                  } else {
                    const ctWindowLevel = this.getInitialWindowLevelFromVolumeId(this.volumeId)
                    if (ctWindowLevel) {
                      setCtMappingRange(ctWindowLevel.windowWidth, ctWindowLevel.windowCenter)
                    }
                    setCtTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
                  }
                }
              }])
          }

        }
        viewport.render()
        this.syncViewportStaticState()
        if (this.currentVoiUpper > 0) {
          this.voiChange(this.currentVoiUpper)
        }
      } catch (e) {
        console.log(e)
      }

    },
    cornerstoneToolsMouseMove(e) {
      try {
        const { currentPoints } = e.detail
        const worldPoint = currentPoints.world
        const renderingEngine = getRenderingEngine(this.renderingEngineId)
        const viewport = renderingEngine.getViewport(this.viewportId)
        let referencedImageId = viewport.getCurrentImageId()
        const data = viewport.getImageData()
        if (!data || !referencedImageId) return
        const { dimensions, imageData, metadata, voxelManager } = data
        const index = imageData.worldToIndex(worldPoint)
        index[0] = Math.floor(index[0])
        index[1] = Math.floor(index[1])
        index[2] = Math.floor(index[2])
        this.mousePosition.index = index
        const modality = metadata.Modality
        let ijk = csUtils.transformWorldToIndex(imageData, worldPoint)
        ijk = vec3.round(ijk, ijk);
        if (csUtils.indexWithinDimensions(ijk, dimensions)) {
          this.isHandleOutsideImage = false
          let value = voxelManager.getAtIJKPoint(ijk)
          ijk[2] = viewport.getCurrentImageIdIndex()
          let modalityUnit
          if (modality === 'US') {
            const calibratedResults = cornerstoneTools.utilities.getCalibratedProbeUnitsAndValue(data, [ijk])
            const hasEnhancedRegionValues = calibratedResults.values.every(
              (value) => value !== null
            )
            value = (hasEnhancedRegionValues ? calibratedResults.values : value)
            modalityUnit = hasEnhancedRegionValues
              ? calibratedResults.units
              : 'raw';
          } else {
            const scalingModule = referencedImageId && metaData.get('scalingModule', referencedImageId)
            const isSuvScaled = typeof scalingModule?.suvbw === 'number'
            const pixelUnitsOptions = {
              isPreScaled: cornerstoneTools.utilities.viewport.isViewportPreScaled(viewport, referencedImageId),
              isSuvScaled: isSuvScaled,
            }
            modalityUnit = cornerstoneTools.utilities.getPixelValueUnits(
              modality,
              referencedImageId,
              pixelUnitsOptions
            );
          }
          this.mousePosition.value = value
          this.mousePosition.modalityUnit = modalityUnit
        }
      } catch(e) {
        console.log(e)
      }
    },
    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
      const totalSlices = this.imageInfo.total || this.series.Stack?.length || 1
      const maxIndex = totalSlices > 1 ? totalSlices - 1 : 0
      let sliceIdx = Math.round(maxIndex * height / 100)
      sliceIdx = sliceIdx > maxIndex ? maxIndex : 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 = this.sliderInfo.height * 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
      const totalSlices = this.imageInfo.total || this.series.Stack?.length || 1
      const maxIndex = totalSlices > 1 ? totalSlices - 1 : 0
      let sliceIdx = Math.round(maxIndex * height / 100)
      sliceIdx = sliceIdx > maxIndex ? maxIndex : 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
    },
    handletoolsMouseWheel(e) {
      const { viewportId, wheel } = e.detail
      if (this.isMip) {
        const container = document.getElementById('rotateBar')
        const slider = document.getElementById('rotateSlider')
        const containerWidth = container.offsetWidth
        const sliderWidth = slider.offsetWidth
        const maxX = containerWidth - sliderWidth
        const { direction } = wheel

        var x = Math.trunc(30 * ((containerWidth - sliderWidth) / 360))
        if (direction > 0 && (this.rotateBarLeft + x) > maxX) {
          this.rotateBarLeft = x - (containerWidth - sliderWidth - this.rotateBarLeft)
        } else if (direction < 0 && (this.rotateBarLeft < x)) {
          this.rotateBarLeft = containerWidth - (x - this.rotateBarLeft + sliderWidth)
        } else {
          this.rotateBarLeft = x * direction + this.rotateBarLeft
        }
      }
    },
    rotateBarMouseup(e) {
      this.rotateBarInfo.isMove = false
    },
    rotateBarMousemove(e) {
      // 滚动旋转
      if (!this.rotateBarInfo.isMove) return
      const container = document.getElementById('rotateBar')
      const slider = document.getElementById('rotateSlider')
      const containerWidth = container.offsetWidth
      const sliderWidth = slider.offsetWidth
      let x = Math.trunc(e.clientX - this.rotateBarInfo.initX + this.rotateBarInfo.initLeft)
      if (x < 0) x = 0
      if (x > containerWidth - sliderWidth) x = containerWidth - sliderWidth
      const deltaX = x - this.rotateBarLeft
      const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
      // const angle = (deltaX / (containerWidth - sliderWidth)) * (2 * Math.PI)
      this.rotate(angle)
      this.rotateBarLeft = x
    },
    rotateBarMousedown(e) {
      this.rotateBarInfo.initLeft = e.srcElement.offsetLeft
      this.rotateBarInfo.initX = e.clientX
      this.rotateBarInfo.isMove = true
      e.stopImmediatePropagation()
      e.stopPropagation()
      e.preventDefault()
    },
    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)
      // const angle = (deltaX / (containerWidth - sliderWidth)) * (2 * Math.PI)
      this.rotate(angle)
      this.rotateBarLeft = x
    },
    preventDefault(e) {
      e.stopImmediatePropagation()
      e.stopPropagation()
      e.preventDefault()
    },
  },
  beforeDestroy() {
    this.hideAnnotationContextMenu()
    if (this.element) {
      this.element.oncontextmenu = null
    }
    document.removeEventListener('mouseup', this.handleDocumentMouseUp)
    document.removeEventListener('mousemove', this.handleDocumentMouseMove)
    document.removeEventListener('mousedown', this.handleDocumentMouseDown)
    this.series = null
    this.topFusionVolumeActor = null
  },
  computed: {
    NSTip() {
      return `NS: ${this.$store.state.trials.downloadTip}`
    }
  },
}
</script>
<style lang="scss" scoped>
.viewport-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
  cursor: default !important;

  .annotation-context-menu {
    position: absolute;
    z-index: 30;
    min-width: 120px;
    padding: 4px 0;
    background: rgba(24, 24, 24, 0.96);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 4px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    user-select: none;

    &__item {
      padding: 8px 12px;
      font-size: 12px;
      color: #ddd;
      line-height: 1.2;
      cursor: pointer;

      &:hover {
        background: rgba(255, 255, 255, 0.08);
      }
    }
  }

  .opacity-slider-wrapper {
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 10;
    background: rgba(0, 0, 0, 0.5);
    padding: 10px;
    border-radius: 4px;
    width: 40px;

    .slider-title {
      font-size: 12px;
      color: #ddd;
      margin-bottom: 10px;
      width: 30px;
      text-align: center;
    }

    .opacity-slider {
      -webkit-appearance: slider-vertical;
      appearance: slider-vertical;
      writing-mode: vertical-lr;
      direction: rtl;
      width: 10px;
      height: 150px;
      padding: 0;
      margin: 0;
      cursor: pointer;
      outline: none;
    }
  }

  .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;
  }

  .fusion-order-toggle {
    position: absolute;
    right: 5px;
    top: 5px;
    z-index: 2;
    font-size: 12px;
    padding: 2px 8px;
    background: rgba(0, 0, 0, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 2px;
    cursor: pointer;
    user-select: none;
  }

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