@cornerstonejs/core 3.22.4 → 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.
@@ -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._useAcquisitionPlaneForViewPlane) {
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._useAcquisitionPlaneForViewPlane) {
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;
@@ -1 +1 @@
1
- export declare const version = "3.22.4";
1
+ export declare const version = "3.23.0";
@@ -1 +1 @@
1
- export const version = '3.22.4';
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.22.4",
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": "572327cda5bafbb57a1d965f37d1bcce1868d88b"
100
+ "gitHead": "85796c96f904126c372f8e5bc6bea3db1c955aa8"
101
101
  }