@cornerstonejs/core 1.23.4 → 1.24.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.
Files changed (53) hide show
  1. package/dist/cjs/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.js +3 -5
  2. package/dist/cjs/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.js.map +1 -1
  3. package/dist/cjs/types/index.d.ts +2 -2
  4. package/dist/cjs/utilities/clamp.d.ts +2 -0
  5. package/dist/cjs/utilities/clamp.js +9 -0
  6. package/dist/cjs/utilities/clamp.js.map +1 -0
  7. package/dist/cjs/utilities/eventListener/MultiTargetEventListenerManager.d.ts +7 -0
  8. package/dist/cjs/utilities/eventListener/MultiTargetEventListenerManager.js +39 -0
  9. package/dist/cjs/utilities/eventListener/MultiTargetEventListenerManager.js.map +1 -0
  10. package/dist/cjs/utilities/eventListener/TargetEventListeners.d.ts +15 -0
  11. package/dist/cjs/utilities/eventListener/TargetEventListeners.js +136 -0
  12. package/dist/cjs/utilities/eventListener/TargetEventListeners.js.map +1 -0
  13. package/dist/cjs/utilities/eventListener/index.d.ts +2 -0
  14. package/dist/cjs/utilities/eventListener/index.js +8 -0
  15. package/dist/cjs/utilities/eventListener/index.js.map +1 -0
  16. package/dist/cjs/utilities/getViewportModality.d.ts +3 -0
  17. package/dist/cjs/utilities/getViewportModality.js +25 -0
  18. package/dist/cjs/utilities/getViewportModality.js.map +1 -0
  19. package/dist/cjs/utilities/index.d.ts +4 -1
  20. package/dist/cjs/utilities/index.js +8 -2
  21. package/dist/cjs/utilities/index.js.map +1 -1
  22. package/dist/esm/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.js +1 -3
  23. package/dist/esm/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.js.map +1 -1
  24. package/dist/esm/types/index.d.ts +2 -2
  25. package/dist/esm/utilities/clamp.d.ts +2 -0
  26. package/dist/esm/utilities/clamp.js +5 -0
  27. package/dist/esm/utilities/clamp.js.map +1 -0
  28. package/dist/esm/utilities/eventListener/MultiTargetEventListenerManager.d.ts +7 -0
  29. package/dist/esm/utilities/eventListener/MultiTargetEventListenerManager.js +32 -0
  30. package/dist/esm/utilities/eventListener/MultiTargetEventListenerManager.js.map +1 -0
  31. package/dist/esm/utilities/eventListener/TargetEventListeners.d.ts +15 -0
  32. package/dist/esm/utilities/eventListener/TargetEventListeners.js +129 -0
  33. package/dist/esm/utilities/eventListener/TargetEventListeners.js.map +1 -0
  34. package/dist/esm/utilities/eventListener/index.d.ts +2 -0
  35. package/dist/esm/utilities/eventListener/index.js +3 -0
  36. package/dist/esm/utilities/eventListener/index.js.map +1 -0
  37. package/dist/esm/utilities/getViewportModality.d.ts +3 -0
  38. package/dist/esm/utilities/getViewportModality.js +17 -0
  39. package/dist/esm/utilities/getViewportModality.js.map +1 -0
  40. package/dist/esm/utilities/index.d.ts +4 -1
  41. package/dist/esm/utilities/index.js +4 -1
  42. package/dist/esm/utilities/index.js.map +1 -1
  43. package/dist/umd/index.js +1 -1
  44. package/dist/umd/index.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts +1 -4
  47. package/src/types/index.ts +2 -1
  48. package/src/utilities/clamp.ts +5 -0
  49. package/src/utilities/eventListener/MultiTargetEventListenerManager.ts +90 -0
  50. package/src/utilities/eventListener/TargetEventListeners.ts +278 -0
  51. package/src/utilities/eventListener/index.ts +2 -0
  52. package/src/utilities/getViewportModality.ts +23 -0
  53. package/src/utilities/index.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "1.23.4",
3
+ "version": "1.24.0",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/esm/index.d.ts",
@@ -46,5 +46,5 @@
46
46
  "type": "individual",
47
47
  "url": "https://ohif.org/donate"
48
48
  },
49
- "gitHead": "fbd7662554d78d9db96d83e3d29369db6024b70b"
49
+ "gitHead": "7db76a5a136c78e1532f0abe90f4c2f9613ac68f"
50
50
  }
@@ -8,10 +8,7 @@ import storedPixelDataToCanvasImageDataPseudocolorLUT from './storedPixelDataToC
8
8
  import storedPixelDataToCanvasImageDataPseudocolorLUTPET from './storedPixelDataToCanvasImageDataPseudocolorLUTPET';
9
9
  import * as colors from '../colors/index';
10
10
  import type { IImage, CPUFallbackEnabledElement } from '../../../../types';
11
-
12
- function clamp(value: number, min: number, max: number) {
13
- return Math.max(min, Math.min(max, value));
14
- }
11
+ import { clamp } from '../../../../utilities';
15
12
 
16
13
  /**
17
14
  * Returns an appropriate canvas to render the Image. If the canvas available in the cache is appropriate
@@ -14,7 +14,7 @@ import type IRegisterImageLoader from './IRegisterImageLoader';
14
14
  import type IStreamingVolumeProperties from './IStreamingVolumeProperties';
15
15
  import type CustomEventType from './CustomEventType';
16
16
  import type { IViewport, PublicViewportInput } from './IViewport';
17
- import type { VolumeActor, Actor, ActorEntry } from './IActor';
17
+ import type { VolumeActor, Actor, ActorEntry, ImageActor } from './IActor';
18
18
  import type {
19
19
  IImageLoadObject,
20
20
  IVolumeLoadObject,
@@ -121,6 +121,7 @@ export type {
121
121
  VolumeActor,
122
122
  Actor,
123
123
  ActorEntry,
124
+ ImageActor,
124
125
  IImageLoadObject,
125
126
  IVolumeLoadObject,
126
127
  IVolumeInput,
@@ -0,0 +1,5 @@
1
+ function clamp(value: number, min: number, max: number) {
2
+ return Math.max(min, Math.min(max, value));
3
+ }
4
+
5
+ export { clamp as default, clamp };
@@ -0,0 +1,90 @@
1
+ import TargetEventListeners from './TargetEventListeners';
2
+
3
+ /**
4
+ * MultiTargetEventListenerManager allows you to add event listeners to multiple
5
+ * HTML elements (targets) with support for event types with namespace,
6
+ * allow removing events without having to pass a callback and makes it possible
7
+ * to remove all event lsiteners from all HTML elements in a much simpler avoiding
8
+ * leaving listeners behind which would result in memory leaks.
9
+ *
10
+ * @example
11
+ * Adding and removing event listeners
12
+ * ```javascript
13
+ * const eventListenerManager = new MultiTargetEventListenerManager()
14
+ * const element1 = document.getElementById('foo');
15
+ * const element2 = document.getElementById('bar');
16
+ * const mouseoverCallback = () => { };
17
+ * const mouseoutCallback = () => { };
18
+ * const dragCallback = () => { };
19
+ *
20
+ * eventListenerManager.addEventListener(element1, 'mouseover', mouseoverCallback);
21
+ * eventListenerManager.addEventListener(element1, 'mouseout', mouseoutCallback);
22
+ *
23
+ * eventListenerManager.addEventListener(element2, 'voi.mousemove', dragCallback);
24
+ * eventListenerManager.addEventListener(element2, 'voi.drag', dragCallback);
25
+ * eventListenerManager.addEventListener(element2, 'voi.mouseup', () => {
26
+ * // do not need to store a reference of this function
27
+ * }));
28
+ *
29
+ * // Removes a specific event listener from element2
30
+ * eventListenerManager.removeEventListener(element2, 'voi.mousemove', dragCallback)
31
+ *
32
+ * // Removes all "mouseup" event listeners added to "voi" namespace on element2
33
+ * eventListenerManager.removeEventListener(element2, 'voi.mouseup')
34
+ *
35
+ * // Removes all event listeners added to element1 and element2
36
+ * eventListenerManager.reset();
37
+ * ```
38
+ */
39
+ class MultiTargetEventListenerManager {
40
+ private _targetsEventListeners = new Map<EventTarget, TargetEventListeners>();
41
+
42
+ public addEventListener(
43
+ target: EventTarget,
44
+ type: string,
45
+ callback: EventListener,
46
+ options?: AddEventListenerOptions
47
+ ) {
48
+ let eventListeners = this._targetsEventListeners.get(target);
49
+
50
+ if (!eventListeners) {
51
+ eventListeners = new TargetEventListeners(target);
52
+ this._targetsEventListeners.set(target, eventListeners);
53
+ }
54
+
55
+ eventListeners.addEventListener(type, callback, options);
56
+ }
57
+
58
+ public removeEventListener(
59
+ target: EventTarget,
60
+ type: string,
61
+ callback?: EventListener,
62
+ options?: EventListenerOptions
63
+ ) {
64
+ const eventListeners = this._targetsEventListeners.get(target);
65
+
66
+ if (!eventListeners) {
67
+ return;
68
+ }
69
+
70
+ eventListeners.removeEventListener(type, callback, options);
71
+
72
+ if (eventListeners.isEmpty) {
73
+ this._targetsEventListeners.delete(target);
74
+ }
75
+ }
76
+
77
+ public reset() {
78
+ Array.from(this._targetsEventListeners.entries()).forEach(
79
+ ([target, targetEventListeners]) => {
80
+ targetEventListeners.reset();
81
+ this._targetsEventListeners.delete(target);
82
+ }
83
+ );
84
+ }
85
+ }
86
+
87
+ export {
88
+ MultiTargetEventListenerManager as default,
89
+ MultiTargetEventListenerManager,
90
+ };
@@ -0,0 +1,278 @@
1
+ enum EventListenerPhases {
2
+ None = 0,
3
+ Capture = 1,
4
+ Bubble = 2,
5
+ }
6
+
7
+ type ListenersMap = Map<EventListener, EventListenerPhases>;
8
+
9
+ /**
10
+ * TargetEventListeners adds support for event types with namespace, allow
11
+ * removing events without having to pass a callback and makes it possible to
12
+ * remove all event listeners in a much simpler avoiding leaving listeners behind
13
+ * which would result in memory leaks.
14
+ *
15
+ * @example
16
+ * Creating a new TargetEventListeners instance
17
+ * ```javascript
18
+ * const element = document.getElementById('foo');
19
+ * const targetEventListeners = new TargetEventListeners(element)
20
+ * ```
21
+ *
22
+ * @example
23
+ * Adding and removing event listeners
24
+ * ```javascript
25
+ * const dragCallback = () => { };
26
+ *
27
+ * targetEventListeners.addEventListener('voi.mousemove', dragCallback);
28
+ * targetEventListeners.addEventListener('voi.drag', dragCallback);
29
+ * targetEventListeners.addEventListener('voi.mouseup', () => {
30
+ * // do not need to store a reference of this function
31
+ * }));
32
+ *
33
+ * // Removes a specific event listener
34
+ * targetEventListeners.removeEventListener('voi.mousemove', dragCallback)
35
+ *
36
+ * // Removes all "mouseup" event listeners added to "colorbar.voi" namespace
37
+ * targetEventListeners.removeEventListener('voi.mouseup')
38
+ *
39
+ * // Removes all event listeners added to the element using this targetEventListeners
40
+ * // instance. A TargetEventListeners instance does not removes the event listeners
41
+ * // added by another one.
42
+ * targetEventListeners.reset();
43
+ * ```
44
+ *
45
+ * @example
46
+ * Adding and removing event listeners for capture and bubble phases. Each
47
+ * listener must be removed indenpendently
48
+ * ```javascript
49
+ * const clickCaptureCallback = () => { };
50
+ * const clickBubbleCallback = () => { };
51
+ *
52
+ * targetEventListeners.addEventListener('click', clickCaptureCallback, { capture: true });
53
+ * targetEventListeners.addEventListener('click', clickBubbleCallback);
54
+ *
55
+ * // Removes the event listener added to the capture phase
56
+ * targetEventListeners.removeEventListener('click', clickCaptureCallback, { capture: true });
57
+ *
58
+ * // Removes the event listener added to the bubble phase
59
+ * targetEventListeners.removeEventListener('click', clickBubbleCallback);
60
+ *
61
+ * // Removes all event listeners added to the HTML element
62
+ * targetEventListeners.reset();
63
+ * ```
64
+
65
+ */
66
+ class TargetEventListeners {
67
+ private _target: EventTarget;
68
+ private _eventListeners = new Map<string, ListenersMap>();
69
+ private _children = new Map<string, TargetEventListeners>();
70
+
71
+ constructor(target: EventTarget) {
72
+ this._target = target;
73
+ }
74
+
75
+ public get isEmpty() {
76
+ return this._eventListeners.size === 0 && this._children.size === 0;
77
+ }
78
+
79
+ public addEventListener(
80
+ type: string,
81
+ callback: EventListener,
82
+ options?: AddEventListenerOptions
83
+ ) {
84
+ const dotIndex = type.indexOf('.');
85
+ const isNamespace = dotIndex !== -1;
86
+
87
+ if (isNamespace) {
88
+ const namespaceToken = type.substring(0, dotIndex);
89
+ let childElementEventListener = this._children.get(namespaceToken);
90
+
91
+ if (!childElementEventListener) {
92
+ childElementEventListener = new TargetEventListeners(this._target);
93
+ this._children.set(namespaceToken, childElementEventListener);
94
+ }
95
+
96
+ type = type.substring(dotIndex + 1);
97
+ childElementEventListener.addEventListener(type, callback, options);
98
+ } else {
99
+ this._addEventListener(type, callback, options);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Remove an event listener with support for namespaces and optional callback
105
+ * which makes it remove all listeners of a given type
106
+ * @param type - Event type
107
+ * @param callback - Event listener
108
+ * @param options - Event options
109
+ */
110
+ public removeEventListener(
111
+ type: string,
112
+ callback?: EventListener,
113
+ options?: EventListenerOptions
114
+ ): void {
115
+ const dotIndex = type.indexOf('.');
116
+ const isNamespace = dotIndex !== -1;
117
+
118
+ if (isNamespace) {
119
+ const namespaceToken = type.substring(0, dotIndex);
120
+ const childElementEventListener = this._children.get(namespaceToken);
121
+
122
+ if (!childElementEventListener) {
123
+ return;
124
+ }
125
+
126
+ type = type.substring(dotIndex + 1);
127
+ childElementEventListener.removeEventListener(type, callback, options);
128
+
129
+ // remove empty child objects
130
+ if (childElementEventListener.isEmpty) {
131
+ this._children.delete(namespaceToken);
132
+ }
133
+ } else {
134
+ this._removeEventListener(type, callback, options);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Loop through all types, listeners and phases and removing all of them
140
+ */
141
+ public reset() {
142
+ // Destroy all children (DFS - depth first search)
143
+ Array.from(this._children.entries()).forEach(([namespace, child]) => {
144
+ child.reset();
145
+
146
+ if (child.isEmpty) {
147
+ this._children.delete(namespace);
148
+ } else {
149
+ // This scenario must never happen (safety only)
150
+ throw new Error('Child is not empty and cannot be removed');
151
+ }
152
+ });
153
+
154
+ this._unregisterAllEvents();
155
+ }
156
+
157
+ private _addEventListener(
158
+ type: string,
159
+ callback: EventListener,
160
+ options?: AddEventListenerOptions
161
+ ) {
162
+ let listenersMap = this._eventListeners.get(type);
163
+
164
+ if (!listenersMap) {
165
+ listenersMap = new Map<EventListener, EventListenerPhases>();
166
+ this._eventListeners.set(type, listenersMap);
167
+ }
168
+
169
+ const useCapture = options?.capture ?? false;
170
+ const listenerPhase = useCapture
171
+ ? EventListenerPhases.Capture
172
+ : EventListenerPhases.Bubble;
173
+ const registeredPhases =
174
+ listenersMap.get(callback) ?? EventListenerPhases.None;
175
+
176
+ // Bitwise operator to see if the current phase is already registered
177
+ // because the same listener may be register twice (capturing and bubbling
178
+ // phases)
179
+ if (registeredPhases & listenerPhase) {
180
+ console.warn('A listener is already registered for this phase');
181
+ return;
182
+ }
183
+
184
+ // Add a new event listener or updates the existing one for the phase requested
185
+ listenersMap.set(callback, registeredPhases | listenerPhase);
186
+
187
+ // Add the event listener to the target
188
+ this._target.addEventListener(type, callback, options);
189
+ }
190
+
191
+ private _removeEventListener(
192
+ type: string,
193
+ callback?: EventListener,
194
+ options?: EventListenerOptions
195
+ ): void {
196
+ const useCapture = options?.capture ?? false;
197
+ const listenerPhase = useCapture
198
+ ? EventListenerPhases.Capture
199
+ : EventListenerPhases.Bubble;
200
+
201
+ const listenersMap = this._eventListeners.get(type);
202
+
203
+ if (!listenersMap) {
204
+ return;
205
+ }
206
+
207
+ // It can remove a single or all callbacks for a given namespace
208
+ const callbacks = callback ? [callback] : Array.from(listenersMap.keys());
209
+
210
+ callbacks.forEach((callbackItem) => {
211
+ const registeredPhases =
212
+ listenersMap.get(callbackItem) ?? EventListenerPhases.None;
213
+
214
+ // Bitwise operation to see if the phase is registered
215
+ const phaseRegistered = !!(registeredPhases & listenerPhase);
216
+
217
+ if (!phaseRegistered) {
218
+ return;
219
+ }
220
+
221
+ // Remove the event listener from the target
222
+ this._target.removeEventListener(type, callbackItem, options);
223
+
224
+ // Since it is enabled we can XOR it to zero the bit in that position
225
+ // 00000011 (capture & buble) ^ 00000010 (buble) = 00000001 (capture)
226
+ const newListenerPhase = registeredPhases ^ listenerPhase;
227
+
228
+ // Deletes the listener if it is no more used in capturing or bubbling
229
+ // phases or updates it otherwise
230
+ if (newListenerPhase === EventListenerPhases.None) {
231
+ listenersMap.delete(callbackItem);
232
+ } else {
233
+ listenersMap.set(callbackItem, newListenerPhase);
234
+ }
235
+ });
236
+
237
+ // Deletes the event from the main map if there are no listeners anymore
238
+ if (!listenersMap.size) {
239
+ this._eventListeners.delete(type);
240
+ }
241
+ }
242
+
243
+ private _unregisterAllListeners(type: string, listenersMap: ListenersMap) {
244
+ // Creates a copy with Array.from() because the map mutates every
245
+ // time an event listener is removed
246
+ Array.from(listenersMap.entries()).forEach(([listener, eventPhases]) => {
247
+ const startPhase = EventListenerPhases.Capture;
248
+
249
+ // currentPhase start at 1 and shifts 1 bit to the left because
250
+ // EventListenerPhases is a power of 2
251
+ for (let currentPhase = startPhase; eventPhases; currentPhase <<= 1) {
252
+ // Check if the current phase is registered
253
+ if (!(eventPhases & currentPhase)) {
254
+ continue;
255
+ }
256
+
257
+ const useCapture =
258
+ currentPhase === EventListenerPhases.Capture ? true : false;
259
+
260
+ // Remove the event listener for this given phase
261
+ this.removeEventListener(type, listener, { capture: useCapture });
262
+
263
+ // Switch the bit from the "currentPhase" from 1 to 0
264
+ eventPhases ^= currentPhase;
265
+ }
266
+ });
267
+ }
268
+
269
+ private _unregisterAllEvents() {
270
+ // Creates a copy with Array.from() because the map mutates every
271
+ // time an event listener is removed
272
+ Array.from(this._eventListeners.entries()).forEach(([type, listenersMap]) =>
273
+ this._unregisterAllListeners(type, listenersMap)
274
+ );
275
+ }
276
+ }
277
+
278
+ export { TargetEventListeners as default, TargetEventListeners };
@@ -0,0 +1,2 @@
1
+ export { TargetEventListeners } from './TargetEventListeners';
2
+ export { MultiTargetEventListenerManager } from './MultiTargetEventListenerManager';
@@ -0,0 +1,23 @@
1
+ import { IViewport } from '../types';
2
+ import { StackViewport, VolumeViewport } from '../RenderingEngine';
3
+ import cache from '../cache';
4
+
5
+ function getViewportModality(viewport: IViewport, volumeId?: string): string {
6
+ if (viewport instanceof StackViewport) {
7
+ return viewport.modality;
8
+ }
9
+
10
+ if (viewport instanceof VolumeViewport) {
11
+ volumeId = volumeId ?? viewport.getDefaultActor()?.uid;
12
+
13
+ if (!volumeId) {
14
+ return;
15
+ }
16
+
17
+ return cache.getVolume(volumeId)?.metadata.Modality;
18
+ }
19
+
20
+ throw new Error('Invalid viewport type');
21
+ }
22
+
23
+ export { getViewportModality as default, getViewportModality };
@@ -1,3 +1,4 @@
1
+ import * as eventListener from './eventListener';
1
2
  import csUtils from './invertRgbTransferFunction';
2
3
  import createSigmoidRGBTransferFunction from './createSigmoidRGBTransferFunction';
3
4
  import getVoiFromSigmoidRGBTransferFunction from './getVoiFromSigmoidRGBTransferFunction';
@@ -9,12 +10,14 @@ import getMinMax from './getMinMax';
9
10
  import getRuntimeId from './getRuntimeId';
10
11
  import imageIdToURI from './imageIdToURI';
11
12
  import calibratedPixelSpacingMetadataProvider from './calibratedPixelSpacingMetadataProvider';
13
+ import clamp from './clamp';
12
14
  import isEqual from './isEqual';
13
15
  import isOpposite from './isOpposite';
14
16
  import createUint8SharedArray from './createUint8SharedArray';
15
17
  import createFloat32SharedArray from './createFloat32SharedArray';
16
18
  import createUint16SharedArray from './createUInt16SharedArray';
17
19
  import createInt16SharedArray from './createInt16SharedArray';
20
+ import getViewportModality from './getViewportModality';
18
21
  import getClosestImageId from './getClosestImageId';
19
22
  import getSpacingInNormalDirection from './getSpacingInNormalDirection';
20
23
  import getTargetVolumeAndSpacingInNormalDir from './getTargetVolumeAndSpacingInNormalDir';
@@ -54,6 +57,7 @@ import * as colormap from './colormap';
54
57
  import * as transferFunctionUtils from './transferFunctionUtils';
55
58
 
56
59
  export {
60
+ eventListener,
57
61
  csUtils as invertRgbTransferFunction,
58
62
  createSigmoidRGBTransferFunction,
59
63
  getVoiFromSigmoidRGBTransferFunction,
@@ -62,6 +66,7 @@ export {
62
66
  triggerEvent,
63
67
  imageIdToURI,
64
68
  calibratedPixelSpacingMetadataProvider,
69
+ clamp,
65
70
  uuidv4,
66
71
  planar,
67
72
  getMinMax,
@@ -72,6 +77,7 @@ export {
72
77
  createUint8SharedArray,
73
78
  createUint16SharedArray,
74
79
  createInt16SharedArray,
80
+ getViewportModality,
75
81
  windowLevel,
76
82
  getClosestImageId,
77
83
  getSpacingInNormalDirection,