@cornerstonejs/core 1.25.0 → 1.26.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 (98) hide show
  1. package/dist/cjs/RenderingEngine/BaseVolumeViewport.d.ts +1 -1
  2. package/dist/cjs/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  3. package/dist/cjs/RenderingEngine/RenderingEngine.d.ts +5 -2
  4. package/dist/cjs/RenderingEngine/RenderingEngine.js +13 -5
  5. package/dist/cjs/RenderingEngine/RenderingEngine.js.map +1 -1
  6. package/dist/cjs/RenderingEngine/VideoViewport.d.ts +51 -0
  7. package/dist/cjs/RenderingEngine/VideoViewport.js +355 -0
  8. package/dist/cjs/RenderingEngine/VideoViewport.js.map +1 -0
  9. package/dist/cjs/RenderingEngine/VolumeViewport.js +6 -0
  10. package/dist/cjs/RenderingEngine/VolumeViewport.js.map +1 -1
  11. package/dist/cjs/RenderingEngine/helpers/viewportTypeToViewportClass.d.ts +2 -0
  12. package/dist/cjs/RenderingEngine/helpers/viewportTypeToViewportClass.js +2 -0
  13. package/dist/cjs/RenderingEngine/helpers/viewportTypeToViewportClass.js.map +1 -1
  14. package/dist/cjs/enums/VideoViewport.d.ts +5 -0
  15. package/dist/cjs/enums/VideoViewport.js +10 -0
  16. package/dist/cjs/enums/VideoViewport.js.map +1 -0
  17. package/dist/cjs/enums/ViewportType.d.ts +2 -1
  18. package/dist/cjs/enums/ViewportType.js +1 -0
  19. package/dist/cjs/enums/ViewportType.js.map +1 -1
  20. package/dist/cjs/enums/index.d.ts +2 -1
  21. package/dist/cjs/enums/index.js +26 -1
  22. package/dist/cjs/enums/index.js.map +1 -1
  23. package/dist/cjs/getEnabledElement.js.map +1 -1
  24. package/dist/cjs/index.d.ts +2 -1
  25. package/dist/cjs/index.js +3 -1
  26. package/dist/cjs/index.js.map +1 -1
  27. package/dist/cjs/types/IRenderingEngine.d.ts +5 -2
  28. package/dist/cjs/types/IVideoViewport.d.ts +12 -0
  29. package/dist/cjs/types/IVideoViewport.js +3 -0
  30. package/dist/cjs/types/IVideoViewport.js.map +1 -0
  31. package/dist/cjs/types/VideoViewportProperties.d.ts +10 -0
  32. package/dist/cjs/types/VideoViewportProperties.js +3 -0
  33. package/dist/cjs/types/VideoViewportProperties.js.map +1 -0
  34. package/dist/cjs/types/VideoViewportTypes.d.ts +18 -0
  35. package/dist/cjs/types/VideoViewportTypes.js +3 -0
  36. package/dist/cjs/types/VideoViewportTypes.js.map +1 -0
  37. package/dist/cjs/types/index.d.ts +4 -1
  38. package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js +10 -2
  39. package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
  40. package/dist/esm/RenderingEngine/BaseVolumeViewport.d.ts +1 -1
  41. package/dist/esm/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  42. package/dist/esm/RenderingEngine/RenderingEngine.d.ts +5 -2
  43. package/dist/esm/RenderingEngine/RenderingEngine.js +13 -5
  44. package/dist/esm/RenderingEngine/RenderingEngine.js.map +1 -1
  45. package/dist/esm/RenderingEngine/VideoViewport.d.ts +51 -0
  46. package/dist/esm/RenderingEngine/VideoViewport.js +330 -0
  47. package/dist/esm/RenderingEngine/VideoViewport.js.map +1 -0
  48. package/dist/esm/RenderingEngine/VolumeViewport.js +6 -0
  49. package/dist/esm/RenderingEngine/VolumeViewport.js.map +1 -1
  50. package/dist/esm/RenderingEngine/helpers/viewportTypeToViewportClass.d.ts +2 -0
  51. package/dist/esm/RenderingEngine/helpers/viewportTypeToViewportClass.js +2 -0
  52. package/dist/esm/RenderingEngine/helpers/viewportTypeToViewportClass.js.map +1 -1
  53. package/dist/esm/enums/VideoViewport.d.ts +5 -0
  54. package/dist/esm/enums/VideoViewport.js +7 -0
  55. package/dist/esm/enums/VideoViewport.js.map +1 -0
  56. package/dist/esm/enums/ViewportType.d.ts +2 -1
  57. package/dist/esm/enums/ViewportType.js +1 -0
  58. package/dist/esm/enums/ViewportType.js.map +1 -1
  59. package/dist/esm/enums/index.d.ts +2 -1
  60. package/dist/esm/enums/index.js +2 -1
  61. package/dist/esm/enums/index.js.map +1 -1
  62. package/dist/esm/getEnabledElement.js.map +1 -1
  63. package/dist/esm/index.d.ts +2 -1
  64. package/dist/esm/index.js +2 -1
  65. package/dist/esm/index.js.map +1 -1
  66. package/dist/esm/types/IRenderingEngine.d.ts +5 -2
  67. package/dist/esm/types/IVideoViewport.d.ts +12 -0
  68. package/dist/esm/types/IVideoViewport.js +2 -0
  69. package/dist/esm/types/IVideoViewport.js.map +1 -0
  70. package/dist/esm/types/VideoViewportProperties.d.ts +10 -0
  71. package/dist/esm/types/VideoViewportProperties.js +2 -0
  72. package/dist/esm/types/VideoViewportProperties.js.map +1 -0
  73. package/dist/esm/types/VideoViewportTypes.d.ts +18 -0
  74. package/dist/esm/types/VideoViewportTypes.js +2 -0
  75. package/dist/esm/types/VideoViewportTypes.js.map +1 -0
  76. package/dist/esm/types/index.d.ts +4 -1
  77. package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js +10 -2
  78. package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
  79. package/dist/umd/index.js +1 -1
  80. package/dist/umd/index.js.map +1 -1
  81. package/package.json +2 -2
  82. package/src/RenderingEngine/BaseVolumeViewport.ts +2 -5
  83. package/src/RenderingEngine/RenderingEngine.ts +38 -21
  84. package/src/RenderingEngine/VideoViewport.ts +523 -0
  85. package/src/RenderingEngine/VolumeViewport.ts +8 -0
  86. package/src/RenderingEngine/helpers/viewportTypeToViewportClass.ts +2 -0
  87. package/src/enums/VideoViewport.ts +9 -0
  88. package/src/enums/ViewportType.ts +1 -0
  89. package/src/enums/index.ts +2 -0
  90. package/src/getEnabledElement.ts +4 -2
  91. package/src/index.ts +2 -0
  92. package/src/types/IEnabledElement.ts +4 -1
  93. package/src/types/IRenderingEngine.ts +5 -2
  94. package/src/types/IVideoViewport.ts +35 -0
  95. package/src/types/VideoViewportProperties.ts +17 -0
  96. package/src/types/VideoViewportTypes.ts +20 -0
  97. package/src/types/index.ts +11 -0
  98. package/src/utilities/getTargetVolumeAndSpacingInNormalDir.ts +25 -5
@@ -0,0 +1,523 @@
1
+ import { Events as EVENTS, VideoViewport as VideoViewportEnum } from '../enums';
2
+ import {
3
+ IVideoViewport,
4
+ VideoViewportProperties,
5
+ Point3,
6
+ Point2,
7
+ ICamera,
8
+ InternalVideoCamera,
9
+ VideoViewportInput,
10
+ } from '../types';
11
+ import { Transform } from './helpers/cpuFallback/rendering/transform';
12
+ import { triggerEvent } from '../utilities';
13
+ import Viewport from './Viewport';
14
+ import { getOrCreateCanvas } from './helpers';
15
+
16
+ /**
17
+ * An object representing a single stack viewport, which is a camera
18
+ * looking into an internal scene, and an associated target output `canvas`.
19
+ */
20
+ class VideoViewport extends Viewport implements IVideoViewport {
21
+ public static readonly useCustomRenderingPipeline = true;
22
+
23
+ // Viewport Data
24
+ readonly uid;
25
+ readonly renderingEngineId: string;
26
+ readonly canvasContext: CanvasRenderingContext2D;
27
+ private videoElement?: HTMLVideoElement;
28
+ private videoWidth = 0;
29
+ private videoHeight = 0;
30
+
31
+ private loop = false;
32
+ private mute = true;
33
+ private isPlaying = false;
34
+ private scrollSpeed = 1;
35
+ private fps = 30; // TODO We need to find a good solution for this.
36
+ private videoCamera: InternalVideoCamera = {
37
+ panWorld: [0, 0],
38
+ parallelScale: 1,
39
+ };
40
+
41
+ constructor(props: VideoViewportInput) {
42
+ super({
43
+ ...props,
44
+ canvas: props.canvas || getOrCreateCanvas(props.element),
45
+ });
46
+ this.canvasContext = this.canvas.getContext('2d');
47
+ this.renderingEngineId = props.renderingEngineId;
48
+
49
+ this.element.setAttribute('data-viewport-uid', this.id);
50
+ this.element.setAttribute(
51
+ 'data-rendering-engine-uid',
52
+ this.renderingEngineId
53
+ );
54
+
55
+ this.videoElement = document.createElement('video');
56
+ this.videoElement.muted = this.mute;
57
+ this.videoElement.loop = this.loop;
58
+ this.videoElement.crossOrigin = 'anonymous';
59
+
60
+ this.addEventListeners();
61
+ this.resize();
62
+ }
63
+
64
+ private addEventListeners() {
65
+ this.canvas.addEventListener(
66
+ EVENTS.ELEMENT_DISABLED,
67
+ this.elementDisabledHandler
68
+ );
69
+ }
70
+
71
+ private removeEventListeners() {
72
+ this.canvas.removeEventListener(
73
+ EVENTS.ELEMENT_DISABLED,
74
+ this.elementDisabledHandler
75
+ );
76
+ }
77
+
78
+ private elementDisabledHandler() {
79
+ this.removeEventListeners();
80
+ this.videoElement.remove();
81
+ }
82
+
83
+ public async setVideoURL(videoURL: string) {
84
+ return new Promise((resolve) => {
85
+ this.videoElement.src = videoURL;
86
+ this.videoElement.preload = 'auto';
87
+
88
+ const loadedMetadataEventHandler = () => {
89
+ this.videoWidth = this.videoElement.videoWidth;
90
+ this.videoHeight = this.videoElement.videoHeight;
91
+ this.videoElement.removeEventListener(
92
+ 'loadedmetadata',
93
+ loadedMetadataEventHandler
94
+ );
95
+
96
+ this.refreshRenderValues();
97
+
98
+ resolve(true);
99
+ };
100
+
101
+ this.videoElement.addEventListener(
102
+ 'loadedmetadata',
103
+ loadedMetadataEventHandler
104
+ );
105
+ });
106
+ }
107
+
108
+ public togglePlayPause(): boolean {
109
+ if (this.isPlaying) {
110
+ this.pause();
111
+ return false;
112
+ } else {
113
+ this.play();
114
+ return true;
115
+ }
116
+ }
117
+
118
+ public play() {
119
+ if (!this.isPlaying) {
120
+ this.videoElement.play();
121
+ this.isPlaying = true;
122
+ this.renderWhilstPlaying();
123
+ }
124
+ }
125
+
126
+ public async pause() {
127
+ if (this.isPlaying) {
128
+ await this.videoElement.pause();
129
+ this.isPlaying = false;
130
+ }
131
+ }
132
+
133
+ public async scroll(delta = 1) {
134
+ await this.pause();
135
+
136
+ const videoElement = this.videoElement;
137
+ const renderFrame = this.renderFrame;
138
+
139
+ const currentTime = videoElement.currentTime;
140
+ const newTime = currentTime + (delta * this.scrollSpeed) / this.fps;
141
+
142
+ videoElement.currentTime = newTime;
143
+
144
+ // Need to wait for seek update
145
+ const seekEventListener = (evt) => {
146
+ renderFrame();
147
+
148
+ videoElement.removeEventListener('seeked', seekEventListener);
149
+ };
150
+
151
+ videoElement.addEventListener('seeked', seekEventListener);
152
+ }
153
+
154
+ public async start() {
155
+ const videoElement = this.videoElement;
156
+ const renderFrame = this.renderFrame;
157
+
158
+ videoElement.currentTime = 0;
159
+
160
+ if (videoElement.paused) {
161
+ // Need to wait for seek update
162
+ const seekEventListener = (evt) => {
163
+ console.log('seeked');
164
+
165
+ renderFrame();
166
+
167
+ videoElement.removeEventListener('seeked', seekEventListener);
168
+ };
169
+
170
+ videoElement.addEventListener('seeked', seekEventListener);
171
+ }
172
+ }
173
+
174
+ public async end() {
175
+ const videoElement = this.videoElement;
176
+ const renderFrame = this.renderFrame;
177
+
178
+ videoElement.currentTime = videoElement.duration;
179
+
180
+ if (videoElement.paused) {
181
+ // Need to wait for seek update
182
+ const seekEventListener = (evt) => {
183
+ renderFrame();
184
+
185
+ videoElement.removeEventListener('seeked', seekEventListener);
186
+ };
187
+
188
+ videoElement.addEventListener('seeked', seekEventListener);
189
+ }
190
+ }
191
+
192
+ public async setTime(timeInSeconds: number) {
193
+ const videoElement = this.videoElement;
194
+ const renderFrame = this.renderFrame;
195
+
196
+ videoElement.currentTime = timeInSeconds;
197
+
198
+ if (videoElement.paused) {
199
+ // Need to wait for seek update
200
+ const seekEventListener = (evt) => {
201
+ renderFrame();
202
+
203
+ videoElement.removeEventListener('seeked', seekEventListener);
204
+ };
205
+
206
+ videoElement.addEventListener('seeked', seekEventListener);
207
+ }
208
+ }
209
+
210
+ // Sets the frame number - note according to DICOM, this is 1 based
211
+ public async setFrame(frame: number) {
212
+ this.setTime((frame - 1) / this.fps);
213
+ }
214
+
215
+ public setProperties(videoInterface: VideoViewportProperties) {
216
+ if (videoInterface.loop !== undefined) {
217
+ this.videoElement.loop = videoInterface.loop;
218
+ }
219
+
220
+ if (videoInterface.muted !== undefined) {
221
+ this.videoElement.muted = videoInterface.muted;
222
+ }
223
+
224
+ if (videoInterface.playbackRate !== undefined) {
225
+ this.setPlaybackRate(videoInterface.playbackRate);
226
+ }
227
+ }
228
+
229
+ public setPlaybackRate(rate = 1) {
230
+ // Minimum playback speed in chrome is 0.0625 compared to normal
231
+ if (rate < 0.0625) {
232
+ this.pause();
233
+ return;
234
+ }
235
+ if (!this.videoElement) {
236
+ return;
237
+ }
238
+ this.videoElement.playbackRate = rate;
239
+ this.play();
240
+ }
241
+
242
+ public setScrollSpeed(
243
+ scrollSpeed = 1,
244
+ unit = VideoViewportEnum.SpeedUnit.FRAME
245
+ ) {
246
+ this.scrollSpeed =
247
+ unit === VideoViewportEnum.SpeedUnit.SECOND
248
+ ? scrollSpeed * this.fps
249
+ : scrollSpeed;
250
+ }
251
+
252
+ public getProperties = (): VideoViewportProperties => {
253
+ return {
254
+ loop: this.videoElement.loop,
255
+ muted: this.videoElement.muted,
256
+ };
257
+ };
258
+
259
+ public resetProperties() {
260
+ this.setProperties({
261
+ loop: false,
262
+ muted: true,
263
+ });
264
+ }
265
+
266
+ public getImageData() {
267
+ return null;
268
+ }
269
+
270
+ public setCamera(camera: ICamera): void {
271
+ const { parallelScale, focalPoint } = camera;
272
+
273
+ // NOTE: the parallel scale should be done first
274
+ // because it affects the focal point later
275
+ if (camera.parallelScale !== undefined) {
276
+ this.videoCamera.parallelScale = 1 / parallelScale;
277
+ }
278
+
279
+ if (focalPoint !== undefined) {
280
+ const focalPointCanvas = this.worldToCanvas(focalPoint);
281
+ const canvasCenter: Point2 = [
282
+ this.element.clientWidth / 2,
283
+ this.element.clientHeight / 2,
284
+ ];
285
+
286
+ const panWorldDelta: Point2 = [
287
+ (focalPointCanvas[0] - canvasCenter[0]) /
288
+ this.videoCamera.parallelScale,
289
+ (focalPointCanvas[1] - canvasCenter[1]) /
290
+ this.videoCamera.parallelScale,
291
+ ];
292
+
293
+ this.videoCamera.panWorld = [
294
+ this.videoCamera.panWorld[0] - panWorldDelta[0],
295
+ this.videoCamera.panWorld[1] - panWorldDelta[1],
296
+ ];
297
+ }
298
+
299
+ this.canvasContext.fillRect(0, 0, this.canvas.width, this.canvas.height);
300
+
301
+ if (this.isPlaying === false) {
302
+ this.renderFrame();
303
+ }
304
+ }
305
+
306
+ public getCamera(): ICamera {
307
+ const { parallelScale } = this.videoCamera;
308
+
309
+ const canvasCenter: Point2 = [
310
+ this.element.clientWidth / 2,
311
+ this.element.clientHeight / 2,
312
+ ];
313
+
314
+ // All other viewports have the focal point in canvas coordinates in the center
315
+ // of the canvas, so to make tools work the same, we need to do the same here
316
+ // and convert to the world coordinate system since focal point is in world coordinates.
317
+ const canvasCenterWorld = this.canvasToWorld(canvasCenter);
318
+
319
+ return {
320
+ parallelProjection: true,
321
+ focalPoint: canvasCenterWorld,
322
+ position: [0, 0, 0],
323
+ parallelScale: 1 / parallelScale, // Reverse zoom direction back
324
+ viewPlaneNormal: [0, 0, 1],
325
+ };
326
+ }
327
+
328
+ public resetCamera = (): boolean => {
329
+ this.refreshRenderValues();
330
+
331
+ this.canvasContext.fillRect(0, 0, this.canvas.width, this.canvas.height);
332
+
333
+ if (this.isPlaying === false) {
334
+ // If its not replaying, just re-render the frame on move.
335
+ this.renderFrame();
336
+ }
337
+ return true;
338
+ };
339
+
340
+ public getFrameOfReferenceUID = (): string => {
341
+ // The video itself is the frame of reference.
342
+ return this.videoElement.src;
343
+ };
344
+
345
+ public resize = (): void => {
346
+ const canvas = this.canvas;
347
+ const { clientWidth, clientHeight } = canvas;
348
+
349
+ // Set the canvas to be same resolution as the client.
350
+ if (canvas.width !== clientWidth || canvas.height !== clientHeight) {
351
+ canvas.width = clientWidth;
352
+ canvas.height = clientHeight;
353
+ }
354
+
355
+ this.refreshRenderValues();
356
+
357
+ if (this.isPlaying === false) {
358
+ // If its not playing, just re-render on resize.
359
+ this.renderFrame();
360
+ }
361
+ };
362
+
363
+ /**
364
+ * Converts a VideoViewport canvas coordinate to a video coordinate.
365
+ *
366
+ * @param canvasPos - to convert to world
367
+ * @returns World position
368
+ */
369
+ public canvasToWorld = (canvasPos: Point2): Point3 => {
370
+ const pan: Point2 = this.videoCamera.panWorld; // In world coordinates
371
+ const worldToCanvasRatio: number = this.getWorldToCanvasRatio();
372
+
373
+ const panOffsetCanvas: Point2 = [
374
+ pan[0] * worldToCanvasRatio,
375
+ pan[1] * worldToCanvasRatio,
376
+ ];
377
+
378
+ const subCanvasPos: Point2 = [
379
+ canvasPos[0] - panOffsetCanvas[0],
380
+ canvasPos[1] - panOffsetCanvas[1],
381
+ ];
382
+
383
+ const worldPos: Point3 = [
384
+ subCanvasPos[0] / worldToCanvasRatio,
385
+ subCanvasPos[1] / worldToCanvasRatio,
386
+ 0,
387
+ ];
388
+
389
+ return worldPos;
390
+ };
391
+
392
+ /**
393
+ * Converts and [x,y] video coordinate to a Cornerstone3D VideoViewport.
394
+ *
395
+ * @param worldPos - world coord to convert to canvas
396
+ * @returns Canvas position
397
+ */
398
+ public worldToCanvas = (worldPos: Point3): Point2 => {
399
+ const pan: Point2 = this.videoCamera.panWorld;
400
+ const worldToCanvasRatio: number = this.getWorldToCanvasRatio();
401
+
402
+ const subCanvasPos: Point2 = [
403
+ (worldPos[0] + pan[0]) * worldToCanvasRatio,
404
+ (worldPos[1] + pan[1]) * worldToCanvasRatio,
405
+ ];
406
+
407
+ const canvasPos: Point2 = [subCanvasPos[0], subCanvasPos[1]];
408
+
409
+ return canvasPos;
410
+ };
411
+
412
+ private refreshRenderValues() {
413
+ // this means that each unit (pixel) in the world (video) would be
414
+ // represented by n pixels in the canvas.
415
+ let worldToCanvasRatio = this.canvas.width / this.videoWidth;
416
+
417
+ if (this.videoHeight * worldToCanvasRatio > this.canvas.height) {
418
+ // If by fitting the width, we exceed the height of the viewport, then we need to decrease the
419
+ // size of the viewport further by considering its verticality.
420
+ const secondWorldToCanvasRatio =
421
+ this.canvas.height / (this.videoHeight * worldToCanvasRatio);
422
+
423
+ worldToCanvasRatio *= secondWorldToCanvasRatio;
424
+ }
425
+
426
+ // Set the width as big as possible, this is the portion of the canvas
427
+ // that the video will occupy.
428
+ const drawWidth = Math.floor(this.videoWidth * worldToCanvasRatio);
429
+ const drawHeight = Math.floor(this.videoHeight * worldToCanvasRatio);
430
+
431
+ // calculate x and y offset in order to center the image
432
+ const xOffsetCanvas = this.canvas.width / 2 - drawWidth / 2;
433
+ const yOffsetCanvas = this.canvas.height / 2 - drawHeight / 2;
434
+
435
+ const xOffsetWorld = xOffsetCanvas / worldToCanvasRatio;
436
+ const yOffsetWorld = yOffsetCanvas / worldToCanvasRatio;
437
+
438
+ this.videoCamera.panWorld = [xOffsetWorld, yOffsetWorld];
439
+ this.videoCamera.parallelScale = worldToCanvasRatio;
440
+ }
441
+
442
+ private getWorldToCanvasRatio() {
443
+ return this.videoCamera.parallelScale;
444
+ }
445
+
446
+ private getCanvasToWorldRatio() {
447
+ return 1.0 / this.videoCamera.parallelScale;
448
+ }
449
+
450
+ public customRenderViewportToCanvas = () => {
451
+ this.renderFrame();
452
+ };
453
+
454
+ private renderFrame = () => {
455
+ const panWorld: Point2 = this.videoCamera.panWorld;
456
+ const worldToCanvasRatio: number = this.getWorldToCanvasRatio();
457
+ const canvasToWorldRatio: number = this.getCanvasToWorldRatio();
458
+
459
+ const halfCanvas = [this.canvas.width / 2, this.canvas.height / 2];
460
+ const halfCanvasWorldCoordinates = [
461
+ halfCanvas[0] * canvasToWorldRatio,
462
+ halfCanvas[1] * canvasToWorldRatio,
463
+ ];
464
+
465
+ const transform = new Transform();
466
+
467
+ // Translate to the center of the canvas (move origin of the transform
468
+ // to the center of the canvas)
469
+ transform.translate(halfCanvas[0], halfCanvas[1]);
470
+
471
+ // Scale
472
+ transform.scale(worldToCanvasRatio, worldToCanvasRatio);
473
+
474
+ // Apply the translation
475
+ transform.translate(panWorld[0], panWorld[1]);
476
+
477
+ // Translate back
478
+ transform.translate(
479
+ -halfCanvasWorldCoordinates[0],
480
+ -halfCanvasWorldCoordinates[1]
481
+ );
482
+ const transformationMatrix: number[] = transform.getMatrix();
483
+
484
+ this.canvasContext.transform(
485
+ transformationMatrix[0],
486
+ transformationMatrix[1],
487
+ transformationMatrix[2],
488
+ transformationMatrix[3],
489
+ transformationMatrix[4],
490
+ transformationMatrix[5]
491
+ );
492
+
493
+ this.canvasContext.drawImage(
494
+ this.videoElement,
495
+ 0,
496
+ 0,
497
+ this.videoWidth,
498
+ this.videoHeight
499
+ );
500
+
501
+ this.canvasContext.resetTransform();
502
+
503
+ triggerEvent(this.element, EVENTS.IMAGE_RENDERED, {
504
+ element: this.element,
505
+ viewportId: this.id,
506
+ viewport: this,
507
+ renderingEngineId: this.renderingEngineId,
508
+ time: this.videoElement.currentTime,
509
+ duration: this.videoElement.duration,
510
+ });
511
+ };
512
+
513
+ private renderWhilstPlaying = () => {
514
+ this.renderFrame();
515
+
516
+ //wait approximately 16ms and run again
517
+ if (this.isPlaying) {
518
+ requestAnimationFrame(this.renderWhilstPlaying);
519
+ }
520
+ };
521
+ }
522
+
523
+ export default VideoViewport;
@@ -302,6 +302,7 @@ class VolumeViewport extends BaseVolumeViewport {
302
302
  const currentCamera = this.getCamera();
303
303
  this.updateClippingPlanesForActors(currentCamera);
304
304
  this.triggerCameraModifiedEventIfNecessary(currentCamera, currentCamera);
305
+ this.viewportProperties.slabThickness = slabThickness;
305
306
  }
306
307
 
307
308
  /**
@@ -387,6 +388,13 @@ class VolumeViewport extends BaseVolumeViewport {
387
388
  throw new Error(`No actor found for the given volumeId: ${volumeId}`);
388
389
  }
389
390
 
391
+ // if a custom slabThickness was set, we need to reset it
392
+ if (volumeActor.slabThickness) {
393
+ volumeActor.slabThickness = RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS;
394
+ this.viewportProperties.slabThickness = undefined;
395
+ this.updateClippingPlanesForActors(this.getCamera());
396
+ }
397
+
390
398
  const imageVolume = cache.getVolume(volumeActor.uid);
391
399
  if (!imageVolume) {
392
400
  throw new Error(
@@ -3,12 +3,14 @@ import StackViewport from '../StackViewport';
3
3
  import VolumeViewport from '../VolumeViewport';
4
4
  import ViewportType from '../../enums/ViewportType';
5
5
  import VolumeViewport3D from '../VolumeViewport3D';
6
+ import VideoViewport from '../VideoViewport';
6
7
 
7
8
  const viewportTypeToViewportClass = {
8
9
  [ViewportType.ORTHOGRAPHIC]: VolumeViewport,
9
10
  [ViewportType.PERSPECTIVE]: VolumeViewport,
10
11
  [ViewportType.STACK]: StackViewport,
11
12
  [ViewportType.VOLUME_3D]: VolumeViewport3D,
13
+ [ViewportType.VIDEO]: VideoViewport,
12
14
  };
13
15
 
14
16
  export default viewportTypeToViewportClass;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * video viewport speed units
3
+ */
4
+ enum SpeedUnit {
5
+ FRAME = 'f',
6
+ SECOND = 's',
7
+ }
8
+
9
+ export { SpeedUnit };
@@ -16,6 +16,7 @@ enum ViewportType {
16
16
  /** Perspective Viewport: Not Implemented yet */
17
17
  PERSPECTIVE = 'perspective',
18
18
  VOLUME_3D = 'volume3d',
19
+ VIDEO = 'video',
19
20
  }
20
21
 
21
22
  export default ViewportType;
@@ -11,6 +11,7 @@ import VOILUTFunctionType from './VOILUTFunctionType';
11
11
  import DynamicOperatorType from './DynamicOperatorType';
12
12
  import CalibrationTypes from './CalibrationTypes';
13
13
  import ViewportStatus from './ViewportStatus';
14
+ import * as VideoViewport from './VideoViewport';
14
15
 
15
16
  export {
16
17
  Events,
@@ -26,4 +27,5 @@ export {
26
27
  VOILUTFunctionType,
27
28
  DynamicOperatorType,
28
29
  ViewportStatus,
30
+ VideoViewport,
29
31
  };
@@ -1,7 +1,7 @@
1
1
  import getRenderingEngine, {
2
2
  getRenderingEngines,
3
3
  } from './RenderingEngine/getRenderingEngine';
4
- import { IEnabledElement } from './types';
4
+ import { IEnabledElement, IStackViewport, IVolumeViewport } from './types';
5
5
 
6
6
  /**
7
7
  * A convenience method to find an EnabledElement given a reference to its
@@ -67,7 +67,9 @@ export function getEnabledElementByIds(
67
67
  return;
68
68
  }
69
69
 
70
- const viewport = renderingEngine.getViewport(viewportId);
70
+ const viewport = renderingEngine.getViewport(viewportId) as
71
+ | IStackViewport
72
+ | IVolumeViewport;
71
73
 
72
74
  if (!viewport) {
73
75
  return;
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import VolumeViewport from './RenderingEngine/VolumeViewport';
12
12
  import VolumeViewport3D from './RenderingEngine/VolumeViewport3D';
13
13
  import BaseVolumeViewport from './RenderingEngine/BaseVolumeViewport';
14
14
  import StackViewport from './RenderingEngine/StackViewport';
15
+ import VideoViewport from './RenderingEngine/VideoViewport';
15
16
  import Viewport from './RenderingEngine/Viewport';
16
17
  import eventTarget from './eventTarget';
17
18
  import {
@@ -80,6 +81,7 @@ export {
80
81
  VolumeViewport3D,
81
82
  Viewport,
82
83
  StackViewport,
84
+ VideoViewport,
83
85
  RenderingEngine,
84
86
  ImageVolume,
85
87
  // Helpers
@@ -6,7 +6,10 @@ import type IVolumeViewport from './IVolumeViewport';
6
6
  * Cornerstone Enabled Element interface
7
7
  */
8
8
  interface IEnabledElement {
9
- /** Cornerstone Viewport instance - can be Stack or Volume Viewport as of now */
9
+ /** Cornerstone Viewport instance - can be Stack or Volume, or Video Viewport as of now.
10
+ * For the moment, need to cast to unknown first before casting to IVideoViewport
11
+ * (TODO) - this will be done as part of adding annotation tools for video
12
+ */
10
13
  viewport: IStackViewport | IVolumeViewport;
11
14
  /** Cornerstone Rendering Engine instance */
12
15
  renderingEngine: IRenderingEngine;
@@ -1,6 +1,8 @@
1
1
  import IStackViewport from './IStackViewport';
2
2
  import { PublicViewportInput } from './IViewport';
3
3
  import IVolumeViewport from './IVolumeViewport';
4
+ import { IViewport } from './IViewport';
5
+ import IVideoViewport from './IVideoViewport';
4
6
 
5
7
  export default interface IRenderingEngine {
6
8
  id: string;
@@ -9,8 +11,8 @@ export default interface IRenderingEngine {
9
11
  offScreenCanvasContainer: any;
10
12
  setViewports(viewports: Array<PublicViewportInput>): void;
11
13
  resize(immediate?: boolean, keepCamera?: boolean): void;
12
- getViewport(id: string): IStackViewport | IVolumeViewport;
13
- getViewports(): Array<IStackViewport | IVolumeViewport>;
14
+ getViewport(id: string): IViewport;
15
+ getViewports(): Array<IViewport>;
14
16
  render(): void;
15
17
  renderViewports(viewportIds: Array<string>): void;
16
18
  renderViewport(viewportId: string): void;
@@ -23,6 +25,7 @@ export default interface IRenderingEngine {
23
25
  disableElement(viewportId: string): void;
24
26
  getStackViewports(): Array<IStackViewport>;
25
27
  getVolumeViewports(): Array<IVolumeViewport>;
28
+ getVideoViewports(): Array<IVideoViewport>;
26
29
  destroy(): void;
27
30
  _debugRender(): void;
28
31
  }
@@ -0,0 +1,35 @@
1
+ import { IViewport } from './IViewport';
2
+ import VideoViewportProperties from './VideoViewportProperties';
3
+
4
+ /**
5
+ * Interface for Stack Viewport
6
+ */
7
+ export default interface IVideoViewport extends IViewport {
8
+ /**
9
+ * Resizes the viewport - only used in CPU fallback for StackViewport. The
10
+ * GPU resizing happens inside the RenderingEngine.
11
+ */
12
+ resize: () => void;
13
+ /**
14
+ * Sets the properties for the viewport on the default actor.
15
+ */
16
+ setProperties(props: VideoViewportProperties, suppressEvents?: boolean): void;
17
+ /**
18
+ * Retrieve the viewport properties
19
+ */
20
+ getProperties: () => VideoViewportProperties;
21
+
22
+ setVideoURL: (url: string) => void;
23
+
24
+ play: () => void;
25
+
26
+ pause: () => void;
27
+ /**
28
+ * Reset the viewport properties to the default values
29
+ */
30
+ resetProperties(): void;
31
+ /**
32
+ * Centers Pan and resets the zoom for stack viewport.
33
+ */
34
+ resetCamera(resetPan?: boolean, resetZoom?: boolean): boolean;
35
+ }