diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue
index 99b6222d..bc191c69 100644
--- a/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue
@@ -661,6 +661,7 @@ export default {
},
sliderMouseup(e) {
this.sliderInfo.isMove = false
+ this.$emit('contentMouseup', e)
},
sliderMousedown(e) {
const boxHeight = this.$refs['sliderBox'].clientHeight
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
index c64aa02b..517db540 100644
--- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
@@ -295,7 +295,7 @@
+ @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
+ @renderAnnotations="renderAnnotations" @upperRangeChange="upperRangeChange"
+ @contentMouseup="contentMouseup" />
@@ -361,7 +362,8 @@
{{ $t('trials:reading:button:handbooks') }}
-
@@ -460,6 +462,7 @@ import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
import FusionForm from './FusionForm.vue'
import colorMap from './colorMap.vue'
import RectangleROITool from './tools/RectangleROITool'
+import ScaleOverlayTool from './tools/ScaleOverlayTool'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getNetWorkSpeed, setNetWorkSpeedSizeAll, workSpeedclose } from "@/utils"
@@ -470,7 +473,7 @@ const {
ToolGroupManager,
Enums: csToolsEnums,
StackScrollTool,
- ScaleOverlayTool,
+ // ScaleOverlayTool,
PanTool,
ZoomTool,
WindowLevelTool,
@@ -625,7 +628,12 @@ export default {
uploadStatus: 'upload',
taskId: '',
isReadingTaskViewInOrder: null,
- trialCriterion: {}
+ trialCriterion: {},
+
+ curOperation: {
+ type: '',
+ annotation: null
+ }
}
},
computed: {
@@ -1479,11 +1487,8 @@ export default {
if (isBound || this.isNumber(operateStateEnum)) {
this.$refs[`ecrf_${series.TaskInfo.VisitTaskId}`][0].updateAnnotationToQuestion(annotation)
} else {
- if (this.saveCustomAnnotationTimer) {
- clearTimeout(this.saveCustomAnnotationTimer)
- this.saveCustomAnnotationTimer = null
- }
- this.saveCustomAnnotationTimer = setTimeout(() => { this.saveCustomAnnotation(annotation) }, 1000)
+ this.curOperation.type = 'Modified'
+ this.curOperation.annotation = annotation
}
}
this.setToolsPassive()
@@ -1553,6 +1558,17 @@ export default {
console.log(e)
}
},
+ contentMouseup(e) {
+ console.log('contentMouseup')
+ if (this.curOperation.type === 'Modified') {
+ let annotation = this.curOperation.annotation
+ this.saveCustomAnnotation(annotation)
+ }
+ this.curOperation = {
+ type: '',
+ annotation: null
+ }
+ },
removeAnnotation(annotation) {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
const renderingEngine = getRenderingEngine(renderingEngineId)
@@ -3032,21 +3048,25 @@ export default {
let volumeId = null; let volume = null
const key = isFusion ? `fusion_${serie.SeriesInstanceUid}` : serie.SeriesInstanceUid
if (!this.volumeData[key] || !cache.getVolume(this.volumeData[key].volumeId)) {
- if (serie.Modality === 'PT' && !isFusion) {
- serie.ImageIds.forEach(async id => {
- const imageLoadObject = cache.getImage(id)
- if (imageLoadObject) {
- await new Promise(res => {
- cache.removeImageLoadObject(id, { force: true }) // 从缓存中删除
- res()
- })
- }
- })
+ // if (serie.Modality === 'PT' && !isFusion) {
+ // serie.ImageIds.forEach(async id => {
+ // const imageLoadObject = cache.getImage(id)
+ // if (imageLoadObject) {
+ // await new Promise(res => {
+ // cache.removeImageLoadObject(id, { force: true }) // 从缓存中删除
+ // res()
+ // })
+ // }
+ // })
+ // }
+ volumeId = `${isFusion ? 'fusion' : serie.Modality}Volume` + ':' + serie.SeriesInstanceUid
+ if (cache.getVolume(volumeId)) {
+ volume = cache.getVolume(volumeId)
+ } else {
+ await this.$refs[`viewport-fusion-0`][0].createImageIdsAndCacheMetaData(serie)
+ volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
+ volume.load()
}
- await this.$refs[`viewport-fusion-0`][0].createImageIdsAndCacheMetaData(serie)
- volumeId = `${isFusion ? 'fusion' : serie.Modality}Volume` + ':' + csUtils.uuidv4()
- volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
- volume.load()
this.volumeData[key] = {}
this.volumeData[key].volumeId = volumeId
this.volumeData[key].volume = volume
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue
index 0a3adcd5..7698146c 100644
--- a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue
@@ -481,6 +481,7 @@ export default {
},
sliderMouseup(e) {
this.sliderInfo.isMove = false
+ this.$emit('contentMouseup', e)
},
sliderMousedown(e) {
const boxHeight = this.$refs['sliderBox'].clientHeight
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/tools/ScaleOverlayTool.js b/src/views/trials/trials-panel/reading/dicoms3D/components/tools/ScaleOverlayTool.js
new file mode 100644
index 00000000..5c1611d9
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/tools/ScaleOverlayTool.js
@@ -0,0 +1,409 @@
+// import AnnotationDisplayTool from './base/AnnotationDisplayTool';
+import { vec3 } from 'gl-matrix';
+import { getEnabledElementByIds, getRenderingEngines, utilities as csUtils, } from '@cornerstonejs/core';
+// import { addAnnotation, getAnnotations, } from '../stateManagement/annotation/annotationState';
+// import { drawLine as drawLineSvg, drawTextBox as drawTextBoxSvg, } from '../drawingSvg';
+// import { getToolGroup } from '../store/ToolGroupManager';
+import * as cornerstoneTools from '@cornerstonejs/tools'
+const {
+ annotation,
+ drawing,
+ AnnotationDisplayTool,
+ ToolGroupManager,
+} = cornerstoneTools
+const { getAnnotations, addAnnotation } = annotation.state
+const { getToolGroup } = ToolGroupManager
+const { drawLine, drawTextBox } = drawing
+const drawLineSvg = drawLine
+const drawTextBoxSvg = drawTextBox
+const viewportsWithAnnotations = [];
+class ScaleOverlayTool extends AnnotationDisplayTool {
+ constructor(toolProps = {}, defaultToolProps = {
+ configuration: {
+ viewportId: '',
+ scaleLocation: 'bottom',
+ },
+ }) {
+ super(toolProps, defaultToolProps);
+ this.editData = null;
+ this._init = () => {
+ const renderingEngines = getRenderingEngines();
+ const renderingEngine = renderingEngines[0];
+ if (!renderingEngine) {
+ return;
+ }
+ const viewportIds = getToolGroup(this.toolGroupId).viewportsInfo;
+ if (!viewportIds) {
+ return;
+ }
+ const enabledElements = viewportIds.map((e) => getEnabledElementByIds(e.viewportId, e.renderingEngineId));
+ let { viewport } = enabledElements[0];
+ const { FrameOfReferenceUID } = enabledElements[0];
+ if (this.configuration.viewportId) {
+ enabledElements.forEach((element) => {
+ if (element.viewport.id == this.configuration.viewportId) {
+ viewport = element.viewport;
+ }
+ });
+ }
+ if (!viewport) {
+ return;
+ }
+ const { viewUp, viewPlaneNormal } = viewport.getCamera();
+ const viewportCanvasCornersInWorld = csUtils.getViewportImageCornersInWorld(viewport);
+ let annotation = this.editData?.annotation;
+ const annotations = getAnnotations(this.getToolName(), viewport.element);
+ if (annotations.length) {
+ annotation = annotations.filter((thisAnnotation) => thisAnnotation.data.viewportId == viewport.id)[0];
+ }
+ enabledElements.forEach((element) => {
+ const { viewport } = element;
+ if (!viewportsWithAnnotations.includes(viewport.id) || annotations.length === 0) {
+ const newAnnotation = {
+ metadata: {
+ toolName: this.getToolName(),
+ viewPlaneNormal: [...viewPlaneNormal],
+ viewUp: [...viewUp],
+ FrameOfReferenceUID,
+ referencedImageId: null,
+ },
+ data: {
+ handles: {
+ points: csUtils.getViewportImageCornersInWorld(viewport),
+ },
+ viewportId: viewport.id,
+ },
+ };
+ viewportsWithAnnotations.push(viewport.id);
+ addAnnotation(newAnnotation, viewport.element);
+ annotation = newAnnotation;
+ }
+ });
+ if (this.editData?.annotation &&
+ this.editData.annotation.data.viewportId == viewport.id) {
+ this.editData.annotation.data.handles.points =
+ viewportCanvasCornersInWorld;
+ this.editData.annotation.data.viewportId = viewport.id;
+ }
+ this.editData = {
+ viewport,
+ renderingEngine,
+ annotation,
+ };
+ };
+ this.onSetToolEnabled = () => {
+ this._init();
+ };
+ this.onCameraModified = (evt) => {
+ this.configuration.viewportId = evt.detail.viewportId;
+ this._init();
+ };
+ this.computeScaleSize = (worldWidthViewport, worldHeightViewport, location) => {
+ const scaleSizes = [
+ 16000, 8000, 4000, 2000, 1000, 500, 250, 100, 50, 25, 10, 5, 2,
+ ];
+ let currentScaleSize;
+ if (location == 'top' || location == 'bottom') {
+ currentScaleSize = scaleSizes.filter((scaleSize) => scaleSize < worldWidthViewport * 0.6 &&
+ scaleSize > worldWidthViewport * 0.2);
+ }
+ else {
+ currentScaleSize = scaleSizes.filter((scaleSize) => scaleSize < worldHeightViewport * 0.6 &&
+ scaleSize > worldHeightViewport * 0.2);
+ }
+ return currentScaleSize[0];
+ };
+ this.computeEndScaleTicks = (canvasCoordinates, location) => {
+ const locationTickOffset = {
+ bottom: [
+ [0, -10],
+ [0, -10],
+ ],
+ top: [
+ [0, 10],
+ [0, 10],
+ ],
+ left: [
+ [0, 0],
+ [10, 0],
+ ],
+ right: [
+ [0, 0],
+ [-10, 0],
+ ],
+ };
+ const endTick1 = [
+ [
+ canvasCoordinates[1][0] + locationTickOffset[location][0][0],
+ canvasCoordinates[1][1] + locationTickOffset[location][0][0],
+ ],
+ [
+ canvasCoordinates[1][0] + locationTickOffset[location][1][0],
+ canvasCoordinates[1][1] + locationTickOffset[location][1][1],
+ ],
+ ];
+ const endTick2 = [
+ [
+ canvasCoordinates[0][0] + locationTickOffset[location][0][0],
+ canvasCoordinates[0][1] + locationTickOffset[location][0][0],
+ ],
+ [
+ canvasCoordinates[0][0] + locationTickOffset[location][1][0],
+ canvasCoordinates[0][1] + locationTickOffset[location][1][1],
+ ],
+ ];
+ return {
+ endTick1: endTick1,
+ endTick2: endTick2,
+ };
+ };
+ this.computeInnerScaleTicks = (scaleSize, location, annotationUID, leftTick, rightTick) => {
+ let canvasScaleSize;
+ if (location == 'bottom' || location == 'top') {
+ canvasScaleSize = rightTick[0][0] - leftTick[0][0];
+ }
+ else if (location == 'left' || location == 'right') {
+ canvasScaleSize = rightTick[0][1] - leftTick[0][1];
+ }
+ const tickIds = [];
+ const tickUIDs = [];
+ const tickCoordinates = [];
+ let numberSmallTicks = scaleSize;
+ if (scaleSize >= 50) {
+ numberSmallTicks = scaleSize / 10;
+ }
+ const tickSpacing = canvasScaleSize / numberSmallTicks;
+ for (let i = 0; i < numberSmallTicks - 1; i++) {
+ const locationOffset = {
+ bottom: [
+ [tickSpacing * (i + 1), 0],
+ [tickSpacing * (i + 1), 5],
+ ],
+ top: [
+ [tickSpacing * (i + 1), 0],
+ [tickSpacing * (i + 1), -5],
+ ],
+ left: [
+ [0, tickSpacing * (i + 1)],
+ [-5, tickSpacing * (i + 1)],
+ ],
+ right: [
+ [0, tickSpacing * (i + 1)],
+ [5, tickSpacing * (i + 1)],
+ ],
+ };
+ tickIds.push(`${annotationUID}-tick${i}`);
+ tickUIDs.push(`tick${i}`);
+ if ((i + 1) % 5 == 0) {
+ tickCoordinates.push([
+ [
+ leftTick[0][0] + locationOffset[location][0][0],
+ leftTick[0][1] + locationOffset[location][0][1],
+ ],
+ [
+ leftTick[1][0] + locationOffset[location][0][0],
+ leftTick[1][1] + locationOffset[location][0][1],
+ ],
+ ]);
+ }
+ else {
+ tickCoordinates.push([
+ [
+ leftTick[0][0] + locationOffset[location][0][0],
+ leftTick[0][1] + locationOffset[location][0][1],
+ ],
+ [
+ leftTick[1][0] + locationOffset[location][1][0],
+ leftTick[1][1] + locationOffset[location][1][1],
+ ],
+ ]);
+ }
+ }
+ return { tickIds, tickUIDs, tickCoordinates };
+ };
+ this.computeWorldScaleCoordinates = (scaleSize, location, pointSet) => {
+ let worldCoordinates;
+ let topBottomVec = vec3.subtract(vec3.create(), pointSet[0], pointSet[1]);
+ topBottomVec = vec3.normalize(vec3.create(), topBottomVec);
+ let topRightVec = vec3.subtract(vec3.create(), pointSet[2], pointSet[0]);
+ topRightVec = vec3.normalize(vec3.create(), topRightVec);
+ const midpointLocation = {
+ bottom: [pointSet[1], pointSet[2]],
+ top: [pointSet[0], pointSet[3]],
+ right: [pointSet[2], pointSet[3]],
+ left: [pointSet[0], pointSet[1]],
+ };
+ const midpoint = vec3
+ .add(vec3.create(), midpointLocation[location][0], midpointLocation[location][0])
+ .map((i) => i / 2);
+ const offset = scaleSize /
+ 2 /
+ Math.sqrt(Math.pow(topBottomVec[0], 2) +
+ Math.pow(topBottomVec[1], 2) +
+ Math.pow(topBottomVec[2], 2));
+ if (location == 'top' || location == 'bottom') {
+ worldCoordinates = [
+ vec3.subtract(vec3.create(), midpoint, topRightVec.map((i) => i * offset)),
+ vec3.add(vec3.create(), midpoint, topRightVec.map((i) => i * offset)),
+ ];
+ }
+ else if (location == 'left' || location == 'right') {
+ worldCoordinates = [
+ vec3.add(vec3.create(), midpoint, topBottomVec.map((i) => i * offset)),
+ vec3.subtract(vec3.create(), midpoint, topBottomVec.map((i) => i * offset)),
+ ];
+ }
+ return worldCoordinates;
+ };
+ this.computeCanvasScaleCoordinates = (canvasSize, canvasCoordinates, vscaleBounds, hscaleBounds, location) => {
+ let scaleCanvasCoordinates;
+ if (location == 'top' || location == 'bottom') {
+ const worldDistanceOnCanvas = canvasCoordinates[0][0] - canvasCoordinates[1][0];
+ scaleCanvasCoordinates = [
+ [canvasSize.width / 2 - worldDistanceOnCanvas / 2, vscaleBounds.height],
+ [canvasSize.width / 2 + worldDistanceOnCanvas / 2, vscaleBounds.height],
+ ];
+ }
+ else if (location == 'left' || location == 'right') {
+ const worldDistanceOnCanvas = canvasCoordinates[0][1] - canvasCoordinates[1][1];
+ scaleCanvasCoordinates = [
+ [hscaleBounds.width, canvasSize.height / 2 - worldDistanceOnCanvas / 2],
+ [hscaleBounds.width, canvasSize.height / 2 + worldDistanceOnCanvas / 2],
+ ];
+ }
+ return scaleCanvasCoordinates;
+ };
+ this.computeScaleBounds = (canvasSize, horizontalReduction, verticalReduction, location) => {
+ const hReduction = horizontalReduction * Math.min(1000, canvasSize.width);
+ const vReduction = verticalReduction * Math.min(1000, canvasSize.height);
+ const locationBounds = {
+ bottom: [-vReduction, -hReduction],
+ top: [vReduction, hReduction],
+ left: [vReduction, hReduction],
+ right: [-vReduction, -hReduction],
+ };
+ const canvasBounds = {
+ bottom: [canvasSize.height, canvasSize.width],
+ top: [0, canvasSize.width],
+ left: [canvasSize.height, 0],
+ right: [canvasSize.height, canvasSize.width],
+ };
+ return {
+ height: canvasBounds[location][0] + locationBounds[location][0],
+ width: canvasBounds[location][1] + locationBounds[location][1],
+ };
+ };
+ }
+ renderAnnotation(enabledElement, svgDrawingHelper) {
+ if (!this.editData || !this.editData.viewport) {
+ return;
+ }
+ const location = this.configuration.scaleLocation;
+ const { viewport } = enabledElement;
+ const annotations = getAnnotations(this.getToolName(), viewport.element);
+ const annotation = annotations.filter((thisAnnotation) => thisAnnotation.data.viewportId == viewport.id)[0];
+ const canvas = enabledElement.viewport.canvas;
+ const renderStatus = false;
+ if (!viewport) {
+ return renderStatus;
+ }
+ const styleSpecifier = {
+ toolGroupId: this.toolGroupId,
+ toolName: this.getToolName(),
+ viewportId: enabledElement.viewport.id,
+ };
+ const canvasSize = {
+ width: canvas.width / window.devicePixelRatio || 1,
+ height: canvas.height / window.devicePixelRatio || 1,
+ };
+ const topLeft = annotation.data.handles.points[0];
+ const topRight = annotation.data.handles.points[1];
+ const bottomLeft = annotation.data.handles.points[2];
+ const bottomRight = annotation.data.handles.points[3];
+ const pointSet1 = [topLeft, bottomLeft, topRight, bottomRight];
+ const worldWidthViewport = vec3.distance(bottomLeft, bottomRight);
+ const worldHeightViewport = vec3.distance(topLeft, bottomLeft);
+ const hscaleBounds = this.computeScaleBounds(canvasSize, 0.05, 0.05, location);
+ const vscaleBounds = this.computeScaleBounds(canvasSize, 0.05, 0.05, location);
+ const scaleSize = this.computeScaleSize(worldWidthViewport, worldHeightViewport, location);
+ const canvasCoordinates = this.computeWorldScaleCoordinates(scaleSize, location, pointSet1).map((world) => viewport.worldToCanvas(world));
+ const scaleCanvasCoordinates = this.computeCanvasScaleCoordinates(canvasSize, canvasCoordinates, vscaleBounds, hscaleBounds, location);
+ const scaleTicks = this.computeEndScaleTicks(scaleCanvasCoordinates, location);
+ const { annotationUID } = annotation;
+ styleSpecifier.annotationUID = annotationUID;
+ const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
+ const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
+ const color = this.getStyle('color', styleSpecifier, annotation);
+ const shadow = this.getStyle('shadow', styleSpecifier, annotation);
+ const scaleId = `${annotationUID}-scaleline`;
+ const scaleLineUID = '1';
+ drawLineSvg(svgDrawingHelper, annotationUID, scaleLineUID, scaleCanvasCoordinates[0], scaleCanvasCoordinates[1], {
+ color,
+ width: lineWidth,
+ lineDash,
+ shadow,
+ }, scaleId);
+ const leftTickId = `${annotationUID}-left`;
+ const leftTickUID = '2';
+ drawLineSvg(svgDrawingHelper, annotationUID, leftTickUID, scaleTicks.endTick1[0], scaleTicks.endTick1[1], {
+ color,
+ width: lineWidth,
+ lineDash,
+ shadow,
+ }, leftTickId);
+ const rightTickId = `${annotationUID}-right`;
+ const rightTickUID = '3';
+ drawLineSvg(svgDrawingHelper, annotationUID, rightTickUID, scaleTicks.endTick2[0], scaleTicks.endTick2[1], {
+ color,
+ width: lineWidth,
+ lineDash,
+ shadow,
+ }, rightTickId);
+ const locationTextOffest = {
+ bottom: [-10, -42],
+ top: [-12, -35],
+ left: [-40, -20],
+ right: [-50, -20],
+ };
+ const textCanvasCoordinates = [
+ scaleCanvasCoordinates[0][0] + locationTextOffest[location][0],
+ scaleCanvasCoordinates[0][1] + locationTextOffest[location][1],
+ ];
+ const textBoxLines = this._getTextLines(scaleSize);
+ const { tickIds, tickUIDs, tickCoordinates } = this.computeInnerScaleTicks(scaleSize, location, annotationUID, scaleTicks.endTick1, scaleTicks.endTick2);
+ for (let i = 0; i < tickUIDs.length; i++) {
+ drawLineSvg(svgDrawingHelper, annotationUID, tickUIDs[i], tickCoordinates[i][0], tickCoordinates[i][1], {
+ color,
+ width: lineWidth,
+ lineDash,
+ shadow,
+ }, tickIds[i]);
+ }
+ const textUID = 'text0';
+ drawTextBoxSvg(svgDrawingHelper, annotationUID, textUID, textBoxLines, [textCanvasCoordinates[0], textCanvasCoordinates[1]], {
+ fontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
+ fontSize: '14px',
+ lineDash: '2,3',
+ lineWidth: '1',
+ shadow: true,
+ color: color,
+ });
+ return renderStatus;
+ }
+ _getTextLines(scaleSize) {
+ let scaleSizeDisplayValue;
+ let scaleSizeUnits;
+ if (scaleSize >= 50) {
+ scaleSizeDisplayValue = scaleSize / 10;
+ scaleSizeUnits = ' cm';
+ }
+ else {
+ scaleSizeDisplayValue = scaleSize;
+ scaleSizeUnits = ' mm';
+ }
+ const textLines = [scaleSizeDisplayValue.toString().concat(scaleSizeUnits)];
+ return textLines;
+ }
+}
+ScaleOverlayTool.toolName = 'ScaleOverlay';
+export default ScaleOverlayTool;