@cornerstonejs/core 3.30.3 → 3.31.1

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.
Files changed (31) hide show
  1. package/dist/esm/RenderingEngine/BaseRenderingEngine.js +7 -1
  2. package/dist/esm/RenderingEngine/BaseVolumeViewport.d.ts +3 -1
  3. package/dist/esm/RenderingEngine/BaseVolumeViewport.js +68 -9
  4. package/dist/esm/RenderingEngine/StackViewport.js +5 -2
  5. package/dist/esm/RenderingEngine/VideoViewport.js +1 -1
  6. package/dist/esm/RenderingEngine/Viewport.d.ts +2 -1
  7. package/dist/esm/RenderingEngine/Viewport.js +38 -1
  8. package/dist/esm/RenderingEngine/helpers/stats/StatsOverlay.d.ts +28 -0
  9. package/dist/esm/RenderingEngine/helpers/stats/StatsOverlay.js +137 -0
  10. package/dist/esm/RenderingEngine/helpers/stats/StatsPanel.d.ts +24 -0
  11. package/dist/esm/RenderingEngine/helpers/stats/StatsPanel.js +101 -0
  12. package/dist/esm/RenderingEngine/helpers/stats/constants.d.ts +44 -0
  13. package/dist/esm/RenderingEngine/helpers/stats/constants.js +36 -0
  14. package/dist/esm/RenderingEngine/helpers/stats/enums.d.ts +6 -0
  15. package/dist/esm/RenderingEngine/helpers/stats/enums.js +7 -0
  16. package/dist/esm/RenderingEngine/helpers/stats/index.d.ts +2 -0
  17. package/dist/esm/RenderingEngine/helpers/stats/index.js +2 -0
  18. package/dist/esm/RenderingEngine/helpers/stats/types.d.ts +22 -0
  19. package/dist/esm/RenderingEngine/helpers/stats/types.js +0 -0
  20. package/dist/esm/init.js +3 -0
  21. package/dist/esm/types/Cornerstone3DConfig.d.ts +3 -0
  22. package/dist/esm/types/IViewport.d.ts +9 -2
  23. package/dist/esm/utilities/index.d.ts +1 -0
  24. package/dist/esm/utilities/index.js +1 -0
  25. package/dist/esm/utilities/isEqual.d.ts +5 -5
  26. package/dist/esm/utilities/isEqual.js +5 -5
  27. package/dist/esm/utilities/updatePlaneRestriction.d.ts +2 -0
  28. package/dist/esm/utilities/updatePlaneRestriction.js +41 -0
  29. package/dist/esm/version.d.ts +1 -1
  30. package/dist/esm/version.js +1 -1
  31. package/package.json +2 -2
@@ -8,9 +8,10 @@ import BaseVolumeViewport from './BaseVolumeViewport';
8
8
  import StackViewport from './StackViewport';
9
9
  import viewportTypeUsesCustomRenderingPipeline from './helpers/viewportTypeUsesCustomRenderingPipeline';
10
10
  import getOrCreateCanvas from './helpers/getOrCreateCanvas';
11
- import { getShouldUseCPURendering, isCornerstoneInitialized } from '../init';
11
+ import { getShouldUseCPURendering, isCornerstoneInitialized, getConfiguration, } from '../init';
12
12
  import viewportTypeToViewportClass from './helpers/viewportTypeToViewportClass';
13
13
  import { OrientationAxis } from '../enums';
14
+ import { StatsOverlay } from './helpers/stats';
14
15
  export const VIEWPORT_MIN_SIZE = 2;
15
16
  class BaseRenderingEngine {
16
17
  constructor(id) {
@@ -34,6 +35,10 @@ class BaseRenderingEngine {
34
35
  }
35
36
  this._viewports = new Map();
36
37
  this.hasBeenDestroyed = false;
38
+ const config = getConfiguration();
39
+ if (config?.debug?.statsOverlay) {
40
+ StatsOverlay.setup();
41
+ }
37
42
  }
38
43
  enableElement(viewportInputEntry) {
39
44
  const viewportInput = this._normalizeViewportInputEntry(viewportInputEntry);
@@ -169,6 +174,7 @@ class BaseRenderingEngine {
169
174
  if (this.hasBeenDestroyed) {
170
175
  return;
171
176
  }
177
+ StatsOverlay.cleanup();
172
178
  if (!this.useCPURendering) {
173
179
  const viewports = this._getViewportsAsArray();
174
180
  viewports.forEach((vp) => {
@@ -2,7 +2,7 @@ import type { mat4 } from 'gl-matrix';
2
2
  import type { BlendModes, InterpolationType, OrientationAxis } from '../enums';
3
3
  import type { FlipDirection, IImageData, IVolumeInput, OrientationVectors, Point2, Point3, VolumeViewportProperties, ViewReferenceSpecifier, ReferenceCompatibleOptions, ViewReference, ICamera } from '../types';
4
4
  import type { VoiModifiedEventDetail } from '../types/EventTypes';
5
- import type { ViewportInput } from '../types/IViewport';
5
+ import type { PlaneRestriction, ViewportInput } from '../types/IViewport';
6
6
  import type { TransferFunctionNodes } from '../types/ITransferFunctionNode';
7
7
  import Viewport from './Viewport';
8
8
  import type vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
@@ -43,6 +43,7 @@ declare abstract class BaseVolumeViewport extends Viewport {
43
43
  isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
44
44
  scroll(delta?: number): void;
45
45
  abstract isInAcquisitionPlane(): boolean;
46
+ setViewPlane(planeRestriction: PlaneRestriction, viewPlaneNormal: any): void;
46
47
  setViewReference(viewRef: ViewReference): void;
47
48
  private setThreshold;
48
49
  setProperties({ voiRange, VOILUTFunction, invert, colormap, preset, interpolationType, slabThickness, }?: VolumeViewportProperties, volumeId?: string, suppressEvents?: boolean): void;
@@ -78,6 +79,7 @@ declare abstract class BaseVolumeViewport extends Viewport {
78
79
  getRendererContextPool(): vtkRenderer;
79
80
  getRendererTiled(): vtkRenderer;
80
81
  hasImageURI: (imageURI: string) => boolean;
82
+ protected _getViewUp(viewPlaneNormal: any): Point3;
81
83
  protected _getOrientationVectors(orientation: OrientationAxis | OrientationVectors): OrientationVectors;
82
84
  protected _getAcquisitionPlaneOrientation(): OrientationVectors;
83
85
  getSlabThickness(): number;
@@ -23,7 +23,7 @@ import getVolumeViewportScrollInfo from '../utilities/getVolumeViewportScrollInf
23
23
  import { actorIsA, isImageActor } from '../utilities/actorCheck';
24
24
  import snapFocalPointToSlice from '../utilities/snapFocalPointToSlice';
25
25
  import getVoiFromSigmoidRGBTransferFunction from '../utilities/getVoiFromSigmoidRGBTransferFunction';
26
- import isEqual, { isEqualNegative } from '../utilities/isEqual';
26
+ import isEqual, { isEqualAbs, isEqualNegative } from '../utilities/isEqual';
27
27
  import applyPreset from '../utilities/applyPreset';
28
28
  import imageIdToURI from '../utilities/imageIdToURI';
29
29
  import uuidv4 from '../utilities/uuidv4';
@@ -257,11 +257,11 @@ class BaseVolumeViewport extends Viewport {
257
257
  const volumeActors = this.getActors().filter((actorEntry) => actorIsA(actorEntry, 'vtkVolume'));
258
258
  return volumeActors.some(({ uid, referencedId }) => {
259
259
  const volume = cache.getVolume(referencedId || uid);
260
- if (!volume?.imageIds) {
260
+ if (!volume?.getImageIdIndex) {
261
261
  return false;
262
262
  }
263
- const volumeImageURIs = volume.imageIds.map(imageIdToURI);
264
- return volumeImageURIs.includes(imageURI);
263
+ return (volume.getImageIdIndex(imageURI) !== undefined ||
264
+ volume.getImageURIIndex(imageURI) !== undefined);
265
265
  });
266
266
  };
267
267
  this.getImageIds = (volumeId) => {
@@ -580,7 +580,8 @@ class BaseVolumeViewport extends Viewport {
580
580
  return false;
581
581
  }
582
582
  if (options?.withNavigation) {
583
- return true;
583
+ const { referencedImageId } = viewRef;
584
+ return !referencedImageId || this.hasImageURI(referencedImageId);
584
585
  }
585
586
  const currentSliceIndex = this.getSliceIndex();
586
587
  const { sliceIndex } = viewRef;
@@ -604,14 +605,50 @@ class BaseVolumeViewport extends Viewport {
604
605
  });
605
606
  this.render();
606
607
  }
608
+ setViewPlane(planeRestriction, viewPlaneNormal) {
609
+ const { point, inPlaneVector1, inPlaneVector2, FrameOfReferenceUID } = planeRestriction;
610
+ if (!inPlaneVector1) {
611
+ return this.setViewReference({
612
+ FrameOfReferenceUID,
613
+ cameraFocalPoint: point,
614
+ });
615
+ }
616
+ if (inPlaneVector2) {
617
+ const planeNormal = (vec3.cross(vec3.create(), inPlaneVector1, inPlaneVector2));
618
+ vec3.normalize(planeNormal, planeNormal);
619
+ return this.setViewReference({
620
+ FrameOfReferenceUID,
621
+ cameraFocalPoint: point,
622
+ viewPlaneNormal: planeNormal,
623
+ });
624
+ }
625
+ const dotNormal = vec3.dot(viewPlaneNormal, inPlaneVector1);
626
+ if (isEqual(dotNormal, 0)) {
627
+ return this.setViewReference({
628
+ FrameOfReferenceUID,
629
+ viewPlaneNormal,
630
+ cameraFocalPoint: point,
631
+ });
632
+ }
633
+ const planeNormal = (vec3.cross(vec3.create(), viewPlaneNormal, inPlaneVector1));
634
+ vec3.normalize(planeNormal, planeNormal);
635
+ return this.setViewReference({
636
+ FrameOfReferenceUID,
637
+ viewPlaneNormal: planeNormal,
638
+ cameraFocalPoint: point,
639
+ });
640
+ }
607
641
  setViewReference(viewRef) {
608
642
  if (!viewRef) {
609
643
  return;
610
644
  }
611
645
  const volumeId = this.getVolumeId();
612
- const { viewPlaneNormal: refViewPlaneNormal, FrameOfReferenceUID: refFrameOfReference, cameraFocalPoint, referencedImageId, viewUp, } = viewRef;
646
+ const { FrameOfReferenceUID: refFrameOfReference, cameraFocalPoint, referencedImageId, planeRestriction, viewPlaneNormal: refViewPlaneNormal, viewUp, } = viewRef;
613
647
  let { sliceIndex } = viewRef;
614
648
  const { focalPoint, viewPlaneNormal, position } = this.getCamera();
649
+ if (planeRestriction?.inPlaneVector1 && !refViewPlaneNormal) {
650
+ return this.setViewPlane(planeRestriction, viewPlaneNormal);
651
+ }
615
652
  const isNegativeNormal = isEqualNegative(viewPlaneNormal, refViewPlaneNormal);
616
653
  const isSameNormal = isEqual(viewPlaneNormal, refViewPlaneNormal);
617
654
  if (typeof sliceIndex === 'number' &&
@@ -969,13 +1006,35 @@ class BaseVolumeViewport extends Viewport {
969
1006
  }
970
1007
  return renderingEngine.offscreenMultiRenderWindow?.getRenderer(this.id);
971
1008
  }
1009
+ _getViewUp(viewPlaneNormal) {
1010
+ const { viewUp } = this.getCamera();
1011
+ const dot = vec3.dot(viewUp, viewPlaneNormal);
1012
+ if (isEqual(dot, 0)) {
1013
+ return viewUp;
1014
+ }
1015
+ if (isEqualAbs(viewPlaneNormal[0], 1)) {
1016
+ return [0, 0, 1];
1017
+ }
1018
+ if (isEqualAbs(viewPlaneNormal[1], 1)) {
1019
+ return [0, 0, 1];
1020
+ }
1021
+ if (isEqualAbs(viewPlaneNormal[2], 1)) {
1022
+ return [0, -1, 0];
1023
+ }
1024
+ const vupOrthogonal = (vec3.scaleAndAdd(vec3.create(), viewUp, viewPlaneNormal, -dot));
1025
+ vec3.normalize(vupOrthogonal, vupOrthogonal);
1026
+ return vupOrthogonal;
1027
+ }
972
1028
  _getOrientationVectors(orientation) {
973
1029
  if (typeof orientation === 'object') {
974
- if (orientation.viewPlaneNormal && orientation.viewUp) {
975
- return orientation;
1030
+ if (orientation.viewPlaneNormal) {
1031
+ return {
1032
+ ...orientation,
1033
+ viewUp: orientation.viewUp || this._getViewUp(orientation.viewPlaneNormal),
1034
+ };
976
1035
  }
977
1036
  else {
978
- throw new Error('Invalid orientation object. It must contain viewPlaneNormal and viewUp');
1037
+ throw new Error('Invalid orientation object. It must contain viewPlaneNormal');
979
1038
  }
980
1039
  }
981
1040
  else if (typeof orientation === 'string') {
@@ -1794,10 +1794,13 @@ class StackViewport extends Viewport {
1794
1794
  this.imageKeyToIndexMap.get(multiSliceReference.referencedImageId);
1795
1795
  return testIndex <= rangeEndSliceIndex && testIndex >= foundSliceIndex;
1796
1796
  }
1797
- if (!super.isReferenceViewable(viewRef, options)) {
1797
+ if (!super.isReferenceViewable(viewRef, {
1798
+ ...options,
1799
+ withOrientation: options?.asVolume,
1800
+ })) {
1798
1801
  return false;
1799
1802
  }
1800
- if (viewRef.volumeId) {
1803
+ if (viewRef.volumeId || viewRef.FrameOfReferenceUID) {
1801
1804
  return options.asVolume;
1802
1805
  }
1803
1806
  const { cameraFocalPoint } = viewRef;
@@ -574,7 +574,7 @@ class VideoViewport extends Viewport {
574
574
  }
575
575
  }
576
576
  getCurrentImageId(index = this.getCurrentImageIdIndex()) {
577
- const current = this.imageId.replace('/frames/1', `/frames/${index + 1}`);
577
+ const current = this.imageId?.replace('/frames/1', `/frames/${index + 1}`);
578
578
  return current;
579
579
  }
580
580
  getViewReferenceId(specifier = {}) {
@@ -4,7 +4,7 @@ import type { mat4 } from 'gl-matrix';
4
4
  import ViewportStatus from '../enums/ViewportStatus';
5
5
  import ViewportType from '../enums/ViewportType';
6
6
  import type { ICamera, ActorEntry, IRenderingEngine, ViewportInputOptions, Point2, Point3, FlipDirection, DisplayArea, ViewPresentation, ViewReference, ViewportProperties, ImageActor } from '../types';
7
- import type { ViewportInput, ViewReferenceSpecifier, ReferenceCompatibleOptions, ViewPresentationSelector, DataSetOptions } from '../types/IViewport';
7
+ import type { ViewportInput, ViewReferenceSpecifier, ReferenceCompatibleOptions, ViewPresentationSelector, DataSetOptions, PlaneRestriction } from '../types/IViewport';
8
8
  import type { vtkSlabCamera } from './vtkClasses/vtkSlabCamera';
9
9
  import type IImageCalibration from '../types/IImageCalibration';
10
10
  import { InterpolationType } from '../enums';
@@ -121,6 +121,7 @@ declare class Viewport {
121
121
  getClippingPlanesForActor(actorEntry?: ActorEntry): vtkPlane[];
122
122
  private _getWorldDistanceViewUpAndViewRight;
123
123
  getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
124
+ isPlaneViewable(planeRestriction: PlaneRestriction, options?: ReferenceCompatibleOptions): boolean;
124
125
  isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
125
126
  getViewPresentation(viewPresSel?: ViewPresentationSelector): ViewPresentation;
126
127
  setViewReference(viewRef: ViewReference): void;
@@ -14,6 +14,7 @@ import hasNaNValues from '../utilities/hasNaNValues';
14
14
  import { RENDERING_DEFAULTS } from '../constants';
15
15
  import { InterpolationType } from '../enums';
16
16
  import { deepClone } from '../utilities/deepClone';
17
+ import { updatePlaneRestriction } from '../utilities/updatePlaneRestriction';
17
18
  class Viewport {
18
19
  static { this.CameraViewPresentation = {
19
20
  rotation: true,
@@ -906,16 +907,52 @@ class Viewport {
906
907
  }
907
908
  getViewReference(viewRefSpecifier) {
908
909
  const { focalPoint: cameraFocalPoint, viewPlaneNormal, viewUp, } = this.getCamera();
910
+ const FrameOfReferenceUID = this.getFrameOfReferenceUID();
909
911
  const target = {
910
- FrameOfReferenceUID: this.getFrameOfReferenceUID(),
912
+ FrameOfReferenceUID,
911
913
  cameraFocalPoint,
912
914
  viewPlaneNormal,
913
915
  viewUp,
914
916
  sliceIndex: viewRefSpecifier?.sliceIndex ?? this.getSliceIndex(),
917
+ planeRestriction: {
918
+ FrameOfReferenceUID,
919
+ point: viewRefSpecifier?.points?.[0] || cameraFocalPoint,
920
+ inPlaneVector1: viewUp,
921
+ inPlaneVector2: (vec3.cross(vec3.create(), viewUp, viewPlaneNormal)),
922
+ },
915
923
  };
924
+ if (viewRefSpecifier?.points) {
925
+ updatePlaneRestriction(viewRefSpecifier.points, target.planeRestriction);
926
+ }
916
927
  return target;
917
928
  }
929
+ isPlaneViewable(planeRestriction, options) {
930
+ if (planeRestriction.FrameOfReferenceUID !== this.getFrameOfReferenceUID()) {
931
+ return false;
932
+ }
933
+ const { focalPoint, viewPlaneNormal } = this.getCamera();
934
+ const { point, inPlaneVector1, inPlaneVector2 } = planeRestriction;
935
+ if (options?.withOrientation) {
936
+ return true;
937
+ }
938
+ if (inPlaneVector1 &&
939
+ !isEqual(0, vec3.dot(viewPlaneNormal, inPlaneVector1))) {
940
+ return false;
941
+ }
942
+ if (inPlaneVector2 &&
943
+ !isEqual(0, vec3.dot(viewPlaneNormal, inPlaneVector2))) {
944
+ return false;
945
+ }
946
+ if (options?.withNavigation) {
947
+ return true;
948
+ }
949
+ const pointVector = vec3.sub(vec3.create(), point, focalPoint);
950
+ return isEqual(0, vec3.dot(pointVector, viewPlaneNormal));
951
+ }
918
952
  isReferenceViewable(viewRef, options) {
953
+ if (viewRef.planeRestriction) {
954
+ return this.isPlaneViewable(viewRef.planeRestriction, options);
955
+ }
919
956
  if (viewRef.FrameOfReferenceUID &&
920
957
  viewRef.FrameOfReferenceUID !== this.getFrameOfReferenceUID()) {
921
958
  return false;
@@ -0,0 +1,28 @@
1
+ import type { StatsInstance } from './types';
2
+ export declare class StatsOverlay implements StatsInstance {
3
+ private static instance;
4
+ dom: HTMLDivElement | null;
5
+ private currentMode;
6
+ private startTime;
7
+ private lastUpdateTime;
8
+ private frameCount;
9
+ private panels;
10
+ private animationFrameId;
11
+ private isSetup;
12
+ private constructor();
13
+ static getInstance(): StatsOverlay;
14
+ setup(): void;
15
+ cleanup(): void;
16
+ showPanel(panelType: number): void;
17
+ update(): void;
18
+ private createOverlayElement;
19
+ private applyOverlayStyles;
20
+ private handleClick;
21
+ private initializePanels;
22
+ private isMemoryAvailable;
23
+ private addPanel;
24
+ private startLoop;
25
+ private stopLoop;
26
+ private updateStats;
27
+ private updateMemoryPanel;
28
+ }
@@ -0,0 +1,137 @@
1
+ import { StatsPanel } from './StatsPanel';
2
+ import { PanelType } from './enums';
3
+ import { STATS_CONFIG, PANEL_CONFIGS, CONVERSION } from './constants';
4
+ export class StatsOverlay {
5
+ static { this.instance = null; }
6
+ constructor() {
7
+ this.dom = null;
8
+ this.currentMode = 0;
9
+ this.startTime = 0;
10
+ this.lastUpdateTime = 0;
11
+ this.frameCount = 0;
12
+ this.panels = new Map();
13
+ this.animationFrameId = null;
14
+ this.isSetup = false;
15
+ }
16
+ static getInstance() {
17
+ if (!StatsOverlay.instance) {
18
+ StatsOverlay.instance = new StatsOverlay();
19
+ }
20
+ return StatsOverlay.instance;
21
+ }
22
+ setup() {
23
+ if (this.isSetup) {
24
+ return;
25
+ }
26
+ try {
27
+ this.dom = this.createOverlayElement();
28
+ this.startTime = performance.now();
29
+ this.lastUpdateTime = this.startTime;
30
+ this.initializePanels();
31
+ this.showPanel(PanelType.FPS);
32
+ this.applyOverlayStyles();
33
+ document.body.appendChild(this.dom);
34
+ this.startLoop();
35
+ this.isSetup = true;
36
+ }
37
+ catch (error) {
38
+ console.warn('Failed to setup stats overlay:', error);
39
+ }
40
+ }
41
+ cleanup() {
42
+ this.stopLoop();
43
+ if (this.dom && this.dom.parentNode) {
44
+ this.dom.parentNode.removeChild(this.dom);
45
+ }
46
+ this.dom = null;
47
+ this.panels.clear();
48
+ this.isSetup = false;
49
+ }
50
+ showPanel(panelType) {
51
+ const children = Array.from(this.dom.children);
52
+ children.forEach((child, index) => {
53
+ child.style.display = index === panelType ? 'block' : 'none';
54
+ });
55
+ this.currentMode = panelType;
56
+ }
57
+ update() {
58
+ this.startTime = this.updateStats();
59
+ }
60
+ createOverlayElement() {
61
+ const element = document.createElement('div');
62
+ element.addEventListener('click', this.handleClick.bind(this), false);
63
+ return element;
64
+ }
65
+ applyOverlayStyles() {
66
+ Object.assign(this.dom.style, STATS_CONFIG.OVERLAY_STYLES);
67
+ }
68
+ handleClick(event) {
69
+ event.preventDefault();
70
+ const panelCount = this.dom.children.length;
71
+ this.showPanel((this.currentMode + 1) % panelCount);
72
+ }
73
+ initializePanels() {
74
+ const fpsPanel = new StatsPanel(PANEL_CONFIGS[PanelType.FPS].name, PANEL_CONFIGS[PanelType.FPS].foregroundColor, PANEL_CONFIGS[PanelType.FPS].backgroundColor);
75
+ this.addPanel(PanelType.FPS, fpsPanel);
76
+ const msPanel = new StatsPanel(PANEL_CONFIGS[PanelType.MS].name, PANEL_CONFIGS[PanelType.MS].foregroundColor, PANEL_CONFIGS[PanelType.MS].backgroundColor);
77
+ this.addPanel(PanelType.MS, msPanel);
78
+ if (this.isMemoryAvailable()) {
79
+ const memPanel = new StatsPanel(PANEL_CONFIGS[PanelType.MEMORY].name, PANEL_CONFIGS[PanelType.MEMORY].foregroundColor, PANEL_CONFIGS[PanelType.MEMORY].backgroundColor);
80
+ this.addPanel(PanelType.MEMORY, memPanel);
81
+ }
82
+ }
83
+ isMemoryAvailable() {
84
+ const perf = performance;
85
+ return perf.memory !== undefined;
86
+ }
87
+ addPanel(type, panel) {
88
+ this.dom.appendChild(panel.dom);
89
+ this.panels.set(type, panel);
90
+ }
91
+ startLoop() {
92
+ const loop = () => {
93
+ this.update();
94
+ this.animationFrameId = requestAnimationFrame(loop);
95
+ };
96
+ this.animationFrameId = requestAnimationFrame(loop);
97
+ }
98
+ stopLoop() {
99
+ if (this.animationFrameId !== null) {
100
+ cancelAnimationFrame(this.animationFrameId);
101
+ this.animationFrameId = null;
102
+ }
103
+ }
104
+ updateStats() {
105
+ this.frameCount++;
106
+ const currentTime = performance.now();
107
+ const deltaTime = currentTime - this.startTime;
108
+ const msPanel = this.panels.get(PanelType.MS);
109
+ if (msPanel) {
110
+ msPanel.update(deltaTime, STATS_CONFIG.MAX_MS_VALUE);
111
+ }
112
+ if (currentTime >= this.lastUpdateTime + STATS_CONFIG.UPDATE_INTERVAL) {
113
+ const fps = (this.frameCount * CONVERSION.MS_PER_SECOND) /
114
+ (currentTime - this.lastUpdateTime);
115
+ const fpsPanel = this.panels.get(PanelType.FPS);
116
+ if (fpsPanel) {
117
+ fpsPanel.update(fps, STATS_CONFIG.MAX_FPS_VALUE);
118
+ }
119
+ this.lastUpdateTime = currentTime;
120
+ this.frameCount = 0;
121
+ this.updateMemoryPanel();
122
+ }
123
+ return currentTime;
124
+ }
125
+ updateMemoryPanel() {
126
+ const memPanel = this.panels.get(PanelType.MEMORY);
127
+ if (!memPanel) {
128
+ return;
129
+ }
130
+ const perf = performance;
131
+ if (perf.memory) {
132
+ const memoryMB = perf.memory.usedJSHeapSize / CONVERSION.BYTES_TO_MB;
133
+ const maxMemoryMB = perf.memory.jsHeapSizeLimit / CONVERSION.BYTES_TO_MB;
134
+ memPanel.update(memoryMB, maxMemoryMB);
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,24 @@
1
+ import type { Panel } from './types';
2
+ export declare class StatsPanel implements Panel {
3
+ dom: HTMLCanvasElement;
4
+ private context;
5
+ private minValue;
6
+ private maxValue;
7
+ private readonly name;
8
+ private readonly foregroundColor;
9
+ private readonly backgroundColor;
10
+ private readonly devicePixelRatio;
11
+ private readonly dimensions;
12
+ constructor(name: string, foregroundColor: string, backgroundColor: string);
13
+ update(value: number, maxValue: number): void;
14
+ private calculateDimensions;
15
+ private createCanvas;
16
+ private initializeContext;
17
+ private drawInitialPanel;
18
+ private updateMinMax;
19
+ private clearTextArea;
20
+ private drawText;
21
+ private formatText;
22
+ private scrollGraph;
23
+ private drawNewValue;
24
+ }
@@ -0,0 +1,101 @@
1
+ import { PANEL_CONFIG } from './constants';
2
+ export class StatsPanel {
3
+ constructor(name, foregroundColor, backgroundColor) {
4
+ this.minValue = Infinity;
5
+ this.maxValue = 0;
6
+ this.name = name;
7
+ this.foregroundColor = foregroundColor;
8
+ this.backgroundColor = backgroundColor;
9
+ this.devicePixelRatio = Math.round(window.devicePixelRatio || 1);
10
+ this.dimensions = this.calculateDimensions();
11
+ this.dom = this.createCanvas();
12
+ this.context = this.initializeContext();
13
+ this.drawInitialPanel();
14
+ }
15
+ update(value, maxValue) {
16
+ this.updateMinMax(value);
17
+ this.clearTextArea();
18
+ this.drawText(value);
19
+ this.scrollGraph();
20
+ this.drawNewValue(value, maxValue);
21
+ }
22
+ calculateDimensions() {
23
+ const pr = this.devicePixelRatio;
24
+ return {
25
+ width: PANEL_CONFIG.WIDTH * pr,
26
+ height: PANEL_CONFIG.HEIGHT * pr,
27
+ textX: PANEL_CONFIG.TEXT_PADDING * pr,
28
+ textY: PANEL_CONFIG.TEXT_Y_OFFSET * pr,
29
+ graphX: PANEL_CONFIG.TEXT_PADDING * pr,
30
+ graphY: PANEL_CONFIG.GRAPH_Y_OFFSET * pr,
31
+ graphWidth: PANEL_CONFIG.GRAPH_WIDTH * pr,
32
+ graphHeight: PANEL_CONFIG.GRAPH_HEIGHT * pr,
33
+ };
34
+ }
35
+ createCanvas() {
36
+ const canvas = document.createElement('canvas');
37
+ canvas.width = this.dimensions.width;
38
+ canvas.height = this.dimensions.height;
39
+ canvas.style.cssText = `width:${PANEL_CONFIG.WIDTH}px;height:${PANEL_CONFIG.HEIGHT}px`;
40
+ return canvas;
41
+ }
42
+ initializeContext() {
43
+ const ctx = this.dom.getContext('2d');
44
+ if (!ctx) {
45
+ throw new Error('Failed to get 2D context');
46
+ }
47
+ ctx.font = `bold ${PANEL_CONFIG.FONT_SIZE * this.devicePixelRatio}px ${PANEL_CONFIG.FONT_FAMILY}`;
48
+ ctx.textBaseline = 'top';
49
+ return ctx;
50
+ }
51
+ drawInitialPanel() {
52
+ const { width, height, textX, textY, graphX, graphY, graphWidth, graphHeight, } = this.dimensions;
53
+ this.context.fillStyle = this.backgroundColor;
54
+ this.context.fillRect(0, 0, width, height);
55
+ this.context.fillStyle = this.foregroundColor;
56
+ this.context.fillText(this.name, textX, textY);
57
+ this.context.fillRect(graphX, graphY, graphWidth, graphHeight);
58
+ this.context.fillStyle = this.backgroundColor;
59
+ this.context.globalAlpha = PANEL_CONFIG.GRAPH_ALPHA;
60
+ this.context.fillRect(graphX, graphY, graphWidth, graphHeight);
61
+ this.context.globalAlpha = 1;
62
+ }
63
+ updateMinMax(value) {
64
+ this.minValue = Math.min(this.minValue, value);
65
+ this.maxValue = Math.max(this.maxValue, value);
66
+ }
67
+ clearTextArea() {
68
+ const { width, graphY } = this.dimensions;
69
+ this.context.fillStyle = this.backgroundColor;
70
+ this.context.fillRect(0, 0, width, graphY);
71
+ }
72
+ drawText(value) {
73
+ const { textX, textY } = this.dimensions;
74
+ const text = this.formatText(value);
75
+ this.context.fillStyle = this.foregroundColor;
76
+ this.context.fillText(text, textX, textY);
77
+ }
78
+ formatText(value) {
79
+ const roundedValue = Math.round(value);
80
+ const roundedMin = Math.round(this.minValue);
81
+ const roundedMax = Math.round(this.maxValue);
82
+ return `${roundedValue} ${this.name} (${roundedMin}-${roundedMax})`;
83
+ }
84
+ scrollGraph() {
85
+ const { graphX, graphY, graphWidth, graphHeight } = this.dimensions;
86
+ const pr = this.devicePixelRatio;
87
+ this.context.drawImage(this.dom, graphX + pr, graphY, graphWidth - pr, graphHeight, graphX, graphY, graphWidth - pr, graphHeight);
88
+ }
89
+ drawNewValue(value, maxValue) {
90
+ const { graphX, graphY, graphWidth, graphHeight } = this.dimensions;
91
+ const pr = this.devicePixelRatio;
92
+ const x = graphX + graphWidth - pr;
93
+ this.context.fillStyle = this.foregroundColor;
94
+ this.context.fillRect(x, graphY, pr, graphHeight);
95
+ const normalizedHeight = Math.round((1 - value / maxValue) * graphHeight);
96
+ this.context.fillStyle = this.backgroundColor;
97
+ this.context.globalAlpha = PANEL_CONFIG.GRAPH_ALPHA;
98
+ this.context.fillRect(x, graphY, pr, normalizedHeight);
99
+ this.context.globalAlpha = 1;
100
+ }
101
+ }
@@ -0,0 +1,44 @@
1
+ declare const PANEL_CONFIG: {
2
+ readonly WIDTH: 160;
3
+ readonly HEIGHT: 96;
4
+ readonly TEXT_PADDING: 3;
5
+ readonly TEXT_Y_OFFSET: 2;
6
+ readonly GRAPH_Y_OFFSET: 15;
7
+ readonly GRAPH_WIDTH: 150;
8
+ readonly GRAPH_HEIGHT: 70;
9
+ readonly FONT_SIZE: 9;
10
+ readonly FONT_FAMILY: "Helvetica,Arial,sans-serif";
11
+ readonly GRAPH_ALPHA: 0.9;
12
+ };
13
+ declare const STATS_CONFIG: {
14
+ readonly UPDATE_INTERVAL: 1000;
15
+ readonly MAX_MS_VALUE: 200;
16
+ readonly MAX_FPS_VALUE: 300;
17
+ readonly OVERLAY_STYLES: {
18
+ readonly position: "fixed";
19
+ readonly top: "0px";
20
+ readonly right: "0px";
21
+ readonly left: "auto";
22
+ readonly zIndex: "9999";
23
+ readonly cursor: "pointer";
24
+ readonly opacity: "0.9";
25
+ };
26
+ };
27
+ declare const CONVERSION: {
28
+ readonly BYTES_TO_MB: 1048576;
29
+ readonly MS_PER_SECOND: 1000;
30
+ };
31
+ declare const PANEL_CONFIGS: readonly [{
32
+ readonly name: "FPS";
33
+ readonly foregroundColor: "#0ff";
34
+ readonly backgroundColor: "#002";
35
+ }, {
36
+ readonly name: "MS";
37
+ readonly foregroundColor: "#0f0";
38
+ readonly backgroundColor: "#020";
39
+ }, {
40
+ readonly name: "MB";
41
+ readonly foregroundColor: "#f08";
42
+ readonly backgroundColor: "#201";
43
+ }];
44
+ export { PANEL_CONFIG, STATS_CONFIG, CONVERSION, PANEL_CONFIGS };
@@ -0,0 +1,36 @@
1
+ const PANEL_CONFIG = {
2
+ WIDTH: 160,
3
+ HEIGHT: 96,
4
+ TEXT_PADDING: 3,
5
+ TEXT_Y_OFFSET: 2,
6
+ GRAPH_Y_OFFSET: 15,
7
+ GRAPH_WIDTH: 150,
8
+ GRAPH_HEIGHT: 70,
9
+ FONT_SIZE: 9,
10
+ FONT_FAMILY: 'Helvetica,Arial,sans-serif',
11
+ GRAPH_ALPHA: 0.9,
12
+ };
13
+ const STATS_CONFIG = {
14
+ UPDATE_INTERVAL: 1000,
15
+ MAX_MS_VALUE: 200,
16
+ MAX_FPS_VALUE: 300,
17
+ OVERLAY_STYLES: {
18
+ position: 'fixed',
19
+ top: '0px',
20
+ right: '0px',
21
+ left: 'auto',
22
+ zIndex: '9999',
23
+ cursor: 'pointer',
24
+ opacity: '0.9',
25
+ },
26
+ };
27
+ const CONVERSION = {
28
+ BYTES_TO_MB: 1048576,
29
+ MS_PER_SECOND: 1000,
30
+ };
31
+ const PANEL_CONFIGS = [
32
+ { name: 'FPS', foregroundColor: '#0ff', backgroundColor: '#002' },
33
+ { name: 'MS', foregroundColor: '#0f0', backgroundColor: '#020' },
34
+ { name: 'MB', foregroundColor: '#f08', backgroundColor: '#201' },
35
+ ];
36
+ export { PANEL_CONFIG, STATS_CONFIG, CONVERSION, PANEL_CONFIGS };
@@ -0,0 +1,6 @@
1
+ declare enum PanelType {
2
+ FPS = 0,
3
+ MS = 1,
4
+ MEMORY = 2
5
+ }
6
+ export { PanelType };
@@ -0,0 +1,7 @@
1
+ var PanelType;
2
+ (function (PanelType) {
3
+ PanelType[PanelType["FPS"] = 0] = "FPS";
4
+ PanelType[PanelType["MS"] = 1] = "MS";
5
+ PanelType[PanelType["MEMORY"] = 2] = "MEMORY";
6
+ })(PanelType || (PanelType = {}));
7
+ export { PanelType };
@@ -0,0 +1,2 @@
1
+ import { StatsOverlay as Class } from './StatsOverlay';
2
+ export declare const StatsOverlay: Class;
@@ -0,0 +1,2 @@
1
+ import { StatsOverlay as Class } from './StatsOverlay';
2
+ export const StatsOverlay = Class.getInstance();
@@ -0,0 +1,22 @@
1
+ interface Panel {
2
+ dom: HTMLCanvasElement;
3
+ update: (value: number, maxValue: number) => void;
4
+ }
5
+ interface StatsInstance {
6
+ dom: HTMLDivElement;
7
+ showPanel: (id: number) => void;
8
+ update: () => void;
9
+ destroy?: () => void;
10
+ }
11
+ interface PerformanceWithMemory extends Performance {
12
+ memory?: {
13
+ usedJSHeapSize: number;
14
+ jsHeapSizeLimit: number;
15
+ };
16
+ }
17
+ interface PanelConfig {
18
+ name: string;
19
+ foregroundColor: string;
20
+ backgroundColor: string;
21
+ }
22
+ export type { Panel, StatsInstance, PerformanceWithMemory, PanelConfig };
package/dist/esm/init.js CHANGED
@@ -14,6 +14,9 @@ const defaultConfig = {
14
14
  renderingEngineMode: RenderingEngineModeEnum.ContextPool,
15
15
  webGlContextCount: 7,
16
16
  },
17
+ debug: {
18
+ statsOverlay: false,
19
+ },
17
20
  peerImport: (moduleId) => null,
18
21
  };
19
22
  let config = {
@@ -11,6 +11,9 @@ interface Cornerstone3DConfig {
11
11
  renderingEngineMode?: RenderingEngineModeType;
12
12
  webGlContextCount?: number;
13
13
  };
14
+ debug: {
15
+ statsOverlay?: boolean;
16
+ };
14
17
  peerImport?: (moduleId: string) => Promise<any>;
15
18
  }
16
19
  export type { Cornerstone3DConfig as default };
@@ -24,8 +24,15 @@ export interface ReferenceCompatibleOptions {
24
24
  export type ReferencedImageRange = ViewReference & {
25
25
  referencedImageId: string;
26
26
  };
27
- export type ViewReference = {
27
+ export type PlaneRestriction = {
28
+ FrameOfReferenceUID: string;
29
+ point: Point3;
30
+ inPlaneVector1?: Point3;
31
+ inPlaneVector2?: Point3;
32
+ };
33
+ export interface ViewReference {
28
34
  FrameOfReferenceUID?: string;
35
+ planeRestriction?: PlaneRestriction;
29
36
  referencedImageId?: string;
30
37
  referencedImageURI?: string;
31
38
  multiSliceReference?: ReferencedImageRange;
@@ -35,7 +42,7 @@ export type ViewReference = {
35
42
  sliceIndex?: number;
36
43
  volumeId?: string;
37
44
  bounds?: BoundsLPS;
38
- };
45
+ }
39
46
  export interface ViewPresentation {
40
47
  slabThickness?: number;
41
48
  rotation?: number;
@@ -94,6 +94,7 @@ export * as logger from './logger';
94
94
  import { calculateNeighborhoodStats } from './calculateNeighborhoodStats';
95
95
  import getPixelSpacingInformation from './getPixelSpacingInformation';
96
96
  import { asArray } from './asArray';
97
+ export { updatePlaneRestriction } from './updatePlaneRestriction';
97
98
  declare const getViewportModality: (viewport: IViewport, volumeId?: string) => string;
98
99
  export * from './isEqual';
99
100
  export { FrameRange, eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, fnv1aHash, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, getMinMax, getRuntimeId, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, HistoryMemo, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, transformWorldToIndexContinuous, createSubVolume, getVolumeDirectionVectors, calculateSpacingBetweenImageIds, getImageDataMetadata, buildMetadata, calculateNeighborhoodStats, getPixelSpacingInformation, asArray, };
@@ -95,6 +95,7 @@ export * as logger from './logger';
95
95
  import { calculateNeighborhoodStats } from './calculateNeighborhoodStats';
96
96
  import getPixelSpacingInformation from './getPixelSpacingInformation';
97
97
  import { asArray } from './asArray';
98
+ export { updatePlaneRestriction } from './updatePlaneRestriction';
98
99
  const getViewportModality = (viewport, volumeId) => _getViewportModality(viewport, volumeId, cache.getVolume);
99
100
  export * from './isEqual';
100
101
  export { FrameRange, eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, createLinearRGBTransferFunction, scaleRgbTransferFunction, triggerEvent, imageIdToURI, fnv1aHash, calibratedPixelSpacingMetadataProvider, clamp, uuidv4, getMinMax, getRuntimeId, isOpposite, getViewportModality, windowLevel, convertToGrayscale, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, transformIndexToWorld, loadImageToCanvas, renderToCanvasCPU, renderToCanvasGPU, worldToImageCoords, imageToWorldCoords, getVolumeSliceRangeInfo, getVolumeViewportScrollInfo, getSliceRange, snapFocalPointToSlice, getImageSliceDataForVolumeViewport, isImageActor, isPTPrescaledWithSUV, actorIsA, getViewportsWithImageURI, getClosestStackImageIndexForPoint, getCurrentVolumeViewportSlice, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, hasNaNValues, applyPreset, deepMerge, PointsManager, getScalingParameters, colormap, getImageLegacy, ProgressiveIterator, decimate, imageRetrieveMetadataProvider, transferFunctionUtils, updateVTKImageDataWithCornerstoneImage, sortImageIdsAndGetSpacing, makeVolumeMetadata, isValidVolume, genericMetadataProvider, isVideoTransferSyntax, HistoryMemo, generateVolumePropsFromImageIds, getBufferConfiguration, VoxelManager, RLEVoxelMap, convertStackToVolumeViewport, convertVolumeToStackViewport, roundNumber, roundToPrecision, getViewportImageIds, getRandomSampleFromArray, getVolumeId, color, hasFloatScalingParameters, getDynamicVolumeInfo, autoLoad, scaleArray, deepClone, splitImageIdsBy4DTags, pointInShapeCallback, deepEqual, jumpToSlice, scroll, clip, transformWorldToIndexContinuous, createSubVolume, getVolumeDirectionVectors, calculateSpacingBetweenImageIds, getImageDataMetadata, buildMetadata, calculateNeighborhoodStats, getPixelSpacingInformation, asArray, };
@@ -1,5 +1,5 @@
1
- export default function isEqual<ValueType>(v1: ValueType, v2: ValueType, tolerance?: number): boolean;
2
- declare const isEqualNegative: <ValueType>(v1: ValueType, v2: ValueType, tolerance?: any) => boolean;
3
- declare const isEqualAbs: <ValueType>(v1: ValueType, v2: ValueType, tolerance?: any) => boolean;
4
- declare function isNumber(n: number[] | number): boolean;
5
- export { isEqualNegative, isEqual, isEqualAbs, isNumber };
1
+ export declare function isEqual<ValueType>(v1: ValueType, v2: ValueType, tolerance?: number): boolean;
2
+ export declare const isEqualNegative: <ValueType>(v1: ValueType, v2: ValueType, tolerance?: any) => boolean;
3
+ export declare const isEqualAbs: <ValueType>(v1: ValueType, v2: ValueType, tolerance?: any) => boolean;
4
+ export declare function isNumber(n: number[] | number): boolean;
5
+ export default isEqual;
@@ -23,7 +23,7 @@ function isNumberArrayLike(value) {
23
23
  value.length > 0 &&
24
24
  typeof value[0] === 'number');
25
25
  }
26
- export default function isEqual(v1, v2, tolerance = 1e-5) {
26
+ export function isEqual(v1, v2, tolerance = 1e-5) {
27
27
  if (typeof v1 !== typeof v2 || v1 === null || v2 === null) {
28
28
  return false;
29
29
  }
@@ -37,12 +37,12 @@ export default function isEqual(v1, v2, tolerance = 1e-5) {
37
37
  }
38
38
  const negative = (v) => typeof v === 'number' ? -v : v?.map ? v.map(negative) : !v;
39
39
  const abs = (v) => typeof v === 'number' ? Math.abs(v) : v?.map ? v.map(abs) : v;
40
- const isEqualNegative = (v1, v2, tolerance = undefined) => isEqual(v1, negative(v2), tolerance);
41
- const isEqualAbs = (v1, v2, tolerance = undefined) => isEqual(abs(v1), abs(v2), tolerance);
42
- function isNumber(n) {
40
+ export const isEqualNegative = (v1, v2, tolerance = undefined) => isEqual(v1, negative(v2), tolerance);
41
+ export const isEqualAbs = (v1, v2, tolerance = undefined) => isEqual(abs(v1), abs(v2), tolerance);
42
+ export function isNumber(n) {
43
43
  if (Array.isArray(n)) {
44
44
  return isNumber(n[0]);
45
45
  }
46
46
  return isFinite(n) && !isNaN(n);
47
47
  }
48
- export { isEqualNegative, isEqual, isEqualAbs, isNumber };
48
+ export default isEqual;
@@ -0,0 +1,2 @@
1
+ import type { Point3, ViewReference } from '../types';
2
+ export declare function updatePlaneRestriction(points: Point3[], reference: ViewReference): import("../types/IViewport").PlaneRestriction;
@@ -0,0 +1,41 @@
1
+ import { isEqual } from '../utilities/isEqual';
2
+ import { vec3 } from 'gl-matrix';
3
+ const ORTHOGONAL_TEST_VALUE = 0.95;
4
+ export function updatePlaneRestriction(points, reference) {
5
+ if (!points?.length || !reference.FrameOfReferenceUID) {
6
+ return;
7
+ }
8
+ reference.planeRestriction ||= {
9
+ FrameOfReferenceUID: reference.FrameOfReferenceUID,
10
+ point: points[0],
11
+ inPlaneVector1: null,
12
+ inPlaneVector2: null,
13
+ };
14
+ const { planeRestriction } = reference;
15
+ if (points.length === 1) {
16
+ planeRestriction.inPlaneVector1 = null;
17
+ planeRestriction.inPlaneVector2 = null;
18
+ return planeRestriction;
19
+ }
20
+ const v1 = vec3.sub(vec3.create(), points[0], points[Math.floor(points.length / 2)]);
21
+ vec3.normalize(v1, v1);
22
+ planeRestriction.inPlaneVector1 = v1;
23
+ planeRestriction.inPlaneVector2 = null;
24
+ const n = points.length;
25
+ if (n > 2) {
26
+ for (let i = Math.floor(n / 3); i < n; i++) {
27
+ const testVector = vec3.sub(vec3.create(), points[i], points[0]);
28
+ const length = vec3.length(testVector);
29
+ if (isEqual(length, 0)) {
30
+ continue;
31
+ }
32
+ if (vec3.dot(testVector, planeRestriction.inPlaneVector1) <
33
+ length * ORTHOGONAL_TEST_VALUE) {
34
+ vec3.normalize(testVector, testVector);
35
+ planeRestriction.inPlaneVector2 = testVector;
36
+ return planeRestriction;
37
+ }
38
+ }
39
+ }
40
+ return planeRestriction;
41
+ }
@@ -1 +1 @@
1
- export declare const version = "3.30.3";
1
+ export declare const version = "3.31.1";
@@ -1 +1 @@
1
- export const version = '3.30.3';
1
+ export const version = '3.31.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "3.30.3",
3
+ "version": "3.31.1",
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": "ff959f1bf57c60b727e2cd6563db13b194cbabb0"
100
+ "gitHead": "0fc65e9cc1d86a9a8b97ee9e7eb5802ce5e1e635"
101
101
  }