@kitware/vtk.js 29.11.2 → 30.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.
@@ -1,8 +1,8 @@
1
1
  import { m as macro } from '../../macros2.js';
2
2
  import vtkAbstractPicker from './AbstractPicker.js';
3
3
  import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
4
- import { d as dot, l as normalize, n as norm, e as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
5
- import { mat4, vec4 } from 'gl-matrix';
4
+ import { d as dot, l as normalize, s as subtract, e as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
5
+ import { vec3, mat4, vec4 } from 'gl-matrix';
6
6
 
7
7
  const {
8
8
  vtkErrorMacro
@@ -62,17 +62,114 @@ function vtkPicker(publicAPI, model) {
62
62
  return Math.sqrt(tolerance);
63
63
  }
64
64
 
65
+ /**
66
+ * Perform picking on the given renderer, given a ray defined in world coordinates.
67
+ * @param {*} renderer
68
+ * @param {*} tolerance
69
+ * @param {*} p1World
70
+ * @param {*} p2World
71
+ * @returns true if we picked something else false
72
+ */
73
+ function pick3DInternal(renderer, tolerance, p1World, p2World) {
74
+ const p1Mapper = new Float64Array(4);
75
+ const p2Mapper = new Float64Array(4);
76
+ const ray = [];
77
+ const hitPosition = [];
78
+ const props = model.pickFromList ? model.pickList : renderer.getActors();
79
+
80
+ // pre-allocate some arrays.
81
+ const transformScale = new Float64Array(3);
82
+ const pickedPosition = new Float64Array(3);
83
+
84
+ // Loop over props.
85
+ // Transform ray (defined from position of camera to selection point) into coordinates of mapper (not
86
+ // transformed to actors coordinates! Reduces overall computation!!!).
87
+ // Note that only vtkProp3D's can be picked by vtkPicker.
88
+ props.forEach(prop => {
89
+ const mapper = prop.getMapper();
90
+ const propIsNotFullyTranslucent = prop.getProperty?.()?.getOpacity() > 0.0;
91
+ const pickable = prop.getNestedPickable() && prop.getNestedVisibility() && propIsNotFullyTranslucent;
92
+ if (!pickable) {
93
+ // prop cannot be picked
94
+ return;
95
+ }
96
+
97
+ // The prop is candidate for picking:
98
+ // - get its composite matrix and invert it
99
+ // - use the inverted matrix to transform the ray points into mapper coordinates
100
+ model.transformMatrix = prop.getMatrix().slice(0);
101
+ mat4.transpose(model.transformMatrix, model.transformMatrix);
102
+ mat4.invert(model.transformMatrix, model.transformMatrix);
103
+ vec4.transformMat4(p1Mapper, p1World, model.transformMatrix);
104
+ vec4.transformMat4(p2Mapper, p2World, model.transformMatrix);
105
+ vec3.scale(p1Mapper, p1Mapper, 1 / p1Mapper[3]);
106
+ vec3.scale(p2Mapper, p2Mapper, 1 / p2Mapper[3]);
107
+ subtract(p2Mapper, p1Mapper, ray);
108
+
109
+ // We now have the ray endpoints in mapper coordinates.
110
+ // Compare it with the mapper bounds to check if intersection is possible.
111
+
112
+ // Get the bounding box of the mapper.
113
+ // Note that the tolerance is added to the bounding box to make sure things on the edge of the
114
+ // bounding box are picked correctly.
115
+ const bounds = mapper ? vtkBoundingBox.inflate(mapper.getBounds(), tolerance) : [...vtkBoundingBox.INIT_BOUNDS];
116
+ if (vtkBoundingBox.intersectBox(bounds, p1Mapper, ray, hitPosition, [])) {
117
+ mat4.getScaling(transformScale, model.transformMatrix);
118
+ const t = model.intersectWithLine(p1Mapper, p2Mapper, tolerance * 0.333 * (transformScale[0] + transformScale[1] + transformScale[2]), prop, mapper);
119
+ if (t < Number.MAX_VALUE) {
120
+ pickedPosition[0] = (1.0 - t) * p1World[0] + t * p2World[0];
121
+ pickedPosition[1] = (1.0 - t) * p1World[1] + t * p2World[1];
122
+ pickedPosition[2] = (1.0 - t) * p1World[2] + t * p2World[2];
123
+ const actorIndex = model.actors.indexOf(prop);
124
+ if (actorIndex !== -1) {
125
+ // If already in list, compare the previous picked position with the new one.
126
+ // Store the new one if it is closer from the ray endpoint.
127
+ const previousPickedPosition = model.pickedPositions[actorIndex];
128
+ if (distance2BetweenPoints(p1World, pickedPosition) < distance2BetweenPoints(p1World, previousPickedPosition)) {
129
+ model.pickedPositions[actorIndex] = pickedPosition.slice(0);
130
+ }
131
+ } else {
132
+ model.actors.push(prop);
133
+ model.pickedPositions.push(pickedPosition.slice(0));
134
+ }
135
+ }
136
+ }
137
+ });
138
+
139
+ // sort array by distance
140
+ const tempArray = [];
141
+ for (let i = 0; i < model.pickedPositions.length; i++) {
142
+ tempArray.push({
143
+ actor: model.actors[i],
144
+ pickedPosition: model.pickedPositions[i],
145
+ distance2: distance2BetweenPoints(p1World, model.pickedPositions[i])
146
+ });
147
+ }
148
+ tempArray.sort((a, b) => {
149
+ const keyA = a.distance2;
150
+ const keyB = b.distance2;
151
+ // order the actors based on the distance2 attribute, so the near actors comes
152
+ // first in the list
153
+ if (keyA < keyB) return -1;
154
+ if (keyA > keyB) return 1;
155
+ return 0;
156
+ });
157
+ model.pickedPositions = [];
158
+ model.actors = [];
159
+ tempArray.forEach(obj => {
160
+ model.pickedPositions.push(obj.pickedPosition);
161
+ model.actors.push(obj.actor);
162
+ });
163
+ }
164
+
65
165
  // Intersect data with specified ray.
66
166
  // Project the center point of the mapper onto the ray and determine its parametric value
67
- publicAPI.intersectWithLine = (p1, p2, tol, mapper) => {
167
+ model.intersectWithLine = (p1, p2, tolerance, prop, mapper) => {
68
168
  if (!mapper) {
69
169
  return Number.MAX_VALUE;
70
170
  }
71
171
  const center = mapper.getCenter();
72
- const ray = [];
73
- for (let i = 0; i < 3; i++) {
74
- ray[i] = p2[i] - p1[i];
75
- }
172
+ const ray = vec3.subtract(new Float64Array(3), p2, p1);
76
173
  const rayFactor = dot(ray, ray);
77
174
  if (rayFactor === 0.0) {
78
175
  return 2.0;
@@ -86,55 +183,43 @@ function vtkPicker(publicAPI, model) {
86
183
  // To be overridden in subclasses
87
184
  publicAPI.pick = (selection, renderer) => {
88
185
  if (selection.length !== 3) {
89
- vtkWarningMacro('vtkPicker::pick: selectionPt needs three components');
186
+ vtkWarningMacro('vtkPicker.pick - selection needs three components');
90
187
  }
188
+ if (!renderer) {
189
+ vtkErrorMacro('vtkPicker.pick - renderer cannot be null');
190
+ throw new Error('renderer cannot be null');
191
+ }
192
+ initialize();
91
193
  const selectionX = selection[0];
92
194
  const selectionY = selection[1];
93
195
  let selectionZ = selection[2];
94
- let cameraPos = [];
95
- let cameraFP = [];
96
- let displayCoords = [];
97
- let worldCoords = [];
98
- const ray = [];
99
- const cameraDOP = [];
100
- let clipRange = [];
101
- let tF;
102
- let tB;
103
- const p1World = [];
104
- const p2World = [];
105
- let props = [];
106
- let pickable = false;
107
- const p1Mapper = new Float64Array(4);
108
- const p2Mapper = new Float64Array(4);
109
- const bbox = vtkBoundingBox.newInstance();
110
- const t = [];
111
- const hitPosition = [];
112
- const view = renderer.getRenderWindow().getViews()[0];
113
- initialize();
114
196
  model.renderer = renderer;
115
197
  model.selectionPoint[0] = selectionX;
116
198
  model.selectionPoint[1] = selectionY;
117
199
  model.selectionPoint[2] = selectionZ;
118
- if (!renderer) {
119
- vtkErrorMacro('Picker::Pick Must specify renderer');
120
- return;
121
- }
200
+ const p1World = new Float64Array(4);
201
+ const p2World = new Float64Array(4);
122
202
 
123
203
  // Get camera focal point and position. Convert to display (screen)
124
204
  // coordinates. We need a depth value for z-buffer.
125
205
  const camera = renderer.getActiveCamera();
126
- cameraPos = camera.getPosition();
127
- cameraFP = camera.getFocalPoint();
206
+ const cameraPos = camera.getPosition();
207
+ const cameraFP = camera.getFocalPoint();
208
+ const view = renderer.getRenderWindow().getViews()[0];
128
209
  const dims = view.getViewportSize(renderer);
210
+ if (dims[1] === 0) {
211
+ vtkWarningMacro('vtkPicker.pick - viewport area is 0');
212
+ return;
213
+ }
129
214
  const aspect = dims[0] / dims[1];
215
+ let displayCoords = [];
130
216
  displayCoords = renderer.worldToNormalizedDisplay(cameraFP[0], cameraFP[1], cameraFP[2], aspect);
131
217
  displayCoords = view.normalizedDisplayToDisplay(displayCoords[0], displayCoords[1], displayCoords[2]);
132
218
  selectionZ = displayCoords[2];
133
- const tolerance = computeTolerance(selectionZ, aspect, renderer) * model.tolerance;
134
219
 
135
220
  // Convert the selection point into world coordinates.
136
221
  const normalizedDisplay = view.displayToNormalizedDisplay(selectionX, selectionY, selectionZ);
137
- worldCoords = renderer.normalizedDisplayToWorld(normalizedDisplay[0], normalizedDisplay[1], normalizedDisplay[2], aspect);
222
+ const worldCoords = renderer.normalizedDisplayToWorld(normalizedDisplay[0], normalizedDisplay[1], normalizedDisplay[2], aspect);
138
223
  for (let i = 0; i < 3; i++) {
139
224
  model.pickPosition[i] = worldCoords[i];
140
225
  }
@@ -143,9 +228,11 @@ function vtkPicker(publicAPI, model) {
143
228
  // the camera position to the selection point, starting where this line
144
229
  // intersects the front clipping plane, and terminating where this
145
230
  // line intersects the back clipping plane.
231
+ const ray = [];
146
232
  for (let i = 0; i < 3; i++) {
147
233
  ray[i] = model.pickPosition[i] - cameraPos[i];
148
234
  }
235
+ const cameraDOP = [];
149
236
  for (let i = 0; i < 3; i++) {
150
237
  cameraDOP[i] = cameraFP[i] - cameraPos[i];
151
238
  }
@@ -155,7 +242,9 @@ function vtkPicker(publicAPI, model) {
155
242
  vtkWarningMacro('Picker::Pick Cannot process points');
156
243
  return;
157
244
  }
158
- clipRange = camera.getClippingRange();
245
+ const clipRange = camera.getClippingRange();
246
+ let tF;
247
+ let tB;
159
248
  if (camera.getParallelProjection()) {
160
249
  tF = clipRange[0] - rayLength;
161
250
  tB = clipRange[1] - rayLength;
@@ -173,105 +262,25 @@ function vtkPicker(publicAPI, model) {
173
262
  }
174
263
  p1World[3] = 1.0;
175
264
  p2World[3] = 1.0;
176
- if (model.pickFromList) {
177
- props = model.pickList;
178
- } else {
179
- props = renderer.getActors();
265
+ const tolerance = computeTolerance(selectionZ, aspect, renderer) * model.tolerance;
266
+ pick3DInternal(model.renderer, tolerance, p1World, p2World);
267
+ };
268
+ publicAPI.pick3DPoint = (selectionPoint, focalPoint, renderer) => {
269
+ if (!renderer) {
270
+ throw new Error('renderer cannot be null');
180
271
  }
181
- const scale = [];
182
- props.forEach(prop => {
183
- const mapper = prop.getMapper();
184
- pickable = prop.getNestedPickable() && prop.getNestedVisibility();
185
- if (prop.getProperty().getOpacity?.() <= 0.0) {
186
- pickable = false;
187
- }
188
- if (pickable) {
189
- model.transformMatrix = prop.getMatrix().slice(0);
190
- // Webgl need a transpose matrix but we need the untransposed one to project world points
191
- // into the right referential
192
- mat4.transpose(model.transformMatrix, model.transformMatrix);
193
- mat4.invert(model.transformMatrix, model.transformMatrix);
194
- // Extract scale
195
- const col1 = [model.transformMatrix[0], model.transformMatrix[1], model.transformMatrix[2]];
196
- const col2 = [model.transformMatrix[4], model.transformMatrix[5], model.transformMatrix[6]];
197
- const col3 = [model.transformMatrix[8], model.transformMatrix[9], model.transformMatrix[10]];
198
- scale[0] = norm(col1);
199
- scale[1] = norm(col2);
200
- scale[2] = norm(col3);
201
- vec4.transformMat4(p1Mapper, p1World, model.transformMatrix);
202
- vec4.transformMat4(p2Mapper, p2World, model.transformMatrix);
203
- p1Mapper[0] /= p1Mapper[3];
204
- p1Mapper[1] /= p1Mapper[3];
205
- p1Mapper[2] /= p1Mapper[3];
206
- p2Mapper[0] /= p2Mapper[3];
207
- p2Mapper[1] /= p2Mapper[3];
208
- p2Mapper[2] /= p2Mapper[3];
209
- for (let i = 0; i < 3; i++) {
210
- ray[i] = p2Mapper[i] - p1Mapper[i];
211
- }
212
- if (mapper) {
213
- bbox.setBounds(mapper.getBounds());
214
- bbox.inflate(tolerance);
215
- } else {
216
- bbox.reset();
217
- }
218
- if (bbox.intersectBox(p1Mapper, ray, hitPosition, t)) {
219
- t[0] = publicAPI.intersectWithLine(p1Mapper, p2Mapper, tolerance * 0.333 * (scale[0] + scale[1] + scale[2]), prop, mapper);
220
- if (t[0] < Number.MAX_VALUE) {
221
- const p = [];
222
- p[0] = (1.0 - t[0]) * p1World[0] + t[0] * p2World[0];
223
- p[1] = (1.0 - t[0]) * p1World[1] + t[0] * p2World[1];
224
- p[2] = (1.0 - t[0]) * p1World[2] + t[0] * p2World[2];
225
-
226
- // Check if the current actor is already in the list
227
- let actorID = -1;
228
- for (let i = 0; i < model.actors.length; i++) {
229
- if (model.actors[i] === prop) {
230
- actorID = i;
231
- break;
232
- }
233
- }
234
- if (actorID === -1) {
235
- model.actors.push(prop);
236
- model.pickedPositions.push(p);
237
- } else {
238
- const oldPoint = model.pickedPositions[actorID];
239
- const distOld = distance2BetweenPoints(p1World, oldPoint);
240
- const distCurrent = distance2BetweenPoints(p1World, p);
241
- if (distCurrent < distOld) {
242
- model.pickedPositions[actorID] = p;
243
- }
244
- }
245
- }
246
- }
247
- }
248
- publicAPI.invokePickChange(model.pickedPositions);
249
- return 1;
250
- });
251
- // sort array by distance
252
- const tempArray = [];
253
- for (let i = 0; i < model.pickedPositions.length; i++) {
254
- tempArray.push({
255
- actor: model.actors[i],
256
- pickedPosition: model.pickedPositions[i],
257
- distance2: distance2BetweenPoints(p1World, model.pickedPositions[i])
258
- });
272
+ initialize();
273
+ model.renderer = renderer;
274
+ vec3.copy(model.selectionPoint, selectionPoint);
275
+ const view = renderer.getRenderWindow().getViews()[0];
276
+ const dims = view.getViewportSize(renderer);
277
+ if (dims[1] === 0) {
278
+ vtkWarningMacro('vtkPicker.pick3DPoint - viewport area is 0');
279
+ return;
259
280
  }
260
- tempArray.sort((a, b) => {
261
- const keyA = a.distance2;
262
- const keyB = b.distance2;
263
- // order the actors based on the distance2 attribute, so the near actors comes
264
- // first in the list
265
- if (keyA < keyB) return -1;
266
- if (keyA > keyB) return 1;
267
- return 0;
268
- });
269
- model.pickedPositions = [];
270
- model.actors = [];
271
- tempArray.forEach(obj => {
272
- model.pickedPositions.push(obj.pickedPosition);
273
- model.actors.push(obj.actor);
274
- });
281
+ const aspect = dims[0] / dims[1];
282
+ const tolerance = computeTolerance(model.selectionPoint[2], aspect, renderer) * model.tolerance;
283
+ pick3DInternal(renderer, tolerance, selectionPoint, focalPoint);
275
284
  };
276
285
  }
277
286
 
@@ -1,6 +1,8 @@
1
1
  import vtkPicker, { IPickerInitialValues } from './Picker';
2
2
  import vtkMapper from './Mapper';
3
3
  import { Vector3 } from './../../types';
4
+ import vtkProp3D from './Prop3D';
5
+ import { Nullable } from './../../types';
4
6
 
5
7
  interface IPointPickerInitialValues extends IPickerInitialValues {
6
8
  pointId?: number;
@@ -31,24 +33,6 @@ export interface vtkPointPicker extends vtkPicker {
31
33
  */
32
34
  getUseCells(): boolean;
33
35
 
34
- /**
35
- *
36
- * @param {Vector3} p1
37
- * @param {Vector3} p2
38
- * @param {Number} tol
39
- * @param {vtkMapper} mapper
40
- */
41
- intersectWithLine(p1: Vector3, p2: Vector3, tol: number, mapper: vtkMapper): number;
42
-
43
- /**
44
- *
45
- * @param {Vector3} p1
46
- * @param {Vector3} p2
47
- * @param {Number} tol
48
- * @param {vtkMapper} mapper
49
- */
50
- intersectActorWithLine(p1: Vector3, p2: Vector3, tol: number, mapper: vtkMapper): number;
51
-
52
36
  /**
53
37
  * Specify whether the point search should be based on cell points or directly on the point list.
54
38
  * @param useCells
@@ -13,7 +13,7 @@ const {
13
13
  function vtkPointPicker(publicAPI, model) {
14
14
  // Set our className
15
15
  model.classHierarchy.push('vtkPointPicker');
16
- publicAPI.intersectWithLine = (p1, p2, tol, actor, mapper) => {
16
+ model.intersectWithLine = (p1, p2, tolerance, prop, mapper) => {
17
17
  let tMin = Number.MAX_VALUE;
18
18
  if (mapper.isA('vtkImageMapper') || mapper.isA('vtkImageArrayMapper')) {
19
19
  const pickData = mapper.intersectWithLineForPointPicking(p1, p2);
@@ -22,11 +22,11 @@ function vtkPointPicker(publicAPI, model) {
22
22
  model.pointIJK = pickData.ijk;
23
23
  }
24
24
  } else if (mapper.isA('vtkMapper')) {
25
- tMin = publicAPI.intersectActorWithLine(p1, p2, tol, mapper);
25
+ tMin = model.intersectActorWithLine(p1, p2, tolerance, mapper);
26
26
  }
27
27
  return tMin;
28
28
  };
29
- publicAPI.intersectActorWithLine = (p1, p2, tol, mapper) => {
29
+ model.intersectActorWithLine = (p1, p2, tolerance, mapper) => {
30
30
  // Get dataset
31
31
  const input = mapper.getInputData();
32
32
 
@@ -77,7 +77,7 @@ function vtkPointPicker(publicAPI, model) {
77
77
  maxDist = dist;
78
78
  }
79
79
  } // end for i
80
- if (maxDist <= tol && maxDist < minPtDist) {
80
+ if (maxDist <= tolerance && maxDist < minPtDist) {
81
81
  // within tolerance
82
82
  minPtId = ptId;
83
83
  minPtDist = maxDist;
@@ -105,7 +105,7 @@ function vtkPointPicker(publicAPI, model) {
105
105
  maxDist = dist;
106
106
  }
107
107
  } // end for i
108
- if (maxDist <= tol && maxDist < minPtDist) {
108
+ if (maxDist <= tolerance && maxDist < minPtDist) {
109
109
  // within tolerance
110
110
  minPtId = ptId;
111
111
  minPtDist = maxDist;
@@ -1,4 +1,4 @@
1
- import { mat4 } from "gl-matrix";
1
+ import { mat4, quat } from "gl-matrix";
2
2
  import { Bounds, Vector3, Range } from './../../types';
3
3
  import vtkProp, { IPropInitialValues } from './Prop';
4
4
 
@@ -88,6 +88,13 @@ export interface vtkProp3D extends vtkProp {
88
88
  */
89
89
  getOrientationWXYZ(): number[];
90
90
 
91
+ /**
92
+ * Get the orientation quaternion of the Prop3D.
93
+ * out is optional and will be created if not supplied.
94
+ * @param {quat | undefined} out
95
+ */
96
+ getOrientationQuaternion(out?: quat): quat;
97
+
91
98
  /**
92
99
  * Get a reference to the Prop3D’s 4x4 composite matrix.
93
100
  * Get the matrix from the position, origin, scale and orientation This
@@ -167,6 +174,14 @@ export interface vtkProp3D extends vtkProp {
167
174
  */
168
175
  rotateWXYZ(degrees: number, x: number, y: number, z: number): void;
169
176
 
177
+ /**
178
+ * Rotate the Prop3D by the provided orientation quaternion.
179
+ * If the provided quaternion is identity (~epsilon), this function does nothing.
180
+ * The quaternion should follow the gl-matrix convention: [x,y,z,w]
181
+ * @param {quat} orientationQuaternion The quaternion to rotate the prop by.
182
+ */
183
+ rotateQuaternion(orientationQuaternion: quat): void;
184
+
170
185
  /**
171
186
  * Orientation is specified as X, Y and Z rotations in that order,
172
187
  * but they are performed as RotateZ, RotateX, and finally RotateY.
@@ -4,6 +4,8 @@ import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
4
4
  import { A as degreesFromRadians, r as radiansFromDegrees, a as areMatricesEqual } from '../../Common/Core/Math/index.js';
5
5
  import vtkProp from './Prop.js';
6
6
 
7
+ const VTK_EPSILON = 1e-6;
8
+
7
9
  // ----------------------------------------------------------------------------
8
10
  // vtkProp3D methods
9
11
  // ----------------------------------------------------------------------------
@@ -22,6 +24,10 @@ function vtkProp3D(publicAPI, model) {
22
24
  const w = quat.getAxisAngle(oaxis, q);
23
25
  return [degreesFromRadians(w), oaxis[0], oaxis[1], oaxis[2]];
24
26
  };
27
+ publicAPI.getOrientationQuaternion = function () {
28
+ let out = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
29
+ return mat4.getRotation(out, model.rotation);
30
+ };
25
31
  publicAPI.rotateX = val => {
26
32
  if (val === 0.0) {
27
33
  return;
@@ -57,6 +63,14 @@ function vtkProp3D(publicAPI, model) {
57
63
  mat4.multiply(model.rotation, model.rotation, quatMat);
58
64
  publicAPI.modified();
59
65
  };
66
+ publicAPI.rotateQuaternion = orientationQuaternion => {
67
+ if (Math.abs(orientationQuaternion[3]) >= 1 - VTK_EPSILON) {
68
+ return;
69
+ }
70
+ const oriQuatMat = mat4.fromQuat(new Float64Array(16), orientationQuaternion);
71
+ mat4.multiply(model.rotation, model.rotation, oriQuatMat);
72
+ publicAPI.modified();
73
+ };
60
74
  publicAPI.setOrientation = (x, y, z) => {
61
75
  if (x === model.orientation[0] && y === model.orientation[1] && z === model.orientation[2]) {
62
76
  return false;
@@ -81,6 +81,20 @@ export interface IRenderWindowInteractorEvent {
81
81
  type: InteractorEventType;
82
82
  }
83
83
 
84
+ export interface I3DEvent {
85
+ gamepad: Gamepad;
86
+ position: DOMPointReadOnly;
87
+ orientation: DOMPointReadOnly;
88
+ targetPosition: DOMPointReadOnly;
89
+ targetOrientation: DOMPointReadOnly;
90
+ device: Device;
91
+ }
92
+
93
+ export interface IButton3DEvent extends I3DEvent {
94
+ pressed: boolean;
95
+ input: Input;
96
+ }
97
+
84
98
  export interface vtkRenderWindowInteractor extends vtkObject {
85
99
 
86
100
  /**
@@ -678,10 +692,11 @@ export interface vtkRenderWindowInteractor extends vtkObject {
678
692
  animationEvent(args: any): any;
679
693
 
680
694
  /**
681
- *
695
+ * Triggers the 'Button3D' event.
696
+ *
682
697
  * @param args
683
698
  */
684
- button3DEvent(args: any): any;
699
+ button3DEvent(eventPayload: IButton3DEvent): void;
685
700
 
686
701
  /**
687
702
  *
@@ -804,10 +819,11 @@ export interface vtkRenderWindowInteractor extends vtkObject {
804
819
  mouseWheelEvent(args: any): any;
805
820
 
806
821
  /**
807
- *
808
- * @param args
822
+ * Triggers the 'Move3D' event.
823
+ *
824
+ * @param eventPayload
809
825
  */
810
- move3DEvent(args: any): any;
826
+ move3DEvent(eventPayload: I3DEvent): void;
811
827
 
812
828
  /**
813
829
  *
@@ -481,45 +481,51 @@ function vtkRenderWindowInteractor(publicAPI, model) {
481
481
  // watch for when buttons change state and fire events
482
482
  xrSession.inputSources.forEach(inputSource => {
483
483
  const gripPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.gripSpace, xrRefSpace);
484
- const gp = inputSource.gamepad;
484
+ const targetRayPose = inputSource.gripSpace == null ? null : xrFrame.getPose(inputSource.targetRaySpace, xrRefSpace);
485
+ const gamepad = inputSource.gamepad;
485
486
  const hand = inputSource.handedness;
486
- if (gp) {
487
- if (!(gp.index in model.lastGamepadValues)) {
488
- model.lastGamepadValues[gp.index] = {
489
- left: {
490
- buttons: {}
491
- },
492
- right: {
493
- buttons: {}
494
- },
495
- none: {
496
- buttons: {}
497
- }
498
- };
499
- }
500
- for (let b = 0; b < gp.buttons.length; ++b) {
501
- if (!(b in model.lastGamepadValues[gp.index][hand].buttons)) {
502
- model.lastGamepadValues[gp.index][hand].buttons[b] = false;
503
- }
504
- if (model.lastGamepadValues[gp.index][hand].buttons[b] !== gp.buttons[b].pressed && gripPose != null) {
505
- publicAPI.button3DEvent({
506
- gamepad: gp,
507
- position: gripPose.transform.position,
508
- orientation: gripPose.transform.orientation,
509
- pressed: gp.buttons[b].pressed,
510
- device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController,
511
- input: deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping][b] ? deviceInputMap[gp.mapping][b] : Input.Trigger
512
- });
513
- model.lastGamepadValues[gp.index][hand].buttons[b] = gp.buttons[b].pressed;
514
- }
515
- if (model.lastGamepadValues[gp.index][hand].buttons[b] && gripPose != null) {
516
- publicAPI.move3DEvent({
517
- gamepad: gp,
518
- position: gripPose.transform.position,
519
- orientation: gripPose.transform.orientation,
520
- device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController
521
- });
487
+ if (!gamepad) {
488
+ return;
489
+ }
490
+ if (!(gamepad.index in model.lastGamepadValues)) {
491
+ model.lastGamepadValues[gamepad.index] = {
492
+ left: {
493
+ buttons: {}
494
+ },
495
+ right: {
496
+ buttons: {}
497
+ },
498
+ none: {
499
+ buttons: {}
522
500
  }
501
+ };
502
+ }
503
+ for (let buttonIdx = 0; buttonIdx < gamepad.buttons.length; ++buttonIdx) {
504
+ if (!(buttonIdx in model.lastGamepadValues[gamepad.index][hand].buttons)) {
505
+ model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] = false;
506
+ }
507
+ if (model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] !== gamepad.buttons[buttonIdx].pressed && gripPose != null) {
508
+ publicAPI.button3DEvent({
509
+ gamepad,
510
+ position: gripPose.transform.position,
511
+ orientation: gripPose.transform.orientation,
512
+ targetPosition: targetRayPose.transform.position,
513
+ targetOrientation: targetRayPose.transform.orientation,
514
+ pressed: gamepad.buttons[buttonIdx].pressed,
515
+ device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController,
516
+ input: deviceInputMap[gamepad.mapping] && deviceInputMap[gamepad.mapping][buttonIdx] ? deviceInputMap[gamepad.mapping][buttonIdx] : Input.Trigger
517
+ });
518
+ model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] = gamepad.buttons[buttonIdx].pressed;
519
+ }
520
+ if (model.lastGamepadValues[gamepad.index][hand].buttons[buttonIdx] && gripPose != null) {
521
+ publicAPI.move3DEvent({
522
+ gamepad,
523
+ position: gripPose.transform.position,
524
+ orientation: gripPose.transform.orientation,
525
+ targetPosition: targetRayPose.transform.position,
526
+ targetOrientation: targetRayPose.transform.orientation,
527
+ device: inputSource.handedness === 'left' ? Device.LeftController : Device.RightController
528
+ });
523
529
  }
524
530
  }
525
531
  });
@@ -1,12 +1,12 @@
1
1
  import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction';
2
2
  import { Bounds, Range } from './../../types';
3
- import vtkAbstractMapper, { IAbstractMapperInitialValues } from './AbstractMapper';
3
+ import vtkAbstractMapper3D, { IAbstractMapper3DInitialValues } from './AbstractMapper3D';
4
4
  import { BlendMode, FilterMode } from './VolumeMapper/Constants';
5
5
 
6
6
  /**
7
7
  *
8
8
  */
9
- export interface IVolumeMapperInitialValues extends IAbstractMapperInitialValues {
9
+ export interface IVolumeMapperInitialValues extends IAbstractMapper3DInitialValues {
10
10
  anisotropy?: number;
11
11
  autoAdjustSampleDistances?: boolean;
12
12
  averageIPScalarRange?: Range;
@@ -23,7 +23,7 @@ export interface IVolumeMapperInitialValues extends IAbstractMapperInitialValues
23
23
  LAOKernelSize?: number;
24
24
  }
25
25
 
26
- export interface vtkVolumeMapper extends vtkAbstractMapper {
26
+ export interface vtkVolumeMapper extends vtkAbstractMapper3D {
27
27
 
28
28
  /**
29
29
  * Get the bounds for this mapper as [xmin, xmax, ymin, ymax,zmin, zmax].