@kitware/vtk.js 29.11.1 → 30.0.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,3 +1,8 @@
1
+ ## From 29.x to 30
2
+
3
+ - **ResliceCursorWidget.interactionEvent**: no longer pass an object of computeFocalPointOffset, canUpdateFocalPoint but simply the type of the handle that triggers the event. Those values can easily be recomputed by the consumers of the event. Regarding `computeFocalPointOffset`, it is no longer adviced to compute focal point offset for each interaction, instead observing `startInteraction()` should be considered (see ResliceCursorWidget example).
4
+ - **ResliceCursorWidget.invokeInternalInteractionEvent(methodName)**: has been removed and should be replaced by `ResliceCursorWidget.invokeInteractionEvent(methodName)`.
5
+
1
6
  ## From 28.x to 29
2
7
 
3
8
  - **getOpenGLRenderWindow**: `getOpenGLRenderWindow` has been renamed to `getApiSpecificRenderWindow` in `vtkFullScreenRenderWindow`, `vtkGenericRenderWindow` and `vtkViewProxy` to support WebGL and WebGPU backend. ([#2816](https://github.com/Kitware/vtk-js/pull/2816))
@@ -2,7 +2,9 @@ import vtkCell from './../../Common/DataModel/Cell';
2
2
  import { Vector3 } from './../../types';
3
3
  import vtkMapper from './Mapper';
4
4
  import vtkPicker, { IPickerInitialValues } from './Picker';
5
+ import vtkProp3D from './Prop3D';
5
6
  import vtkRenderer from './Renderer';
7
+ import { Nullable } from './../../types';
6
8
 
7
9
  /**
8
10
  *
@@ -83,26 +85,6 @@ export interface vtkCellPicker extends vtkPicker {
83
85
  * @param {vtkRenderer} renderer The vtkRenderer instance.
84
86
  */
85
87
  pick(selection: any, renderer: vtkRenderer): void;
86
-
87
- /**
88
- *
89
- * @param {Vector3} p1
90
- * @param {Vector3} p2
91
- * @param {Number} tol
92
- * @param {vtkMapper} mapper The vtkMapper instance.
93
- */
94
- intersectWithLine(p1: Vector3, p2: Vector3, tol: number, mapper: vtkMapper): number;
95
-
96
- /**
97
- *
98
- * @param {Vector3} p1
99
- * @param {Vector3} p2
100
- * @param {Number} t1
101
- * @param {Number} t2
102
- * @param {Number} tol
103
- * @param {vtkMapper} mapper The vtkMapper instance.
104
- */
105
- intersectActorWithLine(p1: Vector3, p2: Vector3, t1: number, t2: number, tol: number, mapper: vtkMapper): number;
106
88
  }
107
89
 
108
90
  /**
@@ -158,7 +158,7 @@ function vtkCellPicker(publicAPI, model) {
158
158
  }
159
159
  return pickResult;
160
160
  };
161
- publicAPI.intersectWithLine = (p1, p2, tol, actor, mapper) => {
161
+ model.intersectWithLine = (p1, p2, tolerance, prop, mapper) => {
162
162
  let tMin = Number.MAX_VALUE;
163
163
  let t1 = 0.0;
164
164
  let t2 = 1.0;
@@ -177,13 +177,13 @@ function vtkCellPicker(publicAPI, model) {
177
177
  } else if (mapper.isA('vtkVolumeMapper')) {
178
178
  // we calculate here the parametric intercept points between the ray and the bounding box, so
179
179
  // if the application defines for some reason a too large ray length (1e6), it restrict the calculation
180
- // to the vtkVolume actor bounding box
180
+ // to the vtkVolume prop bounding box
181
181
  const interceptionObject = vtkBox.intersectWithLine(mapper.getBounds(), p1, p2);
182
182
  t1 = interceptionObject?.t1 > clipLine.t1 ? interceptionObject.t1 : clipLine.t1;
183
183
  t2 = interceptionObject?.t2 < clipLine.t2 ? interceptionObject.t2 : clipLine.t2;
184
- tMin = publicAPI.intersectVolumeWithLine(p1, p2, t1, t2, tol, actor);
184
+ tMin = model.intersectVolumeWithLine(p1, p2, t1, t2, tolerance, prop);
185
185
  } else if (mapper.isA('vtkMapper')) {
186
- tMin = publicAPI.intersectActorWithLine(p1, p2, t1, t2, tol, mapper);
186
+ tMin = model.intersectActorWithLine(p1, p2, t1, t2, tolerance, mapper);
187
187
  }
188
188
  if (tMin < model.globalTMin) {
189
189
  model.globalTMin = tMin;
@@ -208,7 +208,7 @@ function vtkCellPicker(publicAPI, model) {
208
208
  }
209
209
  return tMin;
210
210
  };
211
- publicAPI.intersectVolumeWithLine = (p1, p2, t1, t2, tol, volume) => {
211
+ model.intersectVolumeWithLine = (p1, p2, t1, t2, tolerance, volume) => {
212
212
  let tMin = Number.MAX_VALUE;
213
213
  const mapper = volume.getMapper();
214
214
  const imageData = mapper.getInputData();
@@ -315,7 +315,7 @@ function vtkCellPicker(publicAPI, model) {
315
315
  }
316
316
  return tMin;
317
317
  };
318
- publicAPI.intersectActorWithLine = (p1, p2, t1, t2, tol, mapper) => {
318
+ model.intersectActorWithLine = (p1, p2, t1, t2, tolerance, mapper) => {
319
319
  let tMin = Number.MAX_VALUE;
320
320
  const minXYZ = [0, 0, 0];
321
321
  let pDistMin = Number.MAX_VALUE;
@@ -368,9 +368,9 @@ function vtkCellPicker(publicAPI, model) {
368
368
  let cellPicked;
369
369
  {
370
370
  if (vtkCellTypes.hasSubCells(minCellType)) {
371
- cellPicked = cell.intersectWithLine(t1, t2, p1, p2, tol, x, pCoords);
371
+ cellPicked = cell.intersectWithLine(t1, t2, p1, p2, tolerance, x, pCoords);
372
372
  } else {
373
- cellPicked = cell.intersectWithLine(p1, p2, tol, x, pCoords);
373
+ cellPicked = cell.intersectWithLine(p1, p2, tolerance, x, pCoords);
374
374
  }
375
375
  }
376
376
  if (cellPicked.intersect === 1 && cellPicked.t <= tMin + model.tolerance && cellPicked.t >= t1 && cellPicked.t <= t2) {
@@ -2,6 +2,7 @@ import { Vector3, Nullable } from './../../types';
2
2
  import vtkAbstractPicker, { IAbstractPickerInitialValues } from './AbstractPicker';
3
3
  import vtkActor from './Actor';
4
4
  import vtkMapper from './Mapper';
5
+ import vtkProp3D from './Prop3D';
5
6
  import vtkRenderer from './Renderer';
6
7
  import { vtkSubscription } from './../../interfaces';
7
8
 
@@ -67,16 +68,6 @@ export interface vtkPicker extends vtkAbstractPicker {
67
68
  */
68
69
  onPickChange(callback: OnPickChangeCallback): vtkSubscription;
69
70
 
70
- /**
71
- * Intersect data with specified ray.
72
- * Project the center point of the mapper onto the ray and determine its parametric value
73
- * @param {Vector3} p1
74
- * @param {Vector3} p2
75
- * @param {Number} tol
76
- * @param {vtkMapper} mapper
77
- */
78
- intersectWithLine(p1: Vector3, p2: Vector3, tol: number, mapper: vtkMapper): number;
79
-
80
71
  /**
81
72
  * Perform pick operation with selection point provided.
82
73
  * @param {Vector3} selection First two values should be x-y pixel coordinate, the third is usually zero.
@@ -84,6 +75,15 @@ export interface vtkPicker extends vtkAbstractPicker {
84
75
  */
85
76
  pick(selection: Vector3, renderer: vtkRenderer): void;
86
77
 
78
+ /**
79
+ * Perform pick operation with the provided selection and focal points.
80
+ * Both point are in world coordinates.
81
+ * @param {Vector3} selectionPoint
82
+ * @param {Vector3} focalPoint
83
+ * @param {vtkRenderer} renderer
84
+ */
85
+ pick3DPoint(selectionPoint: Vector3, focalPoint: Vector3, renderer: vtkRenderer): void;
86
+
87
87
  /**
88
88
  * Set position in mapper coordinates of pick point.
89
89
  * @param {Number} x The x coordinate.
@@ -141,6 +141,8 @@ export function newInstance(initialValues?: IPickerInitialValues): vtkPicker;
141
141
  * picking of points or cells based on the geometry of any vtkProp3D, use the
142
142
  * subclasses vtkPointPicker or vtkCellPicker. For hardware-accelerated
143
143
  * picking of any type of vtkProp, use vtkPropPicker or vtkWorldPointPicker.
144
+ *
145
+ * Note that only vtkProp3D's can be picked by vtkPicker.
144
146
  */
145
147
  export declare const vtkPicker: {
146
148
  newInstance: typeof newInstance,
@@ -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,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].
@@ -487,7 +487,7 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
487
487
  // weighting shift and scale
488
488
  `uniform float pwfshift0;`, `uniform float pwfscale0;`];
489
489
  if (useProjection) {
490
- tcoordFSDec.push('uniform vec3 spacing;', 'uniform int projectionSlabNumberOfSamples;', 'uniform float projectionConstantOffset;', 'uniform float projectionStepLength;');
490
+ tcoordFSDec.push('uniform vec3 volumeSizeMC;', 'uniform int projectionSlabNumberOfSamples;', 'uniform float projectionConstantOffset;', 'uniform float projectionStepLength;');
491
491
  }
492
492
  if (isDirectionUniform) {
493
493
  tcoordFSDec.push('in vec3 samplingDirVSOutput;');
@@ -571,7 +571,7 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
571
571
  }
572
572
 
573
573
  // Loop on all the samples of the projection
574
- tcoordFSImpl.push('vec3 projectionScaledDirection = projectionDirection / spacing;', 'vec3 projectionStep = projectionStepLength * projectionScaledDirection;', 'vec3 projectionStartPosition = volumePosTC + projectionConstantOffset * projectionScaledDirection;', 'vec4 tvalue = initialProjectionTextureValue;', 'for (int projectionSampleIdx = 0; projectionSampleIdx < projectionSlabNumberOfSamples; ++projectionSampleIdx) {', ' vec3 projectionSamplePosition = projectionStartPosition + float(projectionSampleIdx) * projectionStep;', ' vec4 sampledTextureValue = texture(volumeTexture, projectionSamplePosition);');
574
+ tcoordFSImpl.push('vec3 projectionScaledDirection = projectionDirection / volumeSizeMC;', 'vec3 projectionStep = projectionStepLength * projectionScaledDirection;', 'vec3 projectionStartPosition = volumePosTC + projectionConstantOffset * projectionScaledDirection;', 'vec4 tvalue = initialProjectionTextureValue;', 'for (int projectionSampleIdx = 0; projectionSampleIdx < projectionSlabNumberOfSamples; ++projectionSampleIdx) {', ' vec3 projectionSamplePosition = projectionStartPosition + float(projectionSampleIdx) * projectionStep;', ' vec4 sampledTextureValue = texture(volumeTexture, projectionSamplePosition);');
575
575
  switch (projectionMode) {
576
576
  case ProjectionMode.MAX:
577
577
  tcoordFSImpl.push(' tvalue = max(tvalue, sampledTextureValue);');
@@ -706,9 +706,11 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
706
706
  if (model.renderable.isProjectionEnabled()) {
707
707
  const image = model.currentImageDataInput;
708
708
  const spacing = image.getSpacing();
709
+ const dimensions = image.getDimensions();
709
710
  const projectionSlabThickness = model.renderable.getProjectionSlabThickness();
710
711
  const projectionSlabNumberOfSamples = model.renderable.getProjectionSlabNumberOfSamples();
711
- program.setUniform3fArray('spacing', spacing);
712
+ const volumeSize = vec3.mul([], spacing, dimensions);
713
+ program.setUniform3fArray('volumeSizeMC', volumeSize);
712
714
  program.setUniformi('projectionSlabNumberOfSamples', projectionSlabNumberOfSamples);
713
715
  const constantOffset = -0.5 * projectionSlabThickness;
714
716
  program.setUniformf('projectionConstantOffset', constantOffset);
@@ -1,5 +1,4 @@
1
1
  import vtkAbstractWidget from '../../Core/AbstractWidget';
2
2
 
3
3
  export default interface vtkResliceCursorWidgetDefaultInstance extends vtkAbstractWidget {
4
- invokeInternalInteractionEvent: () => void;
5
4
  }
@@ -135,7 +135,7 @@ function widgetBehavior(publicAPI, model) {
135
135
  const step = previousPosition.y - callData.position.y;
136
136
  publicAPI.translateCenterOnPlaneDirection(step);
137
137
  previousPosition = callData.position;
138
- publicAPI.invokeInternalInteractionEvent();
138
+ publicAPI.invokeInteractionEvent(publicAPI.getActiveInteraction());
139
139
  }
140
140
  }
141
141
  return macro.VOID;
@@ -164,7 +164,7 @@ function widgetBehavior(publicAPI, model) {
164
164
  const step = calldata.spinY;
165
165
  isScrolling = true;
166
166
  publicAPI.translateCenterOnPlaneDirection(step);
167
- publicAPI.invokeInternalInteractionEvent(
167
+ publicAPI.invokeInteractionEvent(
168
168
  // Force interaction mode because mouse cursor could be above rotation handle
169
169
  InteractionMethodsName.TranslateCenter);
170
170
  isScrolling = false;
@@ -187,20 +187,11 @@ function widgetBehavior(publicAPI, model) {
187
187
  if (model.activeState.getActive()) {
188
188
  const methodName = publicAPI.getActiveInteraction();
189
189
  publicAPI[methodName](callData);
190
- publicAPI.invokeInternalInteractionEvent(methodName);
190
+ publicAPI.invokeInteractionEvent(methodName);
191
191
  return macro.EVENT_ABORT;
192
192
  }
193
193
  return macro.VOID;
194
194
  };
195
- publicAPI.invokeInternalInteractionEvent = function () {
196
- let methodName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : publicAPI.getActiveInteraction();
197
- const computeFocalPointOffset = methodName !== InteractionMethodsName.RotateLine;
198
- const canUpdateFocalPoint = methodName === InteractionMethodsName.RotateLine;
199
- publicAPI.invokeInteractionEvent({
200
- computeFocalPointOffset,
201
- canUpdateFocalPoint
202
- });
203
- };
204
195
  publicAPI.startInteraction = () => {
205
196
  publicAPI.invokeStartInteractionEvent();
206
197
  // When interacting, plane actor and lines must be re-rendered on other views
@@ -33,7 +33,6 @@ export interface vtkResliceCursorWidget<WidgetInstance extends vtkAbstractWidget
33
33
  renderer: vtkRenderer,
34
34
  viewType: ViewTypes,
35
35
  resetFocalPoint: boolean,
36
- keepCenterFocalDistance: boolean,
37
36
  computeFocalPointOffset: boolean
38
37
  ): void;
39
38
 
@@ -215,8 +215,8 @@ function vtkResliceCursorWidget(publicAPI, model) {
215
215
  // Methods
216
216
  // --------------------------------------------------------------------------
217
217
 
218
- publicAPI.updateCameraPoints = (renderer, viewType, resetFocalPoint, keepCenterFocalDistance, computeFocalPointOffset) => {
219
- publicAPI.resetCamera(renderer, viewType, resetFocalPoint, keepCenterFocalDistance);
218
+ publicAPI.updateCameraPoints = (renderer, viewType, resetFocalPoint, computeFocalPointOffset) => {
219
+ publicAPI.resetCamera(renderer, viewType, resetFocalPoint, !computeFocalPointOffset);
220
220
  if (computeFocalPointOffset) {
221
221
  computeFocalPointOffsetFromResliceCursorCenter(viewType, renderer);
222
222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "29.11.1",
3
+ "version": "30.0.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",