@cornerstonejs/core 3.22.3 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/RenderingEngine/BaseVolumeViewport.js +11 -1
- package/dist/esm/RenderingEngine/VolumeViewport.d.ts +1 -0
- package/dist/esm/RenderingEngine/VolumeViewport.js +37 -2
- package/dist/esm/RenderingEngine/helpers/getCameraVectors.d.ts +18 -0
- package/dist/esm/RenderingEngine/helpers/getCameraVectors.js +122 -0
- package/dist/esm/enums/OrientationAxis.d.ts +5 -1
- package/dist/esm/enums/OrientationAxis.js +4 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
|
@@ -28,6 +28,7 @@ import applyPreset from '../utilities/applyPreset';
|
|
|
28
28
|
import imageIdToURI from '../utilities/imageIdToURI';
|
|
29
29
|
import uuidv4 from '../utilities/uuidv4';
|
|
30
30
|
import * as metaData from '../metaData';
|
|
31
|
+
import { getCameraVectors } from './helpers/getCameraVectors';
|
|
31
32
|
class BaseVolumeViewport extends Viewport {
|
|
32
33
|
constructor(props) {
|
|
33
34
|
super(props);
|
|
@@ -252,7 +253,10 @@ class BaseVolumeViewport extends Viewport {
|
|
|
252
253
|
throw new Error('Method not implemented.');
|
|
253
254
|
}
|
|
254
255
|
applyViewOrientation(orientation, resetCamera = true) {
|
|
255
|
-
const { viewPlaneNormal, viewUp } = this._getOrientationVectors(orientation);
|
|
256
|
+
const { viewPlaneNormal, viewUp } = this._getOrientationVectors(orientation) || {};
|
|
257
|
+
if (!viewPlaneNormal || !viewUp) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
256
260
|
const camera = this.getVtkActiveCamera();
|
|
257
261
|
camera.setDirectionOfProjection(-viewPlaneNormal[0], -viewPlaneNormal[1], -viewPlaneNormal[2]);
|
|
258
262
|
camera.setViewUpFrom(viewUp);
|
|
@@ -895,6 +899,12 @@ class BaseVolumeViewport extends Viewport {
|
|
|
895
899
|
if (orientation === 'acquisition') {
|
|
896
900
|
return this._getAcquisitionPlaneOrientation();
|
|
897
901
|
}
|
|
902
|
+
else if (orientation === 'reformat' ||
|
|
903
|
+
orientation.includes('_reformat')) {
|
|
904
|
+
return getCameraVectors(this, {
|
|
905
|
+
useViewportNormal: true,
|
|
906
|
+
});
|
|
907
|
+
}
|
|
898
908
|
else if (MPR_CAMERA_VALUES[orientation]) {
|
|
899
909
|
this.viewportProperties.orientation = orientation;
|
|
900
910
|
return MPR_CAMERA_VALUES[orientation];
|
|
@@ -13,6 +13,7 @@ declare class VolumeViewport extends BaseVolumeViewport {
|
|
|
13
13
|
jumpToWorld(worldPos: Point3): boolean;
|
|
14
14
|
setOrientation(orientation: OrientationAxis | OrientationVectors, immediate?: boolean): void;
|
|
15
15
|
protected setCameraClippingRange(): void;
|
|
16
|
+
private _setViewPlaneToReformatOrientation;
|
|
16
17
|
private _setViewPlaneToAcquisitionPlane;
|
|
17
18
|
getBlendMode(filterActorUIDs?: string[]): BlendModes;
|
|
18
19
|
setBlendMode(blendMode: BlendModes, filterActorUIDs?: any[], immediate?: boolean): void;
|
|
@@ -16,6 +16,7 @@ import getImageSliceDataForVolumeViewport from '../utilities/getImageSliceDataFo
|
|
|
16
16
|
import { transformCanvasToIJK } from '../utilities/transformCanvasToIJK';
|
|
17
17
|
import { transformIJKToCanvas } from '../utilities/transformIJKToCanvas';
|
|
18
18
|
import getVolumeViewportScrollInfo from '../utilities/getVolumeViewportScrollInfo';
|
|
19
|
+
import { calculateCameraPosition, getCameraVectors, } from './helpers/getCameraVectors';
|
|
19
20
|
class VolumeViewport extends BaseVolumeViewport {
|
|
20
21
|
constructor(props) {
|
|
21
22
|
super(props);
|
|
@@ -87,7 +88,13 @@ class VolumeViewport extends BaseVolumeViewport {
|
|
|
87
88
|
if (!firstImageVolume) {
|
|
88
89
|
throw new Error(`imageVolume with id: ${firstImageVolume.volumeId} does not exist`);
|
|
89
90
|
}
|
|
90
|
-
if (this.
|
|
91
|
+
if (this.options.orientation &&
|
|
92
|
+
typeof this.options.orientation === 'string') {
|
|
93
|
+
if (this.options.orientation.includes('_reformat')) {
|
|
94
|
+
this._setViewPlaneToReformatOrientation(this.options.orientation, firstImageVolume);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (this._useAcquisitionPlaneForViewPlane) {
|
|
91
98
|
this._setViewPlaneToAcquisitionPlane(firstImageVolume);
|
|
92
99
|
this._useAcquisitionPlaneForViewPlane = false;
|
|
93
100
|
}
|
|
@@ -98,7 +105,13 @@ class VolumeViewport extends BaseVolumeViewport {
|
|
|
98
105
|
if (!firstImageVolume) {
|
|
99
106
|
throw new Error(`imageVolume with id: ${firstImageVolume.volumeId} does not exist`);
|
|
100
107
|
}
|
|
101
|
-
if (this.
|
|
108
|
+
if (this.options.orientation &&
|
|
109
|
+
typeof this.options.orientation === 'string') {
|
|
110
|
+
if (this.options.orientation.includes('_reformat')) {
|
|
111
|
+
this._setViewPlaneToReformatOrientation(this.options.orientation, firstImageVolume);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (this._useAcquisitionPlaneForViewPlane) {
|
|
102
115
|
this._setViewPlaneToAcquisitionPlane(firstImageVolume);
|
|
103
116
|
this._useAcquisitionPlaneForViewPlane = false;
|
|
104
117
|
}
|
|
@@ -134,6 +147,12 @@ class VolumeViewport extends BaseVolumeViewport {
|
|
|
134
147
|
if (orientation === OrientationAxis.ACQUISITION) {
|
|
135
148
|
({ viewPlaneNormal, viewUp } = super._getAcquisitionPlaneOrientation());
|
|
136
149
|
}
|
|
150
|
+
else if (orientation === OrientationAxis.REFORMAT ||
|
|
151
|
+
orientation.includes('_reformat')) {
|
|
152
|
+
({ viewPlaneNormal, viewUp } = getCameraVectors(this, {
|
|
153
|
+
useViewportNormal: true,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
137
156
|
else if (MPR_CAMERA_VALUES[orientation]) {
|
|
138
157
|
({ viewPlaneNormal, viewUp } = MPR_CAMERA_VALUES[orientation]);
|
|
139
158
|
}
|
|
@@ -168,6 +187,22 @@ class VolumeViewport extends BaseVolumeViewport {
|
|
|
168
187
|
activeCamera.setClippingRange(RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS, RENDERING_DEFAULTS.MAXIMUM_RAY_DISTANCE);
|
|
169
188
|
}
|
|
170
189
|
}
|
|
190
|
+
_setViewPlaneToReformatOrientation(orientation, imageVolume) {
|
|
191
|
+
let viewPlaneNormal, viewUp;
|
|
192
|
+
if (imageVolume) {
|
|
193
|
+
const { direction } = imageVolume;
|
|
194
|
+
({ viewPlaneNormal, viewUp } = calculateCameraPosition(direction.slice(0, 3), direction.slice(3, 6), direction.slice(6, 9), orientation));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
({ viewPlaneNormal, viewUp } = this._getAcquisitionPlaneOrientation());
|
|
198
|
+
}
|
|
199
|
+
this.setCamera({
|
|
200
|
+
viewPlaneNormal,
|
|
201
|
+
viewUp,
|
|
202
|
+
});
|
|
203
|
+
this.initialViewUp = viewUp;
|
|
204
|
+
this.resetCamera();
|
|
205
|
+
}
|
|
171
206
|
_setViewPlaneToAcquisitionPlane(imageVolume) {
|
|
172
207
|
let viewPlaneNormal, viewUp;
|
|
173
208
|
if (imageVolume) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as Enums from '../../enums';
|
|
2
|
+
import type * as Types from '../../types';
|
|
3
|
+
import { vec3 } from 'gl-matrix';
|
|
4
|
+
export interface CameraPositionConfig {
|
|
5
|
+
orientation?: Enums.OrientationAxis;
|
|
6
|
+
useViewportNormal?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function calculateCameraPosition(rowCosineVec: vec3, colCosineVec: vec3, scanAxisNormal: vec3, orientation: Enums.OrientationAxis): {
|
|
9
|
+
viewPlaneNormal: [number, number, number];
|
|
10
|
+
viewUp: [number, number, number];
|
|
11
|
+
viewRight: [number, number, number];
|
|
12
|
+
};
|
|
13
|
+
export declare function getCameraVectors(viewport: Types.IBaseVolumeViewport, config?: CameraPositionConfig): {
|
|
14
|
+
viewPlaneNormal: [number, number, number];
|
|
15
|
+
viewUp: [number, number, number];
|
|
16
|
+
viewRight: [number, number, number];
|
|
17
|
+
};
|
|
18
|
+
export declare function getOrientationFromScanAxisNormal(scanAxisNormal: vec3): Enums.OrientationAxis;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as metaData from '../../metaData';
|
|
2
|
+
import * as CONSTANTS from '../../constants';
|
|
3
|
+
import * as Enums from '../../enums';
|
|
4
|
+
import { vec3 } from 'gl-matrix';
|
|
5
|
+
const { MPR_CAMERA_VALUES } = CONSTANTS;
|
|
6
|
+
const { OrientationAxis } = Enums;
|
|
7
|
+
export function calculateCameraPosition(rowCosineVec, colCosineVec, scanAxisNormal, orientation) {
|
|
8
|
+
let referenceCameraValues;
|
|
9
|
+
switch (orientation) {
|
|
10
|
+
case OrientationAxis.AXIAL:
|
|
11
|
+
case OrientationAxis.AXIAL_REFORMAT:
|
|
12
|
+
referenceCameraValues = MPR_CAMERA_VALUES.axial;
|
|
13
|
+
break;
|
|
14
|
+
case OrientationAxis.SAGITTAL:
|
|
15
|
+
case OrientationAxis.SAGITTAL_REFORMAT:
|
|
16
|
+
referenceCameraValues = MPR_CAMERA_VALUES.sagittal;
|
|
17
|
+
break;
|
|
18
|
+
case OrientationAxis.CORONAL:
|
|
19
|
+
case OrientationAxis.CORONAL_REFORMAT:
|
|
20
|
+
referenceCameraValues = MPR_CAMERA_VALUES.coronal;
|
|
21
|
+
break;
|
|
22
|
+
default:
|
|
23
|
+
referenceCameraValues = MPR_CAMERA_VALUES.axial;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
const normalizedRowCosine = vec3.normalize(vec3.create(), rowCosineVec);
|
|
27
|
+
const normalizedColCosine = vec3.normalize(vec3.create(), colCosineVec);
|
|
28
|
+
const normalizedScanAxis = vec3.normalize(vec3.create(), scanAxisNormal);
|
|
29
|
+
const inputVectors = [
|
|
30
|
+
normalizedRowCosine,
|
|
31
|
+
normalizedColCosine,
|
|
32
|
+
normalizedScanAxis,
|
|
33
|
+
];
|
|
34
|
+
const referenceVectors = [
|
|
35
|
+
vec3.fromValues(referenceCameraValues.viewRight[0], referenceCameraValues.viewRight[1], referenceCameraValues.viewRight[2]),
|
|
36
|
+
vec3.fromValues(referenceCameraValues.viewUp[0], referenceCameraValues.viewUp[1], referenceCameraValues.viewUp[2]),
|
|
37
|
+
vec3.fromValues(referenceCameraValues.viewPlaneNormal[0], referenceCameraValues.viewPlaneNormal[1], referenceCameraValues.viewPlaneNormal[2]),
|
|
38
|
+
];
|
|
39
|
+
const usedInputIndices = new Set();
|
|
40
|
+
const findBestMatch = (refVector) => {
|
|
41
|
+
let bestMatch = 0;
|
|
42
|
+
let bestDot = -2;
|
|
43
|
+
let shouldInvert = false;
|
|
44
|
+
inputVectors.forEach((inputVec, index) => {
|
|
45
|
+
if (usedInputIndices.has(index)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const dot = vec3.dot(refVector, inputVec);
|
|
49
|
+
const absDot = Math.abs(dot);
|
|
50
|
+
if (absDot > bestDot) {
|
|
51
|
+
bestDot = absDot;
|
|
52
|
+
bestMatch = index;
|
|
53
|
+
shouldInvert = dot < 0;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
usedInputIndices.add(bestMatch);
|
|
57
|
+
const matchedVector = vec3.clone(inputVectors[bestMatch]);
|
|
58
|
+
if (shouldInvert) {
|
|
59
|
+
vec3.negate(matchedVector, matchedVector);
|
|
60
|
+
}
|
|
61
|
+
return matchedVector;
|
|
62
|
+
};
|
|
63
|
+
const viewRight = findBestMatch(referenceVectors[0]);
|
|
64
|
+
const viewUp = findBestMatch(referenceVectors[1]);
|
|
65
|
+
const viewPlaneNormal = findBestMatch(referenceVectors[2]);
|
|
66
|
+
return {
|
|
67
|
+
viewPlaneNormal: [
|
|
68
|
+
viewPlaneNormal[0],
|
|
69
|
+
viewPlaneNormal[1],
|
|
70
|
+
viewPlaneNormal[2],
|
|
71
|
+
],
|
|
72
|
+
viewUp: [viewUp[0], viewUp[1], viewUp[2]],
|
|
73
|
+
viewRight: [viewRight[0], viewRight[1], viewRight[2]],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function getCameraVectors(viewport, config) {
|
|
77
|
+
if (!viewport.getActors()?.length) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (viewport.type !== Enums.ViewportType.ORTHOGRAPHIC) {
|
|
81
|
+
console.warn('Viewport should be a volume viewport');
|
|
82
|
+
}
|
|
83
|
+
let imageId = viewport.getCurrentImageId();
|
|
84
|
+
if (!imageId) {
|
|
85
|
+
imageId = viewport.getImageIds()?.[0];
|
|
86
|
+
}
|
|
87
|
+
if (!imageId) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { imageOrientationPatient } = metaData.get('imagePlaneModule', imageId);
|
|
91
|
+
const rowCosineVec = vec3.fromValues(imageOrientationPatient[0], imageOrientationPatient[1], imageOrientationPatient[2]);
|
|
92
|
+
const colCosineVec = vec3.fromValues(imageOrientationPatient[3], imageOrientationPatient[4], imageOrientationPatient[5]);
|
|
93
|
+
const scanAxisNormal = vec3.cross(vec3.create(), rowCosineVec, colCosineVec);
|
|
94
|
+
let { orientation } = config || {};
|
|
95
|
+
const { useViewportNormal } = config || {};
|
|
96
|
+
let normalPlaneForOrientation = scanAxisNormal;
|
|
97
|
+
if (useViewportNormal) {
|
|
98
|
+
normalPlaneForOrientation = viewport.getCamera().viewPlaneNormal;
|
|
99
|
+
}
|
|
100
|
+
if (!orientation) {
|
|
101
|
+
orientation = getOrientationFromScanAxisNormal(normalPlaneForOrientation);
|
|
102
|
+
}
|
|
103
|
+
return calculateCameraPosition(rowCosineVec, colCosineVec, scanAxisNormal, orientation);
|
|
104
|
+
}
|
|
105
|
+
export function getOrientationFromScanAxisNormal(scanAxisNormal) {
|
|
106
|
+
const normalizedScanAxis = vec3.normalize(vec3.create(), scanAxisNormal);
|
|
107
|
+
const axialNormal = vec3.fromValues(MPR_CAMERA_VALUES.axial.viewPlaneNormal[0], MPR_CAMERA_VALUES.axial.viewPlaneNormal[1], MPR_CAMERA_VALUES.axial.viewPlaneNormal[2]);
|
|
108
|
+
const sagittalNormal = vec3.fromValues(MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[0], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[1], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[2]);
|
|
109
|
+
const coronalNormal = vec3.fromValues(MPR_CAMERA_VALUES.coronal.viewPlaneNormal[0], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[1], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[2]);
|
|
110
|
+
const axialDot = Math.abs(vec3.dot(normalizedScanAxis, axialNormal));
|
|
111
|
+
const sagittalDot = Math.abs(vec3.dot(normalizedScanAxis, sagittalNormal));
|
|
112
|
+
const coronalDot = Math.abs(vec3.dot(normalizedScanAxis, coronalNormal));
|
|
113
|
+
if (axialDot >= sagittalDot && axialDot >= coronalDot) {
|
|
114
|
+
return OrientationAxis.AXIAL;
|
|
115
|
+
}
|
|
116
|
+
else if (sagittalDot >= coronalDot) {
|
|
117
|
+
return OrientationAxis.SAGITTAL;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
return OrientationAxis.CORONAL;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -2,6 +2,10 @@ declare enum OrientationAxis {
|
|
|
2
2
|
AXIAL = "axial",
|
|
3
3
|
CORONAL = "coronal",
|
|
4
4
|
SAGITTAL = "sagittal",
|
|
5
|
-
ACQUISITION = "acquisition"
|
|
5
|
+
ACQUISITION = "acquisition",
|
|
6
|
+
AXIAL_REFORMAT = "axial_reformat",
|
|
7
|
+
CORONAL_REFORMAT = "coronal_reformat",
|
|
8
|
+
SAGITTAL_REFORMAT = "sagittal_reformat",
|
|
9
|
+
REFORMAT = "reformat"
|
|
6
10
|
}
|
|
7
11
|
export default OrientationAxis;
|
|
@@ -4,5 +4,9 @@ var OrientationAxis;
|
|
|
4
4
|
OrientationAxis["CORONAL"] = "coronal";
|
|
5
5
|
OrientationAxis["SAGITTAL"] = "sagittal";
|
|
6
6
|
OrientationAxis["ACQUISITION"] = "acquisition";
|
|
7
|
+
OrientationAxis["AXIAL_REFORMAT"] = "axial_reformat";
|
|
8
|
+
OrientationAxis["CORONAL_REFORMAT"] = "coronal_reformat";
|
|
9
|
+
OrientationAxis["SAGITTAL_REFORMAT"] = "sagittal_reformat";
|
|
10
|
+
OrientationAxis["REFORMAT"] = "reformat";
|
|
7
11
|
})(OrientationAxis || (OrientationAxis = {}));
|
|
8
12
|
export default OrientationAxis;
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "3.
|
|
1
|
+
export declare const version = "3.23.0";
|
package/dist/esm/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '3.
|
|
1
|
+
export const version = '3.23.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.23.0",
|
|
4
4
|
"description": "Cornerstone3D Core",
|
|
5
5
|
"module": "./dist/esm/index.js",
|
|
6
6
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
"type": "individual",
|
|
98
98
|
"url": "https://ohif.org/donate"
|
|
99
99
|
},
|
|
100
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "85796c96f904126c372f8e5bc6bea3db1c955aa8"
|
|
101
101
|
}
|