@kitware/vtk.js 30.7.1 → 30.9.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.
@@ -15,7 +15,7 @@ function vtkInteractorStyleImage(publicAPI, model) {
15
15
  publicAPI.superHandleMouseMove = publicAPI.handleMouseMove;
16
16
  publicAPI.handleMouseMove = callData => {
17
17
  const pos = callData.position;
18
- const renderer = callData.pokedRenderer;
18
+ const renderer = model.getRenderer(callData);
19
19
  switch (model.state) {
20
20
  case States.IS_WINDOW_LEVEL:
21
21
  publicAPI.windowLevel(renderer, pos);
@@ -94,7 +94,7 @@ function vtkInteractorStyleImage(publicAPI, model) {
94
94
 
95
95
  //--------------------------------------------------------------------------
96
96
  publicAPI.handleMouseWheel = callData => {
97
- const camera = callData.pokedRenderer.getActiveCamera();
97
+ const camera = model.getRenderer(callData).getActiveCamera();
98
98
  let distance = camera.getDistance();
99
99
  distance += callData.spinY;
100
100
 
@@ -107,7 +107,7 @@ function vtkInteractorStyleImage(publicAPI, model) {
107
107
  distance = range[1];
108
108
  }
109
109
  camera.setDistance(distance);
110
- const props = callData.pokedRenderer.getViewProps().filter(prop => prop.isA('vtkImageSlice'));
110
+ const props = model.getRenderer(callData).getViewProps().filter(prop => prop.isA('vtkImageSlice'));
111
111
  props.forEach(prop => {
112
112
  if (prop.getMapper().isA('vtkImageResliceMapper')) {
113
113
  const p = prop.getMapper().getSlicePlane();
@@ -251,7 +251,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
251
251
  if (manipulator) {
252
252
  // register the manipulator for this device
253
253
  model.currentVRManipulators.set(ed.device, manipulator);
254
- manipulator.onButton3D(publicAPI, ed.pokedRenderer, model.state, ed);
254
+ manipulator.onButton3D(publicAPI, model.getRenderer(ed), model.state, ed);
255
255
  if (ed.pressed) {
256
256
  publicAPI.startCameraPose();
257
257
  } else {
@@ -271,7 +271,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
271
271
  publicAPI.handleMove3D = ed => {
272
272
  const manipulator = model.currentVRManipulators.get(ed.device);
273
273
  if (manipulator && model.state === States.IS_CAMERA_POSE) {
274
- manipulator.onMove3D(publicAPI, ed.pokedRenderer, model.state, ed);
274
+ manipulator.onMove3D(publicAPI, model.getRenderer(ed), model.state, ed);
275
275
  }
276
276
  };
277
277
 
@@ -292,7 +292,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
292
292
  model.currentManipulator.setRotationFactor(model.rotationFactor);
293
293
  }
294
294
  model.currentManipulator.startInteraction();
295
- model.currentManipulator.onButtonDown(model._interactor, callData.pokedRenderer, callData.position);
295
+ model.currentManipulator.onButtonDown(model._interactor, model.getRenderer(callData), callData.position);
296
296
  model._interactor.requestAnimation(publicAPI.onButtonDown);
297
297
  publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT);
298
298
  } else {
@@ -373,7 +373,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
373
373
  }
374
374
  if (manipulator) {
375
375
  model.currentWheelManipulator = manipulator;
376
- model.currentWheelManipulator.onStartScroll(model._interactor, callData.pokedRenderer, callData.spinY);
376
+ model.currentWheelManipulator.onStartScroll(model._interactor, model.getRenderer(callData), callData.spinY);
377
377
  model.currentWheelManipulator.startInteraction();
378
378
  model._interactor.requestAnimation(publicAPI.handleStartMouseWheel);
379
379
  publicAPI.invokeStartInteractionEvent(START_INTERACTION_EVENT);
@@ -399,7 +399,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
399
399
  //-------------------------------------------------------------------------
400
400
  publicAPI.handleMouseWheel = callData => {
401
401
  if (model.currentWheelManipulator && model.currentWheelManipulator.onScroll) {
402
- model.currentWheelManipulator.onScroll(model._interactor, callData.pokedRenderer, callData.spinY, model.cachedMousePosition);
402
+ model.currentWheelManipulator.onScroll(model._interactor, model.getRenderer(callData), callData.spinY, model.cachedMousePosition);
403
403
  publicAPI.invokeInteractionEvent(INTERACTION_EVENT);
404
404
  }
405
405
  };
@@ -408,7 +408,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
408
408
  publicAPI.handleMouseMove = callData => {
409
409
  model.cachedMousePosition = callData.position;
410
410
  if (model.currentManipulator && model.currentManipulator.onMouseMove) {
411
- model.currentManipulator.onMouseMove(model._interactor, callData.pokedRenderer, callData.position);
411
+ model.currentManipulator.onMouseMove(model._interactor, model.getRenderer(callData), callData.position);
412
412
  publicAPI.invokeInteractionEvent(INTERACTION_EVENT);
413
413
  }
414
414
  };
@@ -418,7 +418,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
418
418
  //-------------------------------------------------------------------------
419
419
  publicAPI.handleKeyPress = callData => {
420
420
  model.keyboardManipulators.filter(m => m.onKeyPress).forEach(manipulator => {
421
- manipulator.onKeyPress(model._interactor, callData.pokedRenderer, callData.key);
421
+ manipulator.onKeyPress(model._interactor, model.getRenderer(callData), callData.key);
422
422
  publicAPI.invokeInteractionEvent(INTERACTION_EVENT);
423
423
  });
424
424
  };
@@ -426,7 +426,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
426
426
  //-------------------------------------------------------------------------
427
427
  publicAPI.handleKeyDown = callData => {
428
428
  model.keyboardManipulators.filter(m => m.onKeyDown).forEach(manipulator => {
429
- manipulator.onKeyDown(model._interactor, callData.pokedRenderer, callData.key);
429
+ manipulator.onKeyDown(model._interactor, model.getRenderer(callData), callData.key);
430
430
  publicAPI.invokeInteractionEvent(INTERACTION_EVENT);
431
431
  });
432
432
  };
@@ -434,7 +434,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
434
434
  //-------------------------------------------------------------------------
435
435
  publicAPI.handleKeyUp = callData => {
436
436
  model.keyboardManipulators.filter(m => m.onKeyUp).forEach(manipulator => {
437
- manipulator.onKeyUp(model._interactor, callData.pokedRenderer, callData.key);
437
+ manipulator.onKeyUp(model._interactor, model.getRenderer(callData), callData.key);
438
438
  publicAPI.invokeInteractionEvent(INTERACTION_EVENT);
439
439
  });
440
440
  };
@@ -539,7 +539,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
539
539
  while (count--) {
540
540
  const manipulator = model.gestureManipulators[count];
541
541
  if (manipulator && manipulator.isPinchEnabled()) {
542
- manipulator.onPinch(model._interactor, callData.pokedRenderer, callData.scale);
542
+ manipulator.onPinch(model._interactor, model.getRenderer(callData), callData.scale);
543
543
  actionCount++;
544
544
  }
545
545
  }
@@ -555,7 +555,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
555
555
  while (count--) {
556
556
  const manipulator = model.gestureManipulators[count];
557
557
  if (manipulator && manipulator.isPanEnabled()) {
558
- manipulator.onPan(model._interactor, callData.pokedRenderer, callData.translation);
558
+ manipulator.onPan(model._interactor, model.getRenderer(callData), callData.translation);
559
559
  actionCount++;
560
560
  }
561
561
  }
@@ -571,7 +571,7 @@ function vtkInteractorStyleManipulator(publicAPI, model) {
571
571
  while (count--) {
572
572
  const manipulator = model.gestureManipulators[count];
573
573
  if (manipulator && manipulator.isRotateEnabled()) {
574
- manipulator.onRotate(model._interactor, callData.pokedRenderer, callData.rotation);
574
+ manipulator.onRotate(model._interactor, model.getRenderer(callData), callData.rotation);
575
575
  actionCount++;
576
576
  }
577
577
  }
@@ -21,7 +21,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
21
21
  // Public API methods
22
22
  publicAPI.handleMouseMove = callData => {
23
23
  const pos = callData.position;
24
- const renderer = callData.pokedRenderer;
24
+ const renderer = model.getRenderer(callData);
25
25
  switch (model.state) {
26
26
  case States.IS_ROTATE:
27
27
  publicAPI.handleMouseRotate(renderer, pos);
@@ -73,7 +73,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
73
73
  publicAPI.updateCameraPose = ed => {
74
74
  // move the world in the direction of the
75
75
  // controller
76
- const camera = ed.pokedRenderer.getActiveCamera();
76
+ const camera = model.getRenderer(ed).getActiveCamera();
77
77
  const oldTrans = camera.getPhysicalTranslation();
78
78
 
79
79
  // look at the y axis to determine how fast / what direction to move
@@ -169,25 +169,25 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
169
169
 
170
170
  //----------------------------------------------------------------------------
171
171
  publicAPI.handlePinch = callData => {
172
- publicAPI.dollyByFactor(callData.pokedRenderer, callData.scale / model.previousScale);
172
+ publicAPI.dollyByFactor(model.getRenderer(callData), callData.scale / model.previousScale);
173
173
  model.previousScale = callData.scale;
174
174
  };
175
175
 
176
176
  //----------------------------------------------------------------------------
177
177
  publicAPI.handlePan = callData => {
178
- const camera = callData.pokedRenderer.getActiveCamera();
178
+ const camera = model.getRenderer(callData).getActiveCamera();
179
179
 
180
180
  // Calculate the focal depth since we'll be using it a lot
181
181
  let viewFocus = camera.getFocalPoint();
182
- viewFocus = publicAPI.computeWorldToDisplay(callData.pokedRenderer, viewFocus[0], viewFocus[1], viewFocus[2]);
182
+ viewFocus = publicAPI.computeWorldToDisplay(model.getRenderer(callData), viewFocus[0], viewFocus[1], viewFocus[2]);
183
183
  const focalDepth = viewFocus[2];
184
184
  const trans = callData.translation;
185
185
  const lastTrans = model.previousTranslation;
186
- const newPickPoint = publicAPI.computeDisplayToWorld(callData.pokedRenderer, viewFocus[0] + trans[0] - lastTrans[0], viewFocus[1] + trans[1] - lastTrans[1], focalDepth);
186
+ const newPickPoint = publicAPI.computeDisplayToWorld(model.getRenderer(callData), viewFocus[0] + trans[0] - lastTrans[0], viewFocus[1] + trans[1] - lastTrans[1], focalDepth);
187
187
 
188
188
  // Has to recalc old mouse point since the viewport has moved,
189
189
  // so can't move it outside the loop
190
- const oldPickPoint = publicAPI.computeDisplayToWorld(callData.pokedRenderer, viewFocus[0], viewFocus[1], focalDepth);
190
+ const oldPickPoint = publicAPI.computeDisplayToWorld(model.getRenderer(callData), viewFocus[0], viewFocus[1], focalDepth);
191
191
 
192
192
  // Camera motion is reversed
193
193
  const motionVector = [];
@@ -199,7 +199,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
199
199
  camera.setFocalPoint(motionVector[0] + viewFocus[0], motionVector[1] + viewFocus[1], motionVector[2] + viewFocus[2]);
200
200
  camera.setPosition(motionVector[0] + viewPoint[0], motionVector[1] + viewPoint[1], motionVector[2] + viewPoint[2]);
201
201
  if (model._interactor.getLightFollowCamera()) {
202
- callData.pokedRenderer.updateLightsGeometryToFollowCamera();
202
+ model.getRenderer(callData).updateLightsGeometryToFollowCamera();
203
203
  }
204
204
  camera.orthogonalizeViewUp();
205
205
  model.previousTranslation = callData.translation;
@@ -207,7 +207,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
207
207
 
208
208
  //----------------------------------------------------------------------------
209
209
  publicAPI.handleRotate = callData => {
210
- const camera = callData.pokedRenderer.getActiveCamera();
210
+ const camera = model.getRenderer(callData).getActiveCamera();
211
211
  camera.roll(callData.rotation - model.previousRotation);
212
212
  camera.orthogonalizeViewUp();
213
213
  model.previousRotation = callData.rotation;
@@ -306,7 +306,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) {
306
306
  //----------------------------------------------------------------------------
307
307
  publicAPI.handleMouseWheel = callData => {
308
308
  const dyf = 1 - callData.spinY / model.zoomFactor;
309
- publicAPI.dollyByFactor(callData.pokedRenderer, dyf);
309
+ publicAPI.dollyByFactor(model.getRenderer(callData), dyf);
310
310
  };
311
311
 
312
312
  //----------------------------------------------------------------------------
@@ -145,6 +145,13 @@ export interface vtkOrientationMarkerWidget extends vtkObject {
145
145
  * Updates the orientation widget viewport size.
146
146
  */
147
147
  updateViewport(): void;
148
+
149
+ /**
150
+ * An instance of this class will spawn its own renderer, by default non interactive.
151
+ * This behavior is configurable through the interactiveRenderer property when initializing the instance.
152
+ * @returns true if the renderer was created as interactive, false otherwise.
153
+ */
154
+ getInteractiveRenderer(): boolean;
148
155
  }
149
156
 
150
157
  /**
@@ -123,7 +123,7 @@ function vtkOrientationMarkerWidget(publicAPI, model) {
123
123
  }
124
124
  // Highest number is foreground
125
125
  selfRenderer.setLayer(renderWindow.getNumberOfLayers() - 1);
126
- selfRenderer.setInteractive(false);
126
+ selfRenderer.setInteractive(model.interactiveRenderer);
127
127
  selfRenderer.addViewProp(model.actor);
128
128
  model.actor.setVisibility(true);
129
129
  onCameraChangedSub = ren.onEvent(event => {
@@ -236,7 +236,8 @@ const DEFAULT_VALUES = {
236
236
  viewportSize: 0.2,
237
237
  minPixelSize: 50,
238
238
  maxPixelSize: 200,
239
- parentRenderer: null
239
+ parentRenderer: null,
240
+ interactiveRenderer: false
240
241
  };
241
242
 
242
243
  // ----------------------------------------------------------------------------
@@ -247,7 +248,7 @@ function extend(publicAPI, model) {
247
248
 
248
249
  // Build VTK API
249
250
  macro.obj(publicAPI, model);
250
- macro.get(publicAPI, model, ['enabled', 'viewportCorner', 'viewportSize']);
251
+ macro.get(publicAPI, model, ['enabled', 'viewportCorner', 'viewportSize', 'interactiveRenderer']);
251
252
 
252
253
  // NOTE: setting these while the widget is enabled will
253
254
  // not update the widget.
@@ -1,5 +1,7 @@
1
1
  import { EventHandler, vtkSubscription } from './../../interfaces';
2
+ import { Nullable } from './../../types';
2
3
  import vtkInteractorObserver from './InteractorObserver';
4
+ import vtkRenderer from './Renderer';
3
5
 
4
6
  export interface vtkInteractorStyle extends vtkInteractorObserver {
5
7
  /**
@@ -210,6 +212,19 @@ export interface vtkInteractorStyle extends vtkInteractorObserver {
210
212
  * Handles a keypress.
211
213
  */
212
214
  handleKeyPress(callData: unknown): void;
215
+
216
+ /**
217
+ * Explicitly defines a renderer to be used for event handling.
218
+ * If never called or called with null, the pokedRenderer of the event will be used.
219
+ *
220
+ * @param {Nullable<vtkRenderer>} renderer
221
+ */
222
+ setFocusedRenderer(renderer: Nullable<vtkRenderer>): boolean;
223
+
224
+ /**
225
+ * Get the renderer used for event handling, returns null if not set.
226
+ */
227
+ getFocusedRenderer(): Nullable<vtkRenderer>;
213
228
  }
214
229
 
215
230
  export interface IInteractorStyleInitialValues {
@@ -64,6 +64,7 @@ function vtkInteractorStyle(publicAPI, model) {
64
64
  model._interactor.render();
65
65
  };
66
66
  });
67
+ model.getRenderer = callData => model.focusedRenderer || callData.pokedRenderer;
67
68
 
68
69
  //----------------------------------------------------------------------------
69
70
  publicAPI.handleKeyPress = callData => {
@@ -72,12 +73,12 @@ function vtkInteractorStyle(publicAPI, model) {
72
73
  switch (callData.key) {
73
74
  case 'r':
74
75
  case 'R':
75
- callData.pokedRenderer.resetCamera();
76
+ model.getRenderer(callData).resetCamera();
76
77
  rwi.render();
77
78
  break;
78
79
  case 'w':
79
80
  case 'W':
80
- ac = callData.pokedRenderer.getActors();
81
+ ac = model.getRenderer(callData).getActors();
81
82
  ac.forEach(anActor => {
82
83
  const prop = anActor.getProperty();
83
84
  if (prop.setRepresentationToWireframe) {
@@ -88,7 +89,7 @@ function vtkInteractorStyle(publicAPI, model) {
88
89
  break;
89
90
  case 's':
90
91
  case 'S':
91
- ac = callData.pokedRenderer.getActors();
92
+ ac = model.getRenderer(callData).getActors();
92
93
  ac.forEach(anActor => {
93
94
  const prop = anActor.getProperty();
94
95
  if (prop.setRepresentationToSurface) {
@@ -99,7 +100,7 @@ function vtkInteractorStyle(publicAPI, model) {
99
100
  break;
100
101
  case 'v':
101
102
  case 'V':
102
- ac = callData.pokedRenderer.getActors();
103
+ ac = model.getRenderer(callData).getActors();
103
104
  ac.forEach(anActor => {
104
105
  const prop = anActor.getProperty();
105
106
  if (prop.setRepresentationToPoints) {
@@ -130,6 +131,7 @@ function extend(publicAPI, model) {
130
131
 
131
132
  // Inheritance
132
133
  vtkInteractorObserver.extend(publicAPI, model, initialValues);
134
+ macro.setGet(publicAPI, model, ['focusedRenderer']);
133
135
 
134
136
  // Object specific methods
135
137
  vtkInteractorStyle(publicAPI, model);
@@ -3,6 +3,7 @@ import vtkAbstractMapper3D, {
3
3
  IAbstractMapper3DInitialValues,
4
4
  } from './AbstractMapper3D';
5
5
  import { ColorMode, GetArray, ScalarMode } from './Mapper/Constants';
6
+ import vtkDataArray from './../../Common/Core/DataArray';
6
7
 
7
8
  interface IPrimitiveCount {
8
9
  points: number;
@@ -13,6 +14,7 @@ interface IPrimitiveCount {
13
14
 
14
15
  interface IAbstractScalars {
15
16
  cellFlag: boolean;
17
+ scalars: Nullable<vtkDataArray>;
16
18
  }
17
19
 
18
20
  interface ICoincidentTopology {
@@ -54,9 +56,13 @@ export interface vtkMapper extends vtkAbstractMapper3D {
54
56
  * When rendering multiblock datasets, if any 2 blocks provide different
55
57
  * lookup tables for the scalars, then also we cannot use textures. This case
56
58
  * can be handled if required.
57
- * @param input
59
+ * @param scalars
60
+ * @param cellFlag True when the scalars are per cell instead of per point
58
61
  */
59
- canUseTextureMapForColoring(input: any): boolean;
62
+ canUseTextureMapForColoring(
63
+ scalars: vtkDataArray,
64
+ cellFlag: boolean
65
+ ): boolean;
60
66
 
61
67
  /**
62
68
  * Call to force a rebuild of color result arrays on next MapScalars.
@@ -77,26 +83,18 @@ export interface vtkMapper extends vtkAbstractMapper3D {
77
83
  /**
78
84
  *
79
85
  * @param input
80
- * @param output
81
- * @param numScalars
82
- * @param numComps
83
86
  * @param component
84
87
  * @param range
85
- * @param tableRange
86
88
  * @param tableNumberOfColors
87
89
  * @param useLogScale
88
90
  */
89
91
  createColorTextureCoordinates(
90
- input: any,
91
- output: any,
92
- numScalars: number,
93
- numComps: number,
92
+ input: vtkDataArray,
94
93
  component: number,
95
94
  range: any,
96
- tableRange: any,
97
95
  tableNumberOfColors: number,
98
96
  useLogScale: boolean
99
- ): void;
97
+ ): vtkDataArray;
100
98
 
101
99
  /**
102
100
  * Create default lookup table. Generally used to create one when
@@ -120,6 +118,17 @@ export interface vtkMapper extends vtkAbstractMapper3D {
120
118
  arrayName: any
121
119
  ): IAbstractScalars;
122
120
 
121
+ /**
122
+ * When scalars are mapped from cells,
123
+ * there is one color coordinate per cell instead of one per point
124
+ * in the vtkDataArray getColorCoordinates().
125
+ * It means that when getAreScalarsMappedFromCells() is true,
126
+ * the number of tuples in getColorCoordinates() is the number of points,
127
+ * and when getAreScalarsMappedFromCells() is false,
128
+ * the number of tuples in getColorCoordinates() is the number of cells.
129
+ */
130
+ getAreScalarsMappedFromCells(): boolean;
131
+
123
132
  /**
124
133
  *
125
134
  */
@@ -132,7 +132,11 @@ function vtkMapper(publicAPI, model) {
132
132
  };
133
133
  };
134
134
  publicAPI.mapScalars = (input, alpha) => {
135
- const scalars = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName).scalars;
135
+ const {
136
+ scalars,
137
+ cellFlag
138
+ } = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName);
139
+ model.areScalarsMappedFromCells = cellFlag;
136
140
  if (!scalars) {
137
141
  model.colorCoordinates = null;
138
142
  model.colorTextureMap = null;
@@ -150,7 +154,7 @@ function vtkMapper(publicAPI, model) {
150
154
  // Decide between texture color or vertex color.
151
155
  // Cell data always uses vertex color.
152
156
  // Only point data can use both texture and vertex coloring.
153
- if (publicAPI.canUseTextureMapForColoring(input)) {
157
+ if (publicAPI.canUseTextureMapForColoring(scalars, cellFlag)) {
154
158
  publicAPI.mapScalarsToTexture(scalars, alpha);
155
159
  } else {
156
160
  model.colorCoordinates = null;
@@ -205,50 +209,56 @@ function vtkMapper(publicAPI, model) {
205
209
  };
206
210
 
207
211
  //-----------------------------------------------------------------------------
208
- publicAPI.createColorTextureCoordinates = (input, output, numScalars, numComps, component, range, tableRange, tableNumberOfColors, useLogScale) => {
212
+ publicAPI.createColorTextureCoordinates = (input, component, range, tableNumberOfColors, useLogScale) => {
209
213
  // We have to change the range used for computing texture
210
214
  // coordinates slightly to accommodate the special above- and
211
215
  // below-range colors that are the first and last texels,
212
216
  // respectively.
213
217
  const scalarTexelWidth = (range[1] - range[0]) / tableNumberOfColors;
214
- const paddedRange = [];
215
- paddedRange[0] = range[0] - scalarTexelWidth;
216
- paddedRange[1] = range[1] + scalarTexelWidth;
218
+ const paddedRange = [range[0] - scalarTexelWidth, range[1] + scalarTexelWidth];
217
219
  const invRangeWidth = 1.0 / (paddedRange[1] - paddedRange[0]);
218
- const outputV = output.getData();
219
220
  const inputV = input.getData();
220
- let count = 0;
221
- let outputCount = 0;
221
+ const numScalars = input.getNumberOfTuples();
222
+ const numComps = input.getNumberOfComponents();
223
+ const output = vtkDataArray.newInstance({
224
+ numberOfComponents: 2,
225
+ values: new Float32Array(numScalars * 2)
226
+ });
227
+ const outputV = output.getData();
222
228
  if (component < 0 || component >= numComps) {
229
+ // Convert the magnitude of all components to texture coordinates
230
+ let inputIdx = 0;
231
+ let outputIdx = 0;
223
232
  for (let scalarIdx = 0; scalarIdx < numScalars; ++scalarIdx) {
224
233
  let sum = 0;
225
234
  for (let compIdx = 0; compIdx < numComps; ++compIdx) {
226
- sum += inputV[count] * inputV[count];
227
- count++;
235
+ sum += inputV[inputIdx] * inputV[inputIdx];
236
+ inputIdx++;
228
237
  }
229
238
  let magnitude = Math.sqrt(sum);
230
239
  if (useLogScale) {
231
- magnitude = vtkLookupTable.applyLogScale(magnitude, tableRange, range);
240
+ magnitude = vtkLookupTable.applyLogScale(magnitude, range, range);
232
241
  }
233
242
  const outputs = publicAPI.scalarToTextureCoordinate(magnitude, paddedRange[0], invRangeWidth);
234
- outputV[outputCount] = outputs.texCoordS;
235
- outputV[outputCount + 1] = outputs.texCoordT;
236
- outputCount += 2;
243
+ outputV[outputIdx++] = outputs.texCoordS;
244
+ outputV[outputIdx++] = outputs.texCoordT;
237
245
  }
238
246
  } else {
239
- count += component;
247
+ // Convert one of the components to texture coordinates
248
+ let inputIdx = component;
249
+ let outputIdx = 0;
240
250
  for (let scalarIdx = 0; scalarIdx < numScalars; ++scalarIdx) {
241
- let inputValue = inputV[count];
251
+ let inputValue = inputV[inputIdx];
242
252
  if (useLogScale) {
243
- inputValue = vtkLookupTable.applyLogScale(inputValue, tableRange, range);
253
+ inputValue = vtkLookupTable.applyLogScale(inputValue, range, range);
244
254
  }
245
255
  const outputs = publicAPI.scalarToTextureCoordinate(inputValue, paddedRange[0], invRangeWidth);
246
- outputV[outputCount] = outputs.texCoordS;
247
- outputV[outputCount + 1] = outputs.texCoordT;
248
- outputCount += 2;
249
- count += numComps;
256
+ outputV[outputIdx++] = outputs.texCoordS;
257
+ outputV[outputIdx++] = outputs.texCoordT;
258
+ inputIdx += numComps;
250
259
  }
251
260
  }
261
+ return output;
252
262
  };
253
263
  publicAPI.mapScalarsToTexture = (scalars, alpha) => {
254
264
  const range = model.lookupTable.getRange();
@@ -274,28 +284,23 @@ function vtkMapper(publicAPI, model) {
274
284
  // Create a dummy ramp of scalars.
275
285
  // In the future, we could extend vtkScalarsToColors.
276
286
  model.lookupTable.build();
277
- let numberOfColors = model.lookupTable.getNumberOfAvailableColors();
278
- if (numberOfColors > 4094) {
279
- numberOfColors = 4094;
280
- }
281
- if (numberOfColors < 64) {
282
- numberOfColors = 64;
283
- }
284
- numberOfColors += 2;
285
- const k = (range[1] - range[0]) / (numberOfColors - 2);
286
- const newArray = new Float64Array(numberOfColors * 2);
287
- for (let i = 0; i < numberOfColors; ++i) {
288
- newArray[i] = range[0] + i * k - k / 2.0; // minus k / 2 to start at below range color
287
+ const numberOfColorsInRange = Math.min(Math.max(model.lookupTable.getNumberOfAvailableColors(), 64), 4094);
288
+ // Texel width is computed before min and max colors that are out of range
289
+ const scalarTexelWidth = (range[1] - range[0]) / numberOfColorsInRange;
290
+ // Add two colors for special min and max colors
291
+ const totalNumberOfColors = numberOfColorsInRange + 2;
292
+ const newArray = new Float64Array(totalNumberOfColors * 2);
293
+ for (let i = 0; i < totalNumberOfColors; ++i) {
294
+ // minus 0.5 to start at below range color
295
+ newArray[i] = range[0] + (i - 0.5) * scalarTexelWidth;
289
296
  if (useLogScale) {
290
297
  newArray[i] = 10.0 ** newArray[i];
291
298
  }
292
299
  }
293
300
  // Dimension on NaN.
294
- for (let i = 0; i < numberOfColors; ++i) {
295
- newArray[i + numberOfColors] = NaN;
296
- }
301
+ newArray.fill(NaN, totalNumberOfColors);
297
302
  model.colorTextureMap = vtkImageData.newInstance();
298
- model.colorTextureMap.setExtent(0, numberOfColors - 1, 0, 1, 0, 0);
303
+ model.colorTextureMap.setExtent(0, totalNumberOfColors - 1, 0, 1, 0, 0);
299
304
  const tmp = vtkDataArray.newInstance({
300
305
  numberOfComponents: 1,
301
306
  values: newArray
@@ -304,28 +309,20 @@ function vtkMapper(publicAPI, model) {
304
309
  model.lookupTable.setAlpha(origAlpha);
305
310
  }
306
311
 
307
- // Create new coordinates if necessary.
308
- // Need to compare lookup table in case the range has changed.
309
- if (!model.colorCoordinates || publicAPI.getMTime() > model.colorCoordinates.getMTime() || publicAPI.getInputData(0).getMTime() > model.colorCoordinates.getMTime() || model.lookupTable.getMTime() > model.colorCoordinates.getMTime()) {
310
- // Get rid of old colors
311
- model.colorCoordinates = null;
312
+ // Although I like the feature of applying magnitude to single component
313
+ // scalars, it is not how the old MapScalars for vertex coloring works.
314
+ const scalarComponent = model.lookupTable.getVectorMode() === VectorMode.MAGNITUDE && scalars.getNumberOfComponents() > 1 ? -1 : model.lookupTable.getVectorComponent();
312
315
 
313
- // Now create the color texture coordinates.
314
- const numComps = scalars.getNumberOfComponents();
315
- const num = scalars.getNumberOfTuples();
316
+ // Reverse the computation of numberOfColorsInRange that is used to compute model.colorTextureMap
317
+ const textureMapTuples = model.colorTextureMap.getPointData().getScalars().getNumberOfTuples();
318
+ const totalNumberOfColors = textureMapTuples / 2;
319
+ const numberOfColorsInRange = totalNumberOfColors - 2;
316
320
 
317
- // const fArray = new FloatArray(num * 2);
318
- model.colorCoordinates = vtkDataArray.newInstance({
319
- numberOfComponents: 2,
320
- values: new Float32Array(num * 2)
321
- });
322
- let scalarComponent = model.lookupTable.getVectorComponent();
323
- // Although I like the feature of applying magnitude to single component
324
- // scalars, it is not how the old MapScalars for vertex coloring works.
325
- if (model.lookupTable.getVectorMode() === VectorMode.MAGNITUDE && scalars.getNumberOfComponents() > 1) {
326
- scalarComponent = -1;
327
- }
328
- publicAPI.createColorTextureCoordinates(scalars, model.colorCoordinates, num, numComps, scalarComponent, range, model.lookupTable.getRange(), model.colorTextureMap.getPointData().getScalars().getNumberOfTuples() / 2 - 2, useLogScale);
321
+ // Create new coordinates if necessary.
322
+ const colorCoordinatesString = `${scalars.getMTime()}/${scalarComponent}/${range}/${numberOfColorsInRange}/${useLogScale}`;
323
+ if (colorCoordinatesString !== model.colorCoordinatesString) {
324
+ model.colorCoordinates = publicAPI.createColorTextureCoordinates(scalars, scalarComponent, range, numberOfColorsInRange, useLogScale);
325
+ model.colorCoordinatesString = colorCoordinatesString;
329
326
  }
330
327
  };
331
328
  publicAPI.getIsOpaque = () => {
@@ -344,7 +341,11 @@ function vtkMapper(publicAPI, model) {
344
341
  }
345
342
  return true;
346
343
  };
347
- publicAPI.canUseTextureMapForColoring = input => {
344
+ publicAPI.canUseTextureMapForColoring = (scalars, cellFlag) => {
345
+ if (cellFlag) {
346
+ return true; // cell data always use textures.
347
+ }
348
+
348
349
  if (!model.interpolateScalarsBeforeMapping) {
349
350
  return false; // user doesn't want us to use texture maps at all.
350
351
  }
@@ -353,16 +354,10 @@ function vtkMapper(publicAPI, model) {
353
354
  if (model.lookupTable && model.lookupTable.getIndexedLookup()) {
354
355
  return false;
355
356
  }
356
- const gasResult = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName);
357
- const scalars = gasResult.scalars;
358
357
  if (!scalars) {
359
358
  // no scalars on this dataset, we don't care if texture is used at all.
360
359
  return false;
361
360
  }
362
- if (gasResult.cellFlag) {
363
- return false; // cell data colors, don't use textures.
364
- }
365
-
366
361
  if (model.colorMode === ColorMode.DEFAULT && scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR || model.colorMode === ColorMode.DIRECT_SCALARS) {
367
362
  // Don't use texture is direct coloring using RGB unsigned chars is
368
363
  // requested.
@@ -464,7 +459,7 @@ function vtkMapper(publicAPI, model) {
464
459
  const DEFAULT_VALUES = {
465
460
  colorMapColors: null,
466
461
  // Same as this->Colors
467
-
462
+ areScalarsMappedFromCells: false,
468
463
  static: false,
469
464
  lookupTable: null,
470
465
  scalarVisibility: true,
@@ -497,7 +492,7 @@ function extend(publicAPI, model) {
497
492
 
498
493
  // Inheritance
499
494
  vtkAbstractMapper3D.extend(publicAPI, model, initialValues);
500
- macro.get(publicAPI, model, ['colorCoordinates', 'colorMapColors', 'colorTextureMap', 'selectionWebGLIdsToVTKIds']);
495
+ macro.get(publicAPI, model, ['areScalarsMappedFromCells', 'colorCoordinates', 'colorMapColors', 'colorTextureMap', 'selectionWebGLIdsToVTKIds']);
501
496
  macro.setGet(publicAPI, model, ['colorByArrayName', 'arrayAccessMode', 'colorMode', 'fieldDataTupleId', 'interpolateScalarsBeforeMapping', 'lookupTable', 'populateSelectionSettings', 'renderTime', 'scalarMode', 'scalarVisibility', 'static', 'useLookupTableScalarRange', 'customShaderAttributes' // point data array names that will be transferred to the VBO
502
497
  ]);
503
498
 
@@ -104,52 +104,52 @@ function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
104
104
  let addAPoint;
105
105
  const cellBuilders = {
106
106
  // easy, every input point becomes an output point
107
- anythingToPoints(numPoints, cellPts, offset) {
107
+ anythingToPoints(numPoints, cellPts, offset, cellId) {
108
108
  for (let i = 0; i < numPoints; ++i) {
109
- addAPoint(cellPts[offset + i]);
109
+ addAPoint(cellPts[offset + i], cellId);
110
110
  }
111
111
  },
112
- linesToWireframe(numPoints, cellPts, offset) {
112
+ linesToWireframe(numPoints, cellPts, offset, cellIdx) {
113
113
  // for lines we add a bunch of segments
114
114
  for (let i = 0; i < numPoints - 1; ++i) {
115
- addAPoint(cellPts[offset + i]);
116
- addAPoint(cellPts[offset + i + 1]);
115
+ addAPoint(cellPts[offset + i], cellIdx);
116
+ addAPoint(cellPts[offset + i + 1], cellIdx);
117
117
  }
118
118
  },
119
- polysToWireframe(numPoints, cellPts, offset) {
119
+ polysToWireframe(numPoints, cellPts, offset, cellIdx) {
120
120
  // for polys we add a bunch of segments and close it
121
121
  if (numPoints > 2) {
122
122
  for (let i = 0; i < numPoints; ++i) {
123
- addAPoint(cellPts[offset + i]);
124
- addAPoint(cellPts[offset + (i + 1) % numPoints]);
123
+ addAPoint(cellPts[offset + i], cellIdx);
124
+ addAPoint(cellPts[offset + (i + 1) % numPoints], cellIdx);
125
125
  }
126
126
  }
127
127
  },
128
- stripsToWireframe(numPoints, cellPts, offset) {
128
+ stripsToWireframe(numPoints, cellPts, offset, cellIdx) {
129
129
  if (numPoints > 2) {
130
130
  // for strips we add a bunch of segments and close it
131
131
  for (let i = 0; i < numPoints - 1; ++i) {
132
- addAPoint(cellPts[offset + i]);
133
- addAPoint(cellPts[offset + i + 1]);
132
+ addAPoint(cellPts[offset + i], cellIdx);
133
+ addAPoint(cellPts[offset + i + 1], cellIdx);
134
134
  }
135
135
  for (let i = 0; i < numPoints - 2; i++) {
136
- addAPoint(cellPts[offset + i]);
137
- addAPoint(cellPts[offset + i + 2]);
136
+ addAPoint(cellPts[offset + i], cellIdx);
137
+ addAPoint(cellPts[offset + i + 2], cellIdx);
138
138
  }
139
139
  }
140
140
  },
141
- polysToSurface(npts, cellPts, offset) {
141
+ polysToSurface(npts, cellPts, offset, cellIdx) {
142
142
  for (let i = 0; i < npts - 2; i++) {
143
- addAPoint(cellPts[offset + 0]);
144
- addAPoint(cellPts[offset + i + 1]);
145
- addAPoint(cellPts[offset + i + 2]);
143
+ addAPoint(cellPts[offset + 0], cellIdx);
144
+ addAPoint(cellPts[offset + i + 1], cellIdx);
145
+ addAPoint(cellPts[offset + i + 2], cellIdx);
146
146
  }
147
147
  },
148
- stripsToSurface(npts, cellPts, offset) {
148
+ stripsToSurface(npts, cellPts, offset, cellIdx) {
149
149
  for (let i = 0; i < npts - 2; i++) {
150
- addAPoint(cellPts[offset + i]);
151
- addAPoint(cellPts[offset + i + 1 + i % 2]);
152
- addAPoint(cellPts[offset + i + 1 + (i + 1) % 2]);
150
+ addAPoint(cellPts[offset + i], cellIdx);
151
+ addAPoint(cellPts[offset + i + 1 + i % 2], cellIdx);
152
+ addAPoint(cellPts[offset + i + 1 + (i + 1) % 2], cellIdx);
153
153
  }
154
154
  }
155
155
  };
@@ -264,16 +264,16 @@ function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
264
264
  }
265
265
  }
266
266
  let pointCount = options.vertexOffset;
267
- addAPoint = function addAPointFunc(i) {
267
+ addAPoint = function addAPointFunc(pointId, cellId) {
268
268
  // Keep track of original point and cell ids, for selection
269
269
  if (selectionMaps) {
270
- selectionMaps.points[pointCount] = i;
270
+ selectionMaps.points[pointCount] = pointId;
271
271
  selectionMaps.cells[pointCount] = cellCount + options.cellOffset;
272
272
  }
273
273
  ++pointCount;
274
274
 
275
275
  // Vertices
276
- pointIdx = i * 3;
276
+ pointIdx = pointId * 3;
277
277
  if (!model.coordShiftAndScaleEnabled) {
278
278
  packedVBO[vboidx++] = pointData[pointIdx++];
279
279
  packedVBO[vboidx++] = pointData[pointIdx++];
@@ -288,20 +288,24 @@ function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
288
288
  if (options.haveCellNormals) {
289
289
  normalIdx = (cellCount + options.cellOffset) * 3;
290
290
  } else {
291
- normalIdx = i * 3;
291
+ normalIdx = pointId * 3;
292
292
  }
293
293
  packedVBO[vboidx++] = normalData[normalIdx++];
294
294
  packedVBO[vboidx++] = normalData[normalIdx++];
295
295
  packedVBO[vboidx++] = normalData[normalIdx++];
296
296
  }
297
297
  model.customData.forEach(attr => {
298
- custIdx = i * attr.components;
298
+ custIdx = pointId * attr.components;
299
299
  for (let j = 0; j < attr.components; ++j) {
300
300
  packedVBO[vboidx++] = attr.data[custIdx++];
301
301
  }
302
302
  });
303
303
  if (tcoordData !== null) {
304
- tcoordIdx = i * textureComponents;
304
+ if (options.useTCoordsPerCell) {
305
+ tcoordIdx = cellId * textureComponents;
306
+ } else {
307
+ tcoordIdx = pointId * textureComponents;
308
+ }
305
309
  for (let j = 0; j < textureComponents; ++j) {
306
310
  packedVBO[vboidx++] = tcoordData[tcoordIdx++];
307
311
  }
@@ -310,7 +314,7 @@ function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
310
314
  if (options.haveCellScalars) {
311
315
  colorIdx = (cellCount + options.cellOffset) * colorComponents;
312
316
  } else {
313
- colorIdx = i * colorComponents;
317
+ colorIdx = pointId * colorComponents;
314
318
  }
315
319
  packedUCVBO[ucidx++] = colorData[colorIdx++];
316
320
  packedUCVBO[ucidx++] = colorData[colorIdx++];
@@ -318,10 +322,11 @@ function vtkOpenGLCellArrayBufferObject(publicAPI, model) {
318
322
  packedUCVBO[ucidx++] = colorComponents === 4 ? colorData[colorIdx++] : 255;
319
323
  }
320
324
  };
321
- for (let index = 0; index < size;) {
322
- func(array[index], array, index + 1);
323
- index += array[index] + 1;
324
- cellCount++;
325
+
326
+ // Browse the cell array: the index is at the beginning of a cell
327
+ // The value of 'array' at the position 'index' is the number of points in the cell
328
+ for (let index = 0; index < size; index += array[index] + 1, cellCount++) {
329
+ func(array[index], array, index + 1, cellCount);
325
330
  }
326
331
  model.elementCount = caboCount;
327
332
  publicAPI.upload(packedVBO, ObjectType.ARRAY_BUFFER);
@@ -147,7 +147,7 @@ function vtkOpenGLPolyDataMapper(publicAPI, model) {
147
147
  if (model.lastBoundBO.getCABO().getColorComponents() !== 0 && !model.drawingEdges) {
148
148
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', colorImpl.concat([' diffuseColor = vertexColorVSOutput.rgb;', ' ambientColor = vertexColorVSOutput.rgb;', ' opacity = opacity*vertexColorVSOutput.a;'])).result;
149
149
  } else {
150
- if (model.renderable.getInterpolateScalarsBeforeMapping() && model.renderable.getColorCoordinates() && !model.drawingEdges) {
150
+ if ((model.renderable.getAreScalarsMappedFromCells() || model.renderable.getInterpolateScalarsBeforeMapping()) && model.renderable.getColorCoordinates() && !model.drawingEdges) {
151
151
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', colorImpl.concat([' vec4 texColor = texture2D(texture1, tcoordVCVSOutput.st);', ' diffuseColor = texColor.rgb;', ' ambientColor = texColor.rgb;', ' opacity = opacity*texColor.a;'])).result;
152
152
  } else {
153
153
  if (actor.getBackfaceProperty() && !model.drawingEdges) {
@@ -993,9 +993,12 @@ function vtkOpenGLPolyDataMapper(publicAPI, model) {
993
993
  tcoords = null;
994
994
  }
995
995
 
996
+ // Flag to check if tcoords are per cell instead of per point
997
+ let useTCoordsPerCell = false;
996
998
  // handle color mapping via texture
997
999
  if (model.renderable.getColorCoordinates()) {
998
1000
  tcoords = model.renderable.getColorCoordinates();
1001
+ useTCoordsPerCell = model.renderable.getAreScalarsMappedFromCells();
999
1002
  if (!model.internalColorTexture) {
1000
1003
  model.internalColorTexture = vtkOpenGLTexture.newInstance({
1001
1004
  resizable: true
@@ -1028,6 +1031,7 @@ function vtkOpenGLPolyDataMapper(publicAPI, model) {
1028
1031
  cellOffset: 0,
1029
1032
  vertexOffset: 0,
1030
1033
  // Used to keep track of vertex ids across primitives for selection
1034
+ useTCoordsPerCell,
1031
1035
  haveCellScalars: model.haveCellScalars,
1032
1036
  haveCellNormals: model.haveCellNormals,
1033
1037
  customAttributes: model.renderable.getCustomShaderAttributes().map(arrayName => poly.getPointData().getArrayByName(arrayName))
@@ -1085,9 +1089,9 @@ function vtkOpenGLPolyDataMapper(publicAPI, model) {
1085
1089
  model.renderable.setSelectionWebGLIdsToVTKIds(model.selectionWebGLIdsToVTKIds);
1086
1090
  publicAPI.updateMaximumPointCellIds();
1087
1091
  }
1088
- model.VBOBuildTime.modified();
1089
1092
  model.VBOBuildString = toString;
1090
1093
  }
1094
+ model.VBOBuildTime.modified();
1091
1095
  };
1092
1096
  publicAPI.getAllocatedGPUMemoryInBytes = () => {
1093
1097
  let memUsed = 0;
@@ -0,0 +1,73 @@
1
+ import vtkAbstractWidget from '../../../Widgets/Core/AbstractWidget';
2
+ import vtkCamera from '../../../Rendering/Core/Camera';
3
+ import vtkInteractiveOrientationWidget from '../InteractiveOrientationWidget';
4
+ import vtkOrientationMarkerWidget from '../../../Interaction/Widgets/OrientationMarkerWidget';
5
+ import vtkRenderer from '../../../Rendering/Core/Renderer';
6
+ import vtkRenderWindowInteractor from '../../../Rendering/Core/RenderWindowInteractor';
7
+ import vtkWidgetManager from '../../../Widgets/Core/WidgetManager';
8
+ import { vtkSubscription } from '../../../interfaces';
9
+ import { Bounds, Vector3 } from '../../../types';
10
+
11
+ export function majorAxis(
12
+ vec3: Vector3,
13
+ idxA: number,
14
+ idxB: number
15
+ ): [number, number, number];
16
+
17
+ /**
18
+ * Create a new vtkOrientationMarkerWidget instance from the provided interactor and parentRenderer and sensible defaults.
19
+ *
20
+ * @param {vtkRenderWindowInteractor} interactor
21
+ * @param {vtkRenderer} parentRenderer
22
+ * @returns {vtkOrientationMarkerWidget}
23
+ */
24
+ export function createOrientationMarkerWidget(
25
+ interactor: vtkRenderWindowInteractor,
26
+ parentRenderer: vtkRenderer
27
+ ): vtkOrientationMarkerWidget;
28
+
29
+ /**
30
+ * Create a new vtkInteractiveOrientationWidget instance and place it at the given bounds.
31
+ *
32
+ * @param {Bounds} bounds
33
+ * @returns {vtkInteractiveOrientationWidget}
34
+ */
35
+ export function createInteractiveOrientationWidget(
36
+ bounds: Bounds
37
+ ): vtkInteractiveOrientationWidget;
38
+
39
+ /**
40
+ * Create a new vtkOrientationMarkerWidget alongside with a new vtkInteractiveOrientationWidget with sensible defaults.
41
+ *
42
+ * @param {vtkWidgetManager} widgetManager
43
+ * @param {vtkRenderWindowInteractor} interactor
44
+ * @param {vtkRenderer} mainRenderer
45
+ * @returns {Object} the constructed widget instances
46
+ */
47
+ export function createInteractiveOrientationMarkerWidget(
48
+ widgetManager: vtkWidgetManager,
49
+ interactor: vtkRenderWindowInteractor,
50
+ mainRenderer: vtkRenderer
51
+ ): {
52
+ interactiveOrientationWidget: vtkInteractiveOrientationWidget;
53
+ orientationMarkerWidget: vtkOrientationMarkerWidget;
54
+ };
55
+
56
+ /**
57
+ * Listen to OrientationChange events on the given view widget.
58
+ * The event handler will align the provided camera and update the provided vtkOrientationMarkerWidget instance.
59
+ *
60
+ * @param {vtkAbstractWidget} viewWidget Must be a vtkInteractiveOrientationWidget view widget
61
+ * @param {vtkCamera} camera The camera instance to upate when orientation changes
62
+ * @param {vtkOrientationMarkerWidget} orientationMarkerWidget The instance to update when orientation changes
63
+ * @param {vtkWidgetManager} widgetManager
64
+ * @param {Function} render A callback that should render the view
65
+ * @returns {vtkSubscription} the corresponding event subscription, can be used to unsubscribe from the event
66
+ */
67
+ export function alignCameraOnViewWidgetOrientationChange(
68
+ viewWidget: vtkAbstractWidget,
69
+ camera: vtkCamera,
70
+ orientationMarkerWidget: vtkOrientationMarkerWidget,
71
+ widgetManager: vtkWidgetManager,
72
+ render: () => void
73
+ ): vtkSubscription;
@@ -0,0 +1,71 @@
1
+ import * as vtkMath from '@kitware/vtk.js/Common/Core/Math';
2
+ import vtkOrientationMarkerWidget from '@kitware/vtk.js/Interaction/Widgets/OrientationMarkerWidget';
3
+ import vtkAxesActor from '@kitware/vtk.js/Rendering/Core/AxesActor';
4
+ import vtkInteractiveOrientationWidget from '@kitware/vtk.js/Widgets/Widgets3D/InteractiveOrientationWidget';
5
+
6
+ function majorAxis(vec3, idxA, idxB) {
7
+ const axis = [0, 0, 0];
8
+ const idx = Math.abs(vec3[idxA]) > Math.abs(vec3[idxB]) ? idxA : idxB;
9
+ const value = vec3[idx] > 0 ? 1 : -1;
10
+ axis[idx] = value;
11
+ return axis;
12
+ }
13
+ function createOrientationMarkerWidget(interactor, parentRenderer) {
14
+ const axes = vtkAxesActor.newInstance();
15
+ const orientationWidget = vtkOrientationMarkerWidget.newInstance({
16
+ actor: axes,
17
+ interactor,
18
+ interactiveRenderer: true,
19
+ viewportSize: 0.1,
20
+ minPixelSize: 100,
21
+ maxPixelSize: 300,
22
+ parentRenderer
23
+ });
24
+ orientationWidget.setEnabled(true);
25
+ orientationWidget.setViewportCorner(vtkOrientationMarkerWidget.Corners.BOTTOM_LEFT);
26
+ return orientationWidget;
27
+ }
28
+ function createInteractiveOrientationWidget(bounds) {
29
+ const widget = vtkInteractiveOrientationWidget.newInstance();
30
+ widget.placeWidget(bounds);
31
+ widget.setBounds(bounds.map(v => v * 0.45));
32
+ return widget;
33
+ }
34
+ function createInteractiveOrientationMarkerWidget(widgetManager, interactor, mainRenderer) {
35
+ const orientationMarkerWidget = createOrientationMarkerWidget(interactor, mainRenderer);
36
+ interactor.getInteractorStyle().setFocusedRenderer(mainRenderer);
37
+ widgetManager.setRenderer(orientationMarkerWidget.getRenderer());
38
+ const widget = createInteractiveOrientationWidget(orientationMarkerWidget.getActor().getBounds());
39
+ return {
40
+ interactiveOrientationWidget: widget,
41
+ orientationMarkerWidget
42
+ };
43
+ }
44
+ function alignCameraOnViewWidgetOrientationChange(viewWidget, camera, orientationMarkerWidget, widgetManager, render) {
45
+ return viewWidget.onOrientationChange(_ref => {
46
+ let {
47
+ up,
48
+ direction,
49
+ action,
50
+ event
51
+ } = _ref;
52
+ const focalPoint = camera.getFocalPoint();
53
+ const position = camera.getPosition();
54
+ const viewUp = camera.getViewUp();
55
+ const distance = Math.sqrt(vtkMath.distance2BetweenPoints(position, focalPoint));
56
+ camera.setPosition(focalPoint[0] + direction[0] * distance, focalPoint[1] + direction[1] * distance, focalPoint[2] + direction[2] * distance);
57
+ if (direction[0]) {
58
+ camera.setViewUp(majorAxis(viewUp, 1, 2));
59
+ }
60
+ if (direction[1]) {
61
+ camera.setViewUp(majorAxis(viewUp, 0, 2));
62
+ }
63
+ if (direction[2]) {
64
+ camera.setViewUp(majorAxis(viewUp, 0, 1));
65
+ }
66
+ orientationMarkerWidget.updateMarkerOrientation();
67
+ render();
68
+ });
69
+ }
70
+
71
+ export { alignCameraOnViewWidgetOrientationChange, createInteractiveOrientationMarkerWidget, createInteractiveOrientationWidget, createOrientationMarkerWidget, majorAxis };
package/index.d.ts CHANGED
@@ -234,6 +234,7 @@
234
234
  /// <reference path="./Widgets/Manipulators/PlaneManipulator.d.ts" />
235
235
  /// <reference path="./Widgets/Manipulators/TrackballManipulator.d.ts" />
236
236
  /// <reference path="./Widgets/Representations/WidgetRepresentation.d.ts" />
237
+ /// <reference path="./Widgets/Widgets3D/InteractiveOrientationWidget/helpers.d.ts" />
237
238
  /// <reference path="./Widgets/Widgets3D/InteractiveOrientationWidget.d.ts" />
238
239
  /// <reference path="./Widgets/Widgets3D/ResliceCursorWidget/Constants.d.ts" />
239
240
  /// <reference path="./Widgets/Widgets3D/ResliceCursorWidget/behavior.d.ts" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "30.7.1",
3
+ "version": "30.9.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",