@kitware/vtk.js 28.13.1 → 29.1.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 (41) hide show
  1. package/BREAKING_CHANGES.md +7 -0
  2. package/Common/Core/Math/index.js +1 -1
  3. package/Common/Core/Math.js +1 -1
  4. package/Proxy/Core/ViewProxy.d.ts +2 -1
  5. package/Rendering/Core/CellPicker.d.ts +11 -0
  6. package/Rendering/Core/CellPicker.js +112 -4
  7. package/Rendering/Core/ColorTransferFunction.js +1 -1
  8. package/Rendering/Core/Coordinate.js +1 -1
  9. package/Rendering/Core/CubeAxesActor.js +1 -1
  10. package/Rendering/Core/ImageMapper.js +1 -1
  11. package/Rendering/Core/Picker.js +26 -2
  12. package/Rendering/Core/PointPicker.js +1 -1
  13. package/Rendering/Core/RenderWindow.d.ts +1 -0
  14. package/Rendering/Core/Renderer.js +1 -1
  15. package/Rendering/Core/ScalarBarActor.js +1 -1
  16. package/Rendering/Core/VolumeMapper.js +1 -1
  17. package/Rendering/Misc/FullScreenRenderWindow.d.ts +4 -1
  18. package/Rendering/Misc/FullScreenRenderWindow.js +2 -1
  19. package/Rendering/Misc/GenericRenderWindow.d.ts +3 -2
  20. package/Rendering/Misc/GenericRenderWindow.js +11 -9
  21. package/Rendering/OpenGL/PolyDataMapper2D.js +1 -1
  22. package/Rendering/OpenGL/RenderWindow.d.ts +0 -34
  23. package/Rendering/OpenGL/RenderWindow.js +2 -177
  24. package/Rendering/OpenGL/Texture.js +1 -1
  25. package/Rendering/OpenGL/VolumeMapper.js +4 -4
  26. package/Rendering/WebGPU/RenderWindow.js +12 -0
  27. package/Rendering/{OpenGL/RenderWindow → WebXR/RenderWindowHelper}/Constants.d.ts +2 -1
  28. package/Rendering/WebXR/RenderWindowHelper.d.ts +90 -0
  29. package/Rendering/WebXR/RenderWindowHelper.js +242 -0
  30. package/Widgets/Core/WidgetManager.d.ts +0 -24
  31. package/Widgets/Core/WidgetManager.js +5 -29
  32. package/Widgets/Widgets3D/AngleWidget.js +1 -1
  33. package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +1 -1
  34. package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +1 -1
  35. package/Widgets/Widgets3D.js +0 -2
  36. package/index.d.ts +2 -1
  37. package/package.json +2 -1
  38. package/Widgets/Widgets3D/DistanceWidget/behavior.js +0 -133
  39. package/Widgets/Widgets3D/DistanceWidget/state.js +0 -22
  40. package/Widgets/Widgets3D/DistanceWidget.js +0 -109
  41. /package/Rendering/{OpenGL/RenderWindow → WebXR/RenderWindowHelper}/Constants.js +0 -0
@@ -8,16 +8,12 @@ import vtkTextureUnitManager from './TextureUnitManager.js';
8
8
  import vtkViewNodeFactory from './ViewNodeFactory.js';
9
9
  import vtkRenderPass from '../SceneGraph/RenderPass.js';
10
10
  import vtkRenderWindowViewNode from '../SceneGraph/RenderWindowViewNode.js';
11
- import Constants from './RenderWindow/Constants.js';
12
- import { createContextProxyHandler, GET_UNDERLYING_CONTEXT } from './RenderWindow/ContextProxy.js';
11
+ import { createContextProxyHandler } from './RenderWindow/ContextProxy.js';
13
12
 
14
13
  const {
15
14
  vtkDebugMacro,
16
15
  vtkErrorMacro
17
16
  } = macro;
18
- const {
19
- XrSessionTypes
20
- } = Constants;
21
17
  const SCREENSHOT_PLACEHOLDER = {
22
18
  position: 'absolute',
23
19
  top: 0,
@@ -25,12 +21,6 @@ const SCREENSHOT_PLACEHOLDER = {
25
21
  width: '100%',
26
22
  height: '100%'
27
23
  };
28
- const DEFAULT_RESET_FACTORS = {
29
- rescaleFactor: 0.25,
30
- // isotropic scale factor reduces apparent size of objects
31
- translateZ: -1.5 // default translation initializes object in front of camera
32
- };
33
-
34
24
  function checkRenderTargetSupport(gl, format, type) {
35
25
  // create temporary frame buffer and texture
36
26
  const framebuffer = gl.createFramebuffer();
@@ -227,168 +217,6 @@ function vtkOpenGLRenderWindow(publicAPI, model) {
227
217
  }
228
218
  return new Proxy(result, cachingContextHandler);
229
219
  };
230
-
231
- // Request an XR session on the user device with WebXR,
232
- // typically in response to a user request such as a button press
233
- publicAPI.startXR = xrSessionType => {
234
- if (navigator.xr === undefined) {
235
- throw new Error('WebXR is not available');
236
- }
237
- model.xrSessionType = xrSessionType !== undefined ? xrSessionType : XrSessionTypes.HmdVR;
238
- const isXrSessionAR = [XrSessionTypes.HmdAR, XrSessionTypes.MobileAR].includes(model.xrSessionType);
239
- const sessionType = isXrSessionAR ? 'immersive-ar' : 'immersive-vr';
240
- if (!navigator.xr.isSessionSupported(sessionType)) {
241
- if (isXrSessionAR) {
242
- throw new Error('Device does not support AR session');
243
- } else {
244
- throw new Error('VR display is not available');
245
- }
246
- }
247
- if (model.xrSession === null) {
248
- navigator.xr.requestSession(sessionType).then(publicAPI.enterXR, () => {
249
- throw new Error('Failed to create XR session!');
250
- });
251
- } else {
252
- throw new Error('XR Session already exists!');
253
- }
254
- };
255
-
256
- // When an XR session is available, set up the XRWebGLLayer
257
- // and request the first animation frame for the device
258
- publicAPI.enterXR = async xrSession => {
259
- model.xrSession = xrSession;
260
- model.oldCanvasSize = model.size.slice();
261
- if (model.xrSession !== null) {
262
- const gl = publicAPI.get3DContext();
263
- await gl.makeXRCompatible();
264
- const glLayer = new global.XRWebGLLayer(model.xrSession,
265
- // constructor needs unproxied context
266
- gl[GET_UNDERLYING_CONTEXT]());
267
- publicAPI.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
268
- model.xrSession.updateRenderState({
269
- baseLayer: glLayer
270
- });
271
- model.xrSession.requestReferenceSpace('local').then(refSpace => {
272
- model.xrReferenceSpace = refSpace;
273
- });
274
-
275
- // Initialize transparent background for augmented reality session
276
- const isXrSessionAR = [XrSessionTypes.HmdAR, XrSessionTypes.MobileAR].includes(model.xrSessionType);
277
- if (isXrSessionAR) {
278
- const ren = model.renderable.getRenderers()[0];
279
- model.preXrSessionBackground = ren.getBackground();
280
- ren.setBackground([0, 0, 0, 0]);
281
- }
282
- publicAPI.resetXRScene();
283
- model.renderable.getInteractor().switchToXRAnimation();
284
- model.xrSceneFrame = model.xrSession.requestAnimationFrame(publicAPI.xrRender);
285
- } else {
286
- throw new Error('Failed to enter XR with a null xrSession.');
287
- }
288
- };
289
- publicAPI.resetXRScene = function () {
290
- let rescaleFactor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_RESET_FACTORS.rescaleFactor;
291
- let translateZ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_RESET_FACTORS.translateZ;
292
- // Adjust world-to-physical parameters for different modalities
293
-
294
- const ren = model.renderable.getRenderers()[0];
295
- ren.resetCamera();
296
- const camera = ren.getActiveCamera();
297
- let physicalScale = camera.getPhysicalScale();
298
- const physicalTranslation = camera.getPhysicalTranslation();
299
- const rescaledTranslateZ = translateZ * physicalScale;
300
- physicalScale /= rescaleFactor;
301
- physicalTranslation[2] += rescaledTranslateZ;
302
- camera.setPhysicalScale(physicalScale);
303
- camera.setPhysicalTranslation(physicalTranslation);
304
- // Clip at 0.1m, 100.0m in physical space by default
305
- camera.setClippingRange(0.1 * physicalScale, 100.0 * physicalScale);
306
- };
307
- publicAPI.stopXR = async () => {
308
- if (navigator.xr === undefined) {
309
- // WebXR polyfill not available so nothing to do
310
- return;
311
- }
312
- if (model.xrSession !== null) {
313
- model.xrSession.cancelAnimationFrame(model.xrSceneFrame);
314
- model.renderable.getInteractor().returnFromXRAnimation();
315
- const gl = publicAPI.get3DContext();
316
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
317
- await model.xrSession.end().catch(error => {
318
- if (!(error instanceof DOMException)) {
319
- throw error;
320
- }
321
- });
322
- model.xrSession = null;
323
- }
324
- if (model.oldCanvasSize !== undefined) {
325
- publicAPI.setSize(...model.oldCanvasSize);
326
- }
327
-
328
- // Reset to default canvas
329
- const ren = model.renderable.getRenderers()[0];
330
- if (model.preXrSessionBackground != null) {
331
- ren.setBackground(model.preXrSessionBackground);
332
- model.preXrSessionBackground = null;
333
- }
334
- ren.getActiveCamera().setProjectionMatrix(null);
335
- ren.resetCamera();
336
- ren.setViewport(0.0, 0, 1.0, 1.0);
337
- publicAPI.traverseAllPasses();
338
- };
339
- publicAPI.xrRender = async (t, frame) => {
340
- const xrSession = frame.session;
341
- const isXrSessionHMD = [XrSessionTypes.HmdVR, XrSessionTypes.HmdAR].includes(model.xrSessionType);
342
- model.renderable.getInteractor().updateXRGamepads(xrSession, frame, model.xrReferenceSpace);
343
- model.xrSceneFrame = model.xrSession.requestAnimationFrame(publicAPI.xrRender);
344
- const xrPose = frame.getViewerPose(model.xrReferenceSpace);
345
- if (xrPose) {
346
- const gl = publicAPI.get3DContext();
347
- if (model.xrSessionType === XrSessionTypes.MobileAR && model.oldCanvasSize !== undefined) {
348
- gl.canvas.width = model.oldCanvasSize[0];
349
- gl.canvas.height = model.oldCanvasSize[1];
350
- }
351
- const glLayer = xrSession.renderState.baseLayer;
352
- gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
353
- gl.clear(gl.COLOR_BUFFER_BIT);
354
- gl.clear(gl.DEPTH_BUFFER_BIT);
355
- publicAPI.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
356
-
357
- // get the first renderer
358
- const ren = model.renderable.getRenderers()[0];
359
-
360
- // Do a render pass for each eye
361
- xrPose.views.forEach((view, index) => {
362
- const viewport = glLayer.getViewport(view);
363
- if (isXrSessionHMD) {
364
- if (view.eye === 'left') {
365
- ren.setViewport(0, 0, 0.5, 1.0);
366
- } else if (view.eye === 'right') {
367
- ren.setViewport(0.5, 0, 1.0, 1.0);
368
- } else {
369
- // No handling for non-eye viewport
370
- return;
371
- }
372
- } else if (model.xrSessionType === XrSessionTypes.LookingGlassVR) {
373
- const startX = viewport.x / glLayer.framebufferWidth;
374
- const startY = viewport.y / glLayer.framebufferHeight;
375
- const endX = (viewport.x + viewport.width) / glLayer.framebufferWidth;
376
- const endY = (viewport.y + viewport.height) / glLayer.framebufferHeight;
377
- ren.setViewport(startX, startY, endX, endY);
378
- } else {
379
- ren.setViewport(0, 0, 1, 1);
380
- }
381
- ren.getActiveCamera().computeViewParametersFromPhysicalMatrix(view.transform.inverse.matrix);
382
- ren.getActiveCamera().setProjectionMatrix(view.projectionMatrix);
383
- publicAPI.traverseAllPasses();
384
- });
385
-
386
- // Reset scissorbox before any subsequent rendering to external displays
387
- // on frame end, such as rendering to a Looking Glass display.
388
- gl.scissor(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight);
389
- gl.disable(gl.SCISSOR_TEST);
390
- }
391
- };
392
220
  publicAPI.restoreContext = () => {
393
221
  const rp = vtkRenderPass.newInstance();
394
222
  rp.setCurrentOperation('Release');
@@ -855,9 +683,6 @@ const DEFAULT_VALUES = {
855
683
  defaultToWebgl2: true,
856
684
  // attempt webgl2 on by default
857
685
  activeFramebuffer: null,
858
- xrSession: null,
859
- xrReferenceSpace: null,
860
- xrSupported: true,
861
686
  imageFormat: 'image/png',
862
687
  useOffScreen: false,
863
688
  useBackgroundImage: false
@@ -906,7 +731,7 @@ function extend(publicAPI, model) {
906
731
  macro.event(publicAPI, model, 'imageReady');
907
732
 
908
733
  // Build VTK API
909
- macro.get(publicAPI, model, ['shaderCache', 'textureUnitManager', 'webgl2', 'vrDisplay', 'useBackgroundImage', 'xrSupported', 'activeFramebuffer']);
734
+ macro.get(publicAPI, model, ['shaderCache', 'textureUnitManager', 'webgl2', 'useBackgroundImage', 'activeFramebuffer']);
910
735
  macro.setGet(publicAPI, model, ['initialized', 'context', 'canvas', 'renderPasses', 'notifyStartCaptureImage', 'defaultToWebgl2', 'cursor', 'useOffScreen']);
911
736
  macro.setGetArray(publicAPI, model, ['size'], 2);
912
737
  macro.event(publicAPI, model, 'windowResizeEvent');
@@ -2,7 +2,7 @@ import Constants from './Texture/Constants.js';
2
2
  import HalfFloat from '../../Common/Core/HalfFloat.js';
3
3
  import { n as newInstance$1, o as obj, s as set, e as setGet, g as get, i as moveToProtected, a as newTypedArray, c as macro } from '../../macros2.js';
4
4
  import vtkDataArray from '../../Common/Core/DataArray.js';
5
- import { R as isPowerOfTwo, N as nearestPowerOfTwo } from '../../Common/Core/Math/index.js';
5
+ import { S as isPowerOfTwo, O as nearestPowerOfTwo } from '../../Common/Core/Math/index.js';
6
6
  import vtkViewNode from '../SceneGraph/ViewNode.js';
7
7
  import { registerOverride } from './ViewNodeFactory.js';
8
8
 
@@ -175,8 +175,8 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
175
175
 
176
176
  // if we have a ztexture then declare it and use it
177
177
  if (model.zBufferTexture !== null) {
178
- FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', ['uniform sampler2D zBufferTexture;', 'uniform float vpWidth;', 'uniform float vpHeight;']).result;
179
- FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['vec4 depthVec = texture2D(zBufferTexture, vec2(gl_FragCoord.x / vpWidth, gl_FragCoord.y/vpHeight));', 'float zdepth = (depthVec.r*256.0 + depthVec.g)/257.0;', 'zdepth = zdepth * 2.0 - 1.0;', 'if (cameraParallel == 0) {', 'zdepth = -2.0 * camFar * camNear / (zdepth*(camFar-camNear)-(camFar+camNear)) - camNear;}', 'else {', 'zdepth = (zdepth + 1.0) * 0.5 * (camFar - camNear);}\n', 'zdepth = -zdepth/rayDir.z;', 'dists.y = min(zdepth,dists.y);']).result;
178
+ FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Dec', ['uniform sampler2D zBufferTexture;', 'uniform float vpZWidth;', 'uniform float vpZHeight;']).result;
179
+ FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', ['vec4 depthVec = texture2D(zBufferTexture, vec2(gl_FragCoord.x / vpZWidth, gl_FragCoord.y/vpZHeight));', 'float zdepth = (depthVec.r*256.0 + depthVec.g)/257.0;', 'zdepth = zdepth * 2.0 - 1.0;', 'if (cameraParallel == 0) {', 'zdepth = -2.0 * camFar * camNear / (zdepth*(camFar-camNear)-(camFar+camNear)) - camNear;}', 'else {', 'zdepth = (zdepth + 1.0) * 0.5 * (camFar - camNear);}\n', 'zdepth = -zdepth/rayDir.z;', 'dists.y = min(zdepth,dists.y);']).result;
180
180
  }
181
181
 
182
182
  // Set the BlendMode approach
@@ -362,8 +362,8 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
362
362
  if (model.zBufferTexture !== null) {
363
363
  program.setUniformi('zBufferTexture', model.zBufferTexture.getTextureUnit());
364
364
  const size = model._useSmallViewport ? [model._smallViewportWidth, model._smallViewportHeight] : model._openGLRenderWindow.getFramebufferSize();
365
- program.setUniformf('vpWidth', size[0]);
366
- program.setUniformf('vpHeight', size[1]);
365
+ program.setUniformf('vpZWidth', size[0]);
366
+ program.setUniformf('vpZHeight', size[1]);
367
367
  }
368
368
  };
369
369
  publicAPI.setCameraShaderParameters = (cellBO, ren, actor) => {
@@ -490,6 +490,17 @@ function vtkWebGPURenderWindow(publicAPI, model) {
490
490
  ret.setWebGPURenderWindow(publicAPI);
491
491
  return ret;
492
492
  };
493
+ const superSetSize = publicAPI.setSize;
494
+ publicAPI.setSize = (width, height) => {
495
+ const modified = superSetSize(width, height);
496
+ if (modified) {
497
+ publicAPI.invokeWindowResizeEvent({
498
+ width,
499
+ height
500
+ });
501
+ }
502
+ return modified;
503
+ };
493
504
  publicAPI.delete = macro.chain(publicAPI.delete, publicAPI.setViewStream);
494
505
  }
495
506
 
@@ -555,6 +566,7 @@ function extend(publicAPI, model) {
555
566
  macro.get(publicAPI, model, ['commandEncoder', 'device', 'presentationFormat', 'useBackgroundImage', 'xrSupported']);
556
567
  macro.setGet(publicAPI, model, ['initialized', 'context', 'canvas', 'device', 'renderPasses', 'notifyStartCaptureImage', 'cursor', 'useOffScreen']);
557
568
  macro.setGetArray(publicAPI, model, ['size'], 2);
569
+ macro.event(publicAPI, model, 'windowResizeEvent');
558
570
 
559
571
  // Object methods
560
572
  vtkWebGPURenderWindow(publicAPI, model);
@@ -1,7 +1,8 @@
1
1
  export declare enum XrSessionTypes {
2
2
  HmdVR = 0,
3
3
  MobileAR = 1,
4
- LookingGlassVR = 2
4
+ LookingGlassVR = 2,
5
+ HmdAR = 3
5
6
  }
6
7
 
7
8
  declare const _default: {
@@ -0,0 +1,90 @@
1
+ import { vtkObject } from './../../interfaces';
2
+ import { Nullable } from './../../types';
3
+ import vtkOpenGLRenderWindow from './../OpenGL/RenderWindow';
4
+
5
+ /**
6
+ *
7
+ */
8
+ export interface IWebXRRenderWindowHelperInitialValues {
9
+ initialized: boolean,
10
+ initCanvasSize?: [number, number],
11
+ initBackground?: [number, number, number, number],
12
+ renderWindow?: Nullable<vtkOpenGLRenderWindow>,
13
+ xrSession?: Nullable<XRSession>,
14
+ xrSessionType: number,
15
+ xrReferenceSpace?: any,
16
+ }
17
+
18
+ export interface vtkWebXRRenderWindowHelper extends vtkObject {
19
+
20
+ /**
21
+ * Initialize the instance.
22
+ */
23
+ initialize(): void;
24
+
25
+ /**
26
+ * Request an XR session on the user device with WebXR,
27
+ * typically in response to a user request such as a button press.
28
+ */
29
+ startXR(xrSessionType: Number): void;
30
+
31
+ /**
32
+ * When an XR session is available, set up the XRWebGLLayer
33
+ * and request the first animation frame for the device
34
+ */
35
+ enterXR(): void;
36
+
37
+ /**
38
+ * Adjust world-to-physical parameters for different viewing modalities
39
+ *
40
+ * @param {Number} inputRescaleFactor
41
+ * @param {Number} inputTranslateZ
42
+ */
43
+ resetXRScene(inputRescaleFactor: number, inputTranslateZ: number): void;
44
+
45
+ /**
46
+ * Request to stop the current XR session
47
+ */
48
+ stopXR(): void;
49
+
50
+ /**
51
+ * Get the underlying render window to drive XR rendering.
52
+ */
53
+ getRenderWindow(): Nullable<vtkOpenGLRenderWindow>;
54
+
55
+ /**
56
+ * Set the underlying render window to drive XR rendering.
57
+ */
58
+ setRenderWindow(renderWindow:Nullable<vtkOpenGLRenderWindow>);
59
+
60
+ /**
61
+ * Get the active WebXR session.
62
+ */
63
+ getXrSession(): Nullable<XRSession>;
64
+ }
65
+
66
+ /**
67
+ * Method used to decorate a given object (publicAPI+model) with vtkWebXRRenderWindowHelper characteristics.
68
+ *
69
+ * @param publicAPI object on which methods will be bounds (public)
70
+ * @param model object on which data structure will be bounds (protected)
71
+ * @param {IWebXRRenderWindowHelperInitialValues} [initialValues] (default: {})
72
+ */
73
+ export function extend(publicAPI: object, model: object, initialValues?: IWebXRRenderWindowHelperInitialValues): void;
74
+
75
+ /**
76
+ * Method used to create a new instance of vtkWebXRRenderWindowHelper.
77
+ * @param {IWebXRRenderWindowHelperInitialValues} [initialValues] for pre-setting some of its content
78
+ */
79
+ export function newInstance(initialValues?: IWebXRRenderWindowHelperInitialValues): vtkWebXRRenderWindowHelper;
80
+
81
+ /**
82
+ * WebXR rendering helper
83
+ *
84
+ * vtkWebXRRenderWindowHelper is designed to wrap a vtkRenderWindow for XR rendering.
85
+ */
86
+ export declare const vtkWebXRRenderWindowHelper: {
87
+ newInstance: typeof newInstance,
88
+ extend: typeof extend,
89
+ };
90
+ export default vtkWebXRRenderWindowHelper;
@@ -0,0 +1,242 @@
1
+ import { m as macro } from '../../macros2.js';
2
+ import Constants from './RenderWindowHelper/Constants.js';
3
+ import { GET_UNDERLYING_CONTEXT } from '../OpenGL/RenderWindow/ContextProxy.js';
4
+
5
+ const {
6
+ XrSessionTypes
7
+ } = Constants;
8
+ const DEFAULT_RESET_FACTORS = {
9
+ rescaleFactor: 0.25,
10
+ // isotropic scale factor reduces apparent size of objects
11
+ translateZ: -1.5 // default translation initializes object in front of camera
12
+ };
13
+
14
+ // ----------------------------------------------------------------------------
15
+ // vtkWebXRRenderWindowHelper methods
16
+ // ----------------------------------------------------------------------------
17
+
18
+ function vtkWebXRRenderWindowHelper(publicAPI, model) {
19
+ // Set our className
20
+ model.classHierarchy.push('vtkWebXRRenderWindowHelper');
21
+ publicAPI.initialize = renderWindow => {
22
+ if (!model.initialized) {
23
+ model.renderWindow = renderWindow;
24
+ model.initialized = true;
25
+ }
26
+ };
27
+ publicAPI.getXrSupported = () => navigator.xr !== undefined;
28
+
29
+ // Request an XR session on the user device with WebXR,
30
+ // typically in response to a user request such as a button press
31
+ publicAPI.startXR = xrSessionType => {
32
+ if (navigator.xr === undefined) {
33
+ throw new Error('WebXR is not available');
34
+ }
35
+ model.xrSessionType = xrSessionType !== undefined ? xrSessionType : XrSessionTypes.HmdVR;
36
+ const isXrSessionAR = [XrSessionTypes.HmdAR, XrSessionTypes.MobileAR].includes(model.xrSessionType);
37
+ const sessionType = isXrSessionAR ? 'immersive-ar' : 'immersive-vr';
38
+ if (!navigator.xr.isSessionSupported(sessionType)) {
39
+ if (isXrSessionAR) {
40
+ throw new Error('Device does not support AR session');
41
+ } else {
42
+ throw new Error('VR display is not available');
43
+ }
44
+ }
45
+ if (model.xrSession === null) {
46
+ navigator.xr.requestSession(sessionType).then(publicAPI.enterXR, () => {
47
+ throw new Error('Failed to create XR session!');
48
+ });
49
+ } else {
50
+ throw new Error('XR Session already exists!');
51
+ }
52
+ };
53
+
54
+ // When an XR session is available, set up the XRWebGLLayer
55
+ // and request the first animation frame for the device
56
+ publicAPI.enterXR = async xrSession => {
57
+ model.xrSession = xrSession;
58
+ model.initCanvasSize = model.renderWindow.getSize();
59
+ if (model.xrSession !== null) {
60
+ const gl = model.renderWindow.get3DContext();
61
+ await gl.makeXRCompatible();
62
+
63
+ // XRWebGLLayer definition is deferred to here to give any WebXR polyfill
64
+ // an opportunity to override this definition.
65
+ const {
66
+ XRWebGLLayer
67
+ } = window;
68
+ const glLayer = new XRWebGLLayer(model.xrSession,
69
+ // constructor needs unproxied context
70
+ gl[GET_UNDERLYING_CONTEXT]());
71
+ model.renderWindow.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
72
+ model.xrSession.updateRenderState({
73
+ baseLayer: glLayer
74
+ });
75
+ model.xrSession.requestReferenceSpace('local').then(refSpace => {
76
+ model.xrReferenceSpace = refSpace;
77
+ });
78
+
79
+ // Initialize transparent background for augmented reality session
80
+ const isXrSessionAR = [XrSessionTypes.HmdAR, XrSessionTypes.MobileAR].includes(model.xrSessionType);
81
+ if (isXrSessionAR) {
82
+ const ren = model.renderWindow.getRenderable().getRenderers()[0];
83
+ model.initBackground = ren.getBackground();
84
+ ren.setBackground([0, 0, 0, 0]);
85
+ }
86
+ publicAPI.resetXRScene();
87
+ model.renderWindow.getRenderable().getInteractor().switchToXRAnimation();
88
+ model.xrSceneFrame = model.xrSession.requestAnimationFrame(model.xrRender);
89
+ publicAPI.modified();
90
+ } else {
91
+ throw new Error('Failed to enter XR with a null xrSession.');
92
+ }
93
+ };
94
+ publicAPI.resetXRScene = function () {
95
+ let rescaleFactor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_RESET_FACTORS.rescaleFactor;
96
+ let translateZ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_RESET_FACTORS.translateZ;
97
+ // Adjust world-to-physical parameters for different modalities
98
+
99
+ const ren = model.renderWindow.getRenderable().getRenderers()[0];
100
+ ren.resetCamera();
101
+ const camera = ren.getActiveCamera();
102
+ let physicalScale = camera.getPhysicalScale();
103
+ const physicalTranslation = camera.getPhysicalTranslation();
104
+ const rescaledTranslateZ = translateZ * physicalScale;
105
+ physicalScale /= rescaleFactor;
106
+ physicalTranslation[2] += rescaledTranslateZ;
107
+ camera.setPhysicalScale(physicalScale);
108
+ camera.setPhysicalTranslation(physicalTranslation);
109
+ // Clip at 0.1m, 100.0m in physical space by default
110
+ camera.setClippingRange(0.1 * physicalScale, 100.0 * physicalScale);
111
+ };
112
+ publicAPI.stopXR = async () => {
113
+ if (navigator.xr === undefined) {
114
+ // WebXR polyfill not available so nothing to do
115
+ return;
116
+ }
117
+ if (model.xrSession !== null) {
118
+ model.xrSession.cancelAnimationFrame(model.xrSceneFrame);
119
+ model.renderWindow.getRenderable().getInteractor().returnFromXRAnimation();
120
+ const gl = model.renderWindow.get3DContext();
121
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
122
+ await model.xrSession.end().catch(error => {
123
+ if (!(error instanceof DOMException)) {
124
+ throw error;
125
+ }
126
+ });
127
+ model.xrSession = null;
128
+ }
129
+ if (model.initCanvasSize !== null) {
130
+ model.renderWindow.setSize(...model.initCanvasSize);
131
+ }
132
+
133
+ // Reset to default canvas
134
+ const ren = model.renderWindow.getRenderable().getRenderers()[0];
135
+ if (model.initBackground != null) {
136
+ ren.setBackground(model.initBackground);
137
+ model.initBackground = null;
138
+ }
139
+ ren.getActiveCamera().setProjectionMatrix(null);
140
+ ren.resetCamera();
141
+ ren.setViewport(0.0, 0, 1.0, 1.0);
142
+ model.renderWindow.traverseAllPasses();
143
+ publicAPI.modified();
144
+ };
145
+ model.xrRender = async (t, frame) => {
146
+ const xrSession = frame.session;
147
+ const isXrSessionHMD = [XrSessionTypes.HmdVR, XrSessionTypes.HmdAR].includes(model.xrSessionType);
148
+ model.renderWindow.getRenderable().getInteractor().updateXRGamepads(xrSession, frame, model.xrReferenceSpace);
149
+ model.xrSceneFrame = model.xrSession.requestAnimationFrame(model.xrRender);
150
+ const xrPose = frame.getViewerPose(model.xrReferenceSpace);
151
+ if (xrPose) {
152
+ const gl = model.renderWindow.get3DContext();
153
+ if (model.xrSessionType === XrSessionTypes.MobileAR && model.initCanvasSize !== null) {
154
+ gl.canvas.width = model.initCanvasSize[0];
155
+ gl.canvas.height = model.initCanvasSize[1];
156
+ }
157
+ const glLayer = xrSession.renderState.baseLayer;
158
+ gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
159
+ gl.clear(gl.COLOR_BUFFER_BIT);
160
+ gl.clear(gl.DEPTH_BUFFER_BIT);
161
+ model.renderWindow.setSize(glLayer.framebufferWidth, glLayer.framebufferHeight);
162
+
163
+ // get the first renderer
164
+ const ren = model.renderWindow.getRenderable().getRenderers()[0];
165
+
166
+ // Do a render pass for each eye
167
+ xrPose.views.forEach((view, index) => {
168
+ const viewport = glLayer.getViewport(view);
169
+ if (isXrSessionHMD) {
170
+ if (view.eye === 'left') {
171
+ ren.setViewport(0, 0, 0.5, 1.0);
172
+ } else if (view.eye === 'right') {
173
+ ren.setViewport(0.5, 0, 1.0, 1.0);
174
+ } else {
175
+ // No handling for non-eye viewport
176
+ return;
177
+ }
178
+ } else if (model.xrSessionType === XrSessionTypes.LookingGlassVR) {
179
+ const startX = viewport.x / glLayer.framebufferWidth;
180
+ const startY = viewport.y / glLayer.framebufferHeight;
181
+ const endX = (viewport.x + viewport.width) / glLayer.framebufferWidth;
182
+ const endY = (viewport.y + viewport.height) / glLayer.framebufferHeight;
183
+ ren.setViewport(startX, startY, endX, endY);
184
+ } else {
185
+ ren.setViewport(0, 0, 1, 1);
186
+ }
187
+ ren.getActiveCamera().computeViewParametersFromPhysicalMatrix(view.transform.inverse.matrix);
188
+ ren.getActiveCamera().setProjectionMatrix(view.projectionMatrix);
189
+ model.renderWindow.traverseAllPasses();
190
+ });
191
+
192
+ // Reset scissorbox before any subsequent rendering to external displays
193
+ // on frame end, such as rendering to a Looking Glass display.
194
+ gl.scissor(0, 0, glLayer.framebufferWidth, glLayer.framebufferHeight);
195
+ gl.disable(gl.SCISSOR_TEST);
196
+ }
197
+ };
198
+ publicAPI.delete = macro.chain(publicAPI.delete);
199
+ }
200
+
201
+ // ----------------------------------------------------------------------------
202
+ // Object factory
203
+ // ----------------------------------------------------------------------------
204
+
205
+ const DEFAULT_VALUES = {
206
+ initialized: false,
207
+ initCanvasSize: null,
208
+ initBackground: null,
209
+ renderWindow: null,
210
+ xrSession: null,
211
+ xrSessionType: 0,
212
+ xrReferenceSpace: null
213
+ };
214
+
215
+ // ----------------------------------------------------------------------------
216
+
217
+ function extend(publicAPI, model) {
218
+ let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
219
+ Object.assign(model, DEFAULT_VALUES, initialValues);
220
+
221
+ // Build VTK API
222
+ macro.obj(publicAPI, model);
223
+ macro.event(publicAPI, model, 'event');
224
+ macro.get(publicAPI, model, ['xrSession']);
225
+ macro.setGet(publicAPI, model, ['renderWindow']);
226
+
227
+ // Object methods
228
+ vtkWebXRRenderWindowHelper(publicAPI, model);
229
+ }
230
+
231
+ // ----------------------------------------------------------------------------
232
+
233
+ const newInstance = macro.newInstance(extend, 'vtkWebXRRenderWindowHelper');
234
+
235
+ // ----------------------------------------------------------------------------
236
+
237
+ var index = {
238
+ newInstance,
239
+ extend
240
+ };
241
+
242
+ export { index as default, extend, newInstance };
@@ -99,16 +99,6 @@ export interface vtkWidgetManager extends vtkObject {
99
99
  */
100
100
  getPickingEnabled(): boolean;
101
101
 
102
- /**
103
- * @deprecated
104
- */
105
- getUseSvgLayer(): boolean;
106
-
107
- /**
108
- * @deprecated
109
- */
110
- setUseSvgLayer(use: boolean): boolean;
111
-
112
102
  /**
113
103
  * Enable the picking.
114
104
  */
@@ -165,16 +155,6 @@ export interface vtkWidgetManager extends vtkObject {
165
155
  */
166
156
  getSelectedDataForXY(x: number, y: number): Promise<ISelectedData>;
167
157
 
168
- /**
169
- * @deprecated
170
- */
171
- updateSelectionFromXY(x: number, y: number): void;
172
-
173
- /**
174
- * @deprecated
175
- */
176
- updateSelectionFromMouseEvent(event: MouseEvent): void;
177
-
178
158
  /**
179
159
  * The all currently selected data.
180
160
  */
@@ -197,10 +177,6 @@ export interface IWidgetManagerInitialValues {
197
177
  captureOn?: CaptureOn;
198
178
  viewType?: ViewTypes;
199
179
  pickingEnabled?: boolean;
200
- /**
201
- * @deprecated
202
- */
203
- useSvgLayer?: boolean;
204
180
  }
205
181
 
206
182
  /**