@cornerstonejs/core 3.29.1 → 3.29.3

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.d.ts +53 -0
  2. package/dist/esm/RenderingEngine/BaseRenderingEngine.js +341 -0
  3. package/dist/esm/RenderingEngine/BaseVolumeViewport.d.ts +18 -0
  4. package/dist/esm/RenderingEngine/BaseVolumeViewport.js +77 -0
  5. package/dist/esm/RenderingEngine/RenderingEngine.d.ts +4 -38
  6. package/dist/esm/RenderingEngine/RenderingEngine.js +37 -614
  7. package/dist/esm/RenderingEngine/SequentialRenderingEngine.d.ts +18 -0
  8. package/dist/esm/RenderingEngine/SequentialRenderingEngine.js +235 -0
  9. package/dist/esm/RenderingEngine/StackViewport.d.ts +2 -0
  10. package/dist/esm/RenderingEngine/StackViewport.js +71 -3
  11. package/dist/esm/RenderingEngine/StandardRenderingEngine.d.ts +23 -0
  12. package/dist/esm/RenderingEngine/StandardRenderingEngine.js +285 -0
  13. package/dist/esm/RenderingEngine/helpers/isSequentialRenderingEngine.d.ts +1 -0
  14. package/dist/esm/RenderingEngine/helpers/isSequentialRenderingEngine.js +6 -0
  15. package/dist/esm/RenderingEngine/index.d.ts +4 -1
  16. package/dist/esm/RenderingEngine/index.js +4 -1
  17. package/dist/esm/enums/RenderingEngineModeEnum.d.ts +5 -0
  18. package/dist/esm/enums/RenderingEngineModeEnum.js +6 -0
  19. package/dist/esm/enums/index.d.ts +2 -1
  20. package/dist/esm/enums/index.js +2 -1
  21. package/dist/esm/index.d.ts +2 -2
  22. package/dist/esm/index.js +2 -2
  23. package/dist/esm/init.js +2 -0
  24. package/dist/esm/types/Cornerstone3DConfig.d.ts +9 -7
  25. package/dist/esm/types/IRenderingEngine.d.ts +1 -1
  26. package/dist/esm/types/RenderingEngineMode.d.ts +2 -0
  27. package/dist/esm/types/RenderingEngineMode.js +0 -0
  28. package/dist/esm/types/index.d.ts +2 -1
  29. package/dist/esm/version.d.ts +1 -1
  30. package/dist/esm/version.js +1 -1
  31. package/package.json +2 -2
@@ -0,0 +1,18 @@
1
+ import BaseRenderingEngine from './BaseRenderingEngine';
2
+ import type IStackViewport from '../types/IStackViewport';
3
+ import type IVolumeViewport from '../types/IVolumeViewport';
4
+ import type * as EventTypes from '../types/EventTypes';
5
+ import type { InternalViewportInput, NormalizedViewportInput, IViewport } from '../types/IViewport';
6
+ declare class SequentialRenderingEngine extends BaseRenderingEngine {
7
+ protected enableVTKjsDrivenViewport(viewportInputEntry: NormalizedViewportInput): void;
8
+ protected addVtkjsDrivenViewport(viewportInputEntry: InternalViewportInput): void;
9
+ protected setVtkjsDrivenViewports(viewportInputEntries: NormalizedViewportInput[]): void;
10
+ protected _resizeVTKViewports(vtkDrivenViewports: (IStackViewport | IVolumeViewport)[], keepCamera?: boolean, immediate?: boolean): void;
11
+ protected _renderFlaggedViewports: () => void;
12
+ protected renderViewportUsingCustomOrVtkPipeline(viewport: IViewport): EventTypes.ImageRenderedEventDetail;
13
+ protected _renderViewportFromVtkCanvasToOnscreenCanvas(viewport: IViewport, offScreenCanvas: HTMLCanvasElement): EventTypes.ImageRenderedEventDetail;
14
+ private _resizeOffScreenCanvasForSingleViewport;
15
+ private _resize;
16
+ private getWidgetRenderers;
17
+ }
18
+ export default SequentialRenderingEngine;
@@ -0,0 +1,235 @@
1
+ import BaseRenderingEngine, { VIEWPORT_MIN_SIZE } from './BaseRenderingEngine';
2
+ import Events from '../enums/Events';
3
+ import eventTarget from '../eventTarget';
4
+ import triggerEvent from '../utilities/triggerEvent';
5
+ import ViewportType from '../enums/ViewportType';
6
+ import VolumeViewport from './VolumeViewport';
7
+ import StackViewport from './StackViewport';
8
+ import viewportTypeUsesCustomRenderingPipeline from './helpers/viewportTypeUsesCustomRenderingPipeline';
9
+ import getOrCreateCanvas from './helpers/getOrCreateCanvas';
10
+ import VolumeViewport3D from './VolumeViewport3D';
11
+ class SequentialRenderingEngine extends BaseRenderingEngine {
12
+ constructor() {
13
+ super(...arguments);
14
+ this._renderFlaggedViewports = () => {
15
+ this._throwIfDestroyed();
16
+ const viewports = this._getViewportsAsArray();
17
+ const eventDetailArray = [];
18
+ for (let i = 0; i < viewports.length; i++) {
19
+ const viewport = viewports[i];
20
+ if (this._needsRender.has(viewport.id)) {
21
+ const eventDetail = this.renderViewportUsingCustomOrVtkPipeline(viewport);
22
+ eventDetailArray.push(eventDetail);
23
+ viewport.setRendered();
24
+ this._needsRender.delete(viewport.id);
25
+ if (this._needsRender.size === 0) {
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ this._animationFrameSet = false;
31
+ this._animationFrameHandle = null;
32
+ eventDetailArray.forEach((eventDetail) => {
33
+ if (!eventDetail?.element) {
34
+ return;
35
+ }
36
+ triggerEvent(eventDetail.element, Events.IMAGE_RENDERED, eventDetail);
37
+ });
38
+ };
39
+ }
40
+ enableVTKjsDrivenViewport(viewportInputEntry) {
41
+ const viewports = this._getViewportsAsArray();
42
+ const viewportsDrivenByVtkJs = viewports.filter((vp) => viewportTypeUsesCustomRenderingPipeline(vp.type) === false);
43
+ const canvasesDrivenByVtkJs = viewportsDrivenByVtkJs.map((vp) => vp.canvas);
44
+ const canvas = getOrCreateCanvas(viewportInputEntry.element);
45
+ canvasesDrivenByVtkJs.push(canvas);
46
+ const internalViewportEntry = { ...viewportInputEntry, canvas };
47
+ this.addVtkjsDrivenViewport(internalViewportEntry);
48
+ }
49
+ addVtkjsDrivenViewport(viewportInputEntry) {
50
+ const { element, canvas, viewportId, type, defaultOptions } = viewportInputEntry;
51
+ element.tabIndex = -1;
52
+ this.offscreenMultiRenderWindow.addRenderer({
53
+ viewport: [0, 0, 1, 1],
54
+ id: viewportId,
55
+ background: defaultOptions.background
56
+ ? defaultOptions.background
57
+ : [0, 0, 0],
58
+ });
59
+ const viewportInput = {
60
+ id: viewportId,
61
+ element,
62
+ renderingEngineId: this.id,
63
+ type,
64
+ canvas,
65
+ sx: 0,
66
+ sy: 0,
67
+ sWidth: canvas.width,
68
+ sHeight: canvas.height,
69
+ defaultOptions: defaultOptions || {},
70
+ };
71
+ let viewport;
72
+ if (type === ViewportType.STACK) {
73
+ viewport = new StackViewport(viewportInput);
74
+ }
75
+ else if (type === ViewportType.ORTHOGRAPHIC ||
76
+ type === ViewportType.PERSPECTIVE) {
77
+ viewport = new VolumeViewport(viewportInput);
78
+ }
79
+ else if (type === ViewportType.VOLUME_3D) {
80
+ viewport = new VolumeViewport3D(viewportInput);
81
+ }
82
+ else {
83
+ throw new Error(`Viewport Type ${type} is not supported`);
84
+ }
85
+ this._viewports.set(viewportId, viewport);
86
+ const eventDetail = {
87
+ element,
88
+ viewportId,
89
+ renderingEngineId: this.id,
90
+ };
91
+ if (!viewport.suppressEvents) {
92
+ triggerEvent(eventTarget, Events.ELEMENT_ENABLED, eventDetail);
93
+ }
94
+ }
95
+ setVtkjsDrivenViewports(viewportInputEntries) {
96
+ if (viewportInputEntries.length) {
97
+ const vtkDrivenCanvases = viewportInputEntries.map((vp) => getOrCreateCanvas(vp.element));
98
+ vtkDrivenCanvases.forEach((canvas) => {
99
+ const devicePixelRatio = window.devicePixelRatio || 1;
100
+ const rect = canvas.getBoundingClientRect();
101
+ canvas.width = rect.width * devicePixelRatio;
102
+ canvas.height = rect.height * devicePixelRatio;
103
+ });
104
+ for (let i = 0; i < viewportInputEntries.length; i++) {
105
+ const vtkDrivenViewportInputEntry = viewportInputEntries[i];
106
+ const canvas = vtkDrivenCanvases[i];
107
+ const internalViewportEntry = {
108
+ ...vtkDrivenViewportInputEntry,
109
+ canvas,
110
+ };
111
+ this.addVtkjsDrivenViewport(internalViewportEntry);
112
+ }
113
+ }
114
+ }
115
+ _resizeVTKViewports(vtkDrivenViewports, keepCamera = true, immediate = true) {
116
+ const canvasesDrivenByVtkJs = vtkDrivenViewports.map((vp) => {
117
+ return getOrCreateCanvas(vp.element);
118
+ });
119
+ canvasesDrivenByVtkJs.forEach((canvas) => {
120
+ const devicePixelRatio = window.devicePixelRatio || 1;
121
+ canvas.width = canvas.clientWidth * devicePixelRatio;
122
+ canvas.height = canvas.clientHeight * devicePixelRatio;
123
+ });
124
+ if (canvasesDrivenByVtkJs.length) {
125
+ this._resize(vtkDrivenViewports);
126
+ }
127
+ vtkDrivenViewports.forEach((vp) => {
128
+ const prevCamera = vp.getCamera();
129
+ const rotation = vp.getRotation();
130
+ const { flipHorizontal } = prevCamera;
131
+ vp.resetCameraForResize();
132
+ const displayArea = vp.getDisplayArea();
133
+ if (keepCamera) {
134
+ if (displayArea) {
135
+ if (flipHorizontal) {
136
+ vp.setCamera({ flipHorizontal });
137
+ }
138
+ if (rotation) {
139
+ vp.setViewPresentation({ rotation });
140
+ }
141
+ }
142
+ else {
143
+ vp.setCamera(prevCamera);
144
+ }
145
+ }
146
+ });
147
+ if (immediate) {
148
+ this.render();
149
+ }
150
+ }
151
+ renderViewportUsingCustomOrVtkPipeline(viewport) {
152
+ let eventDetail;
153
+ if (viewport.sWidth < VIEWPORT_MIN_SIZE ||
154
+ viewport.sHeight < VIEWPORT_MIN_SIZE) {
155
+ console.warn('Viewport is too small', viewport.sWidth, viewport.sHeight);
156
+ return;
157
+ }
158
+ if (viewportTypeUsesCustomRenderingPipeline(viewport.type) === true) {
159
+ eventDetail =
160
+ viewport.customRenderViewportToCanvas();
161
+ }
162
+ else {
163
+ if (this.useCPURendering) {
164
+ throw new Error('GPU not available, and using a viewport with no custom render pipeline.');
165
+ }
166
+ const { offscreenMultiRenderWindow } = this;
167
+ const renderWindow = offscreenMultiRenderWindow.getRenderWindow();
168
+ this._resizeOffScreenCanvasForSingleViewport(viewport.canvas);
169
+ const renderer = offscreenMultiRenderWindow.getRenderer(viewport.id);
170
+ renderer.setViewport([0, 0, 1, 1]);
171
+ const allRenderers = offscreenMultiRenderWindow.getRenderers();
172
+ allRenderers.forEach(({ renderer: r, id }) => {
173
+ r.setDraw(id === viewport.id);
174
+ });
175
+ const widgetRenderers = this.getWidgetRenderers();
176
+ widgetRenderers.forEach((viewportId, renderer) => {
177
+ renderer.setDraw(viewportId === viewport.id);
178
+ });
179
+ renderWindow.render();
180
+ allRenderers.forEach(({ renderer: r }) => r.setDraw(false));
181
+ const openGLRenderWindow = offscreenMultiRenderWindow.getOpenGLRenderWindow();
182
+ const context = openGLRenderWindow.get3DContext();
183
+ const offScreenCanvas = context.canvas;
184
+ eventDetail = this._renderViewportFromVtkCanvasToOnscreenCanvas(viewport, offScreenCanvas);
185
+ }
186
+ return eventDetail;
187
+ }
188
+ _renderViewportFromVtkCanvasToOnscreenCanvas(viewport, offScreenCanvas) {
189
+ const { element, canvas, id: viewportId, renderingEngineId, suppressEvents, } = viewport;
190
+ const { width: dWidth, height: dHeight } = canvas;
191
+ const onScreenContext = canvas.getContext('2d');
192
+ onScreenContext.drawImage(offScreenCanvas, 0, 0, dWidth, dHeight, 0, 0, dWidth, dHeight);
193
+ return {
194
+ element,
195
+ suppressEvents,
196
+ viewportId,
197
+ renderingEngineId,
198
+ viewportStatus: viewport.viewportStatus,
199
+ };
200
+ }
201
+ _resizeOffScreenCanvasForSingleViewport(currentViewport) {
202
+ const { offScreenCanvasContainer, offscreenMultiRenderWindow } = this;
203
+ const offScreenCanvasWidth = currentViewport.width;
204
+ const offScreenCanvasHeight = currentViewport.height;
205
+ offScreenCanvasContainer.width = offScreenCanvasWidth;
206
+ offScreenCanvasContainer.height = offScreenCanvasHeight;
207
+ offscreenMultiRenderWindow.resize();
208
+ return { offScreenCanvasWidth, offScreenCanvasHeight };
209
+ }
210
+ _resize(viewportsDrivenByVtkJs) {
211
+ for (const viewport of viewportsDrivenByVtkJs) {
212
+ viewport.sx = 0;
213
+ viewport.sy = 0;
214
+ viewport.sWidth = viewport.canvas.width;
215
+ viewport.sHeight = viewport.canvas.height;
216
+ const renderer = this.offscreenMultiRenderWindow.getRenderer(viewport.id);
217
+ renderer.setViewport([0, 0, 1, 1]);
218
+ }
219
+ }
220
+ getWidgetRenderers() {
221
+ const allViewports = this._getViewportsAsArray();
222
+ const widgetRenderers = new Map();
223
+ allViewports.forEach((vp) => {
224
+ const widgets = vp.getWidgets ? vp.getWidgets() : [];
225
+ widgets.forEach((widget) => {
226
+ const renderer = widget.getRenderer ? widget.getRenderer() : null;
227
+ if (renderer) {
228
+ widgetRenderers.set(renderer, vp.id);
229
+ }
230
+ });
231
+ });
232
+ return widgetRenderers;
233
+ }
234
+ }
235
+ export default SequentialRenderingEngine;
@@ -181,7 +181,9 @@ declare class StackViewport extends Viewport {
181
181
  jumpToWorld(worldPos: Point3): boolean;
182
182
  private canvasToWorldCPU;
183
183
  private worldToCanvasCPU;
184
+ private canvasToWorldGPUSequential;
184
185
  private canvasToWorldGPU;
186
+ private worldToCanvasGPUSequential;
185
187
  private worldToCanvasGPU;
186
188
  private _getVOIRangeForCurrentImage;
187
189
  private _getValidVOILUTFunction;
@@ -43,6 +43,7 @@ import uuidv4 from '../utilities/uuidv4';
43
43
  import getSpacingInNormalDirection from '../utilities/getSpacingInNormalDirection';
44
44
  import getClosestImageId from '../utilities/getClosestImageId';
45
45
  import { adjustInitialViewUp } from '../utilities/adjustInitialViewUp';
46
+ import { isSequentialRenderingEngine } from './helpers/isSequentialRenderingEngine';
46
47
  const EPSILON = 1;
47
48
  const log = coreLog.getLogger('RenderingEngine', 'StackViewport');
48
49
  class StackViewport extends Viewport {
@@ -195,6 +196,30 @@ class StackViewport extends Viewport {
195
196
  const canvasPoint = pixelToCanvas(this._cpuFallbackEnabledElement, indexPoint);
196
197
  return canvasPoint;
197
198
  };
199
+ this.canvasToWorldGPUSequential = (canvasPos) => {
200
+ const renderer = this.getRenderer();
201
+ const vtkCamera = this.getVtkActiveCamera();
202
+ const crange = vtkCamera.getClippingRange();
203
+ const distance = vtkCamera.getDistance();
204
+ vtkCamera.setClippingRange(distance, distance + 0.1);
205
+ const devicePixelRatio = window.devicePixelRatio || 1;
206
+ const { width, height } = this.canvas;
207
+ const aspectRatio = width / height;
208
+ const canvasPosWithDPR = [
209
+ canvasPos[0] * devicePixelRatio,
210
+ canvasPos[1] * devicePixelRatio,
211
+ ];
212
+ const normalizedDisplay = [
213
+ canvasPosWithDPR[0] / width,
214
+ 1 - canvasPosWithDPR[1] / height,
215
+ 0,
216
+ ];
217
+ const projCoords = renderer.normalizedDisplayToProjection(normalizedDisplay[0], normalizedDisplay[1], normalizedDisplay[2]);
218
+ const viewCoords = renderer.projectionToView(projCoords[0], projCoords[1], projCoords[2], aspectRatio);
219
+ const worldCoord = renderer.viewToWorld(viewCoords[0], viewCoords[1], viewCoords[2]);
220
+ vtkCamera.setClippingRange(crange[0], crange[1]);
221
+ return [worldCoord[0], worldCoord[1], worldCoord[2]];
222
+ };
198
223
  this.canvasToWorldGPU = (canvasPos) => {
199
224
  const renderer = this.getRenderer();
200
225
  const vtkCamera = this.getVtkActiveCamera();
@@ -218,6 +243,27 @@ class StackViewport extends Viewport {
218
243
  vtkCamera.setClippingRange(crange[0], crange[1]);
219
244
  return [worldCoord[0], worldCoord[1], worldCoord[2]];
220
245
  };
246
+ this.worldToCanvasGPUSequential = (worldPos) => {
247
+ const renderer = this.getRenderer();
248
+ const vtkCamera = this.getVtkActiveCamera();
249
+ const crange = vtkCamera.getClippingRange();
250
+ const distance = vtkCamera.getDistance();
251
+ vtkCamera.setClippingRange(distance, distance + 0.1);
252
+ const devicePixelRatio = window.devicePixelRatio || 1;
253
+ const { width, height } = this.canvas;
254
+ const aspectRatio = width / height;
255
+ const viewCoords = renderer.worldToView(worldPos[0], worldPos[1], worldPos[2]);
256
+ const projCoords = renderer.viewToProjection(viewCoords[0], viewCoords[1], viewCoords[2], aspectRatio);
257
+ const normalizedDisplay = renderer.projectionToNormalizedDisplay(projCoords[0], projCoords[1], projCoords[2]);
258
+ const canvasX = normalizedDisplay[0] * width;
259
+ const canvasY = (1 - normalizedDisplay[1]) * height;
260
+ vtkCamera.setClippingRange(crange[0], crange[1]);
261
+ const canvasCoordWithDPR = [
262
+ canvasX / devicePixelRatio,
263
+ canvasY / devicePixelRatio,
264
+ ];
265
+ return canvasCoordWithDPR;
266
+ };
221
267
  this.worldToCanvasGPU = (worldPos) => {
222
268
  const renderer = this.getRenderer();
223
269
  const vtkCamera = this.getVtkActiveCamera();
@@ -344,11 +390,17 @@ class StackViewport extends Viewport {
344
390
  },
345
391
  canvasToWorld: {
346
392
  cpu: this.canvasToWorldCPU,
347
- gpu: this.canvasToWorldGPU,
393
+ gpu: {
394
+ sequential: this.canvasToWorldGPUSequential,
395
+ default: this.canvasToWorldGPU,
396
+ },
348
397
  },
349
398
  worldToCanvas: {
350
399
  cpu: this.worldToCanvasCPU,
351
- gpu: this.worldToCanvasGPU,
400
+ gpu: {
401
+ sequential: this.worldToCanvasGPUSequential,
402
+ default: this.worldToCanvasGPU,
403
+ },
352
404
  },
353
405
  getRenderer: {
354
406
  cpu: () => this.getCPUFallbackError('getRenderer'),
@@ -407,11 +459,27 @@ class StackViewport extends Viewport {
407
459
  return getShouldUseCPURendering();
408
460
  }
409
461
  _configureRenderingPipeline(value) {
462
+ const renderingEngine = this.getRenderingEngine();
463
+ const isSequential = isSequentialRenderingEngine(renderingEngine);
410
464
  this.useCPURendering = value ?? getShouldUseCPURendering();
411
465
  for (const key in this.renderingPipelineFunctions) {
412
466
  if (Object.prototype.hasOwnProperty.call(this.renderingPipelineFunctions, key)) {
413
467
  const functions = this.renderingPipelineFunctions[key];
414
- this[key] = this.useCPURendering ? functions.cpu : functions.gpu;
468
+ if (this.useCPURendering) {
469
+ this[key] = functions.cpu;
470
+ }
471
+ else {
472
+ if (typeof functions.gpu === 'object' &&
473
+ functions.gpu.sequential &&
474
+ functions.gpu.default) {
475
+ this[key] = isSequential
476
+ ? functions.gpu.sequential
477
+ : functions.gpu.default;
478
+ }
479
+ else {
480
+ this[key] = functions.gpu;
481
+ }
482
+ }
415
483
  }
416
484
  }
417
485
  const result = this.useCPURendering
@@ -0,0 +1,23 @@
1
+ import BaseRenderingEngine from './BaseRenderingEngine';
2
+ import type IStackViewport from '../types/IStackViewport';
3
+ import type IVolumeViewport from '../types/IVolumeViewport';
4
+ import type * as EventTypes from '../types/EventTypes';
5
+ import type { InternalViewportInput, NormalizedViewportInput, IViewport } from '../types/IViewport';
6
+ declare class StandardRenderingEngine extends BaseRenderingEngine {
7
+ protected enableVTKjsDrivenViewport(viewportInputEntry: NormalizedViewportInput): void;
8
+ protected addVtkjsDrivenViewport(viewportInputEntry: InternalViewportInput, offscreenCanvasProperties?: {
9
+ offScreenCanvasWidth: number;
10
+ offScreenCanvasHeight: number;
11
+ xOffset: number;
12
+ }): void;
13
+ protected setVtkjsDrivenViewports(viewportInputEntries: NormalizedViewportInput[]): void;
14
+ protected _resizeVTKViewports(vtkDrivenViewports: (IStackViewport | IVolumeViewport)[], keepCamera?: boolean, immediate?: boolean): void;
15
+ protected _renderFlaggedViewports: () => void;
16
+ private performVtkDrawCall;
17
+ protected renderViewportUsingCustomOrVtkPipeline(viewport: IViewport): EventTypes.ImageRenderedEventDetail;
18
+ protected _renderViewportFromVtkCanvasToOnscreenCanvas(viewport: IViewport, offScreenCanvas: HTMLCanvasElement): EventTypes.ImageRenderedEventDetail;
19
+ private _resizeOffScreenCanvas;
20
+ private _resize;
21
+ private _getViewportCoordsOnOffScreenCanvas;
22
+ }
23
+ export default StandardRenderingEngine;