@kitware/vtk.js 30.5.3 → 30.6.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.
@@ -49,11 +49,37 @@ export interface vtkAbstractManipulator extends vtkObject {
49
49
  setUseCameraNormal(useCameraNormal: boolean): boolean;
50
50
 
51
51
  /**
52
- *
52
+ * Processes a vtkRenderWindowInteractor event into 3D world positional info.
53
+ *
54
+ * Returns an object containing:
55
+ * - worldCoords: a 3D coordinate corresponding to the 2D event.
56
+ * - worldDelta: a 3D position delta between the current and the previous call to handleEvent.
57
+ * - worldDirection: a 3D directional vector. Only on select manipulators.
58
+ *
59
+ * worldCoords can be null if the pointer event enters an invalid manipulator region. For example,
60
+ * the PickerManipulator returns null when the pointer event is off of the picked geometry.
61
+ *
62
+ * worldDelta only makes sense between two calls of `handleEvent`. In a queue of `handleEvent` calls,
63
+ * the i-th call returns the delta between the i-th worldCoords and the (i-1)-th worldCoords.
64
+ * Thus, calling `handleEvent` is necessary for maintaining a valid worldDelta even when the return
65
+ * value is ignored.
66
+ *
67
+ * There are three cases where worldDelta needs to handle null events:
68
+ * 1. the first call to `handleEvent`, since there is no previously cached event position.
69
+ * worldDelta is set to [0, 0, 0].
70
+ * 2. if the current `handleEvent` call returns a null worldCoords. worldDelta is set to [0, 0, 0].
71
+ * 3. if the previous `handleEvent` call returned a null worldCoords. In this case, worldDelta is the
72
+ * delta between the current worldCoords and the previous non-null worldCoords, referring to the
73
+ * previous 2 cases when applicable.
74
+ *
53
75
  * @param callData
54
76
  * @param glRenderWindow
55
77
  */
56
- handleEvent(callData: any, glRenderWindow: vtkOpenGLRenderWindow): { worldCoords: Nullable<Vector3>, worldDirection?: Matrix3x3 };
78
+ handleEvent(callData: any, glRenderWindow: vtkOpenGLRenderWindow): {
79
+ worldCoords: Nullable<Vector3>,
80
+ worldDelta: Vector3,
81
+ worldDirection?: Matrix3x3,
82
+ };
57
83
 
58
84
  /* ------------------------------------------------------------------- */
59
85
 
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../macros2.js';
2
+ import { s as subtract } from '../../Common/Core/Math/index.js';
2
3
 
3
4
  // ----------------------------------------------------------------------------
4
5
  // vtkAbstractManipulator methods
@@ -7,6 +8,7 @@ import { m as macro } from '../../macros2.js';
7
8
  function vtkAbstractManipulator(publicAPI, model) {
8
9
  // Set our className
9
10
  model.classHierarchy.push('vtkAbstractManipulator');
11
+ model._prevWorldCoords = [];
10
12
  publicAPI.getOrigin = callData => {
11
13
  if (model.userOrigin) return model.userOrigin;
12
14
  if (model.useCameraFocalPoint) return callData.pokedRenderer.getActiveCamera().getFocalPoint();
@@ -21,6 +23,24 @@ function vtkAbstractManipulator(publicAPI, model) {
21
23
  if (model.widgetNormal) return model.widgetNormal;
22
24
  return [0, 0, 1];
23
25
  };
26
+ model._computeDeltaFromPrevCoords = curWorldCoords => {
27
+ if (!model._prevWorldCoords?.length || !curWorldCoords?.length) return [0, 0, 0];
28
+ return subtract(curWorldCoords, model._prevWorldCoords, []);
29
+ };
30
+ model._addWorldDeltas = manipulatorResults => {
31
+ const {
32
+ worldCoords: curWorldCoords
33
+ } = manipulatorResults;
34
+ const worldDelta = model._computeDeltaFromPrevCoords(curWorldCoords);
35
+ if (curWorldCoords) model._prevWorldCoords = curWorldCoords;
36
+ const deltas = {
37
+ worldDelta
38
+ };
39
+ return {
40
+ ...manipulatorResults,
41
+ ...deltas
42
+ };
43
+ };
24
44
  }
25
45
 
26
46
  // ----------------------------------------------------------------------------
@@ -19,9 +19,9 @@ function vtkCPRManipulator(publicAPI, model) {
19
19
  publicAPI.handleEvent = (callData, glRenderWindow) => {
20
20
  const mapper = model.cprActor?.getMapper();
21
21
  if (!mapper) {
22
- return {
22
+ return model._addWorldDeltas({
23
23
  worldCoords: null
24
- };
24
+ });
25
25
  }
26
26
 
27
27
  // Get normal and origin of the picking plane from the actor matrix
@@ -38,7 +38,7 @@ function vtkCPRManipulator(publicAPI, model) {
38
38
  vec3.transformMat4(modelPlanePicking, worldPlanePicking, inversecprActorMatrix);
39
39
  const height = mapper.getHeight();
40
40
  const distance = height - modelPlanePicking[1];
41
- return publicAPI.distanceEvent(distance);
41
+ return model._addWorldDeltas(publicAPI.distanceEvent(distance));
42
42
  };
43
43
  publicAPI.distanceEvent = distance => {
44
44
  const mapper = model.cprActor?.getMapper();
@@ -36,7 +36,7 @@ function projectDisplayToLine(x, y, lineOrigin, lineDirection, renderer, glRende
36
36
  function vtkLineManipulator(publicAPI, model) {
37
37
  // Set our className
38
38
  model.classHierarchy.push('vtkLineManipulator');
39
- publicAPI.handleEvent = (callData, glRenderWindow) => ({
39
+ publicAPI.handleEvent = (callData, glRenderWindow) => model._addWorldDeltas({
40
40
  worldCoords: projectDisplayToLine(callData.position.x, callData.position.y, publicAPI.getOrigin(callData), publicAPI.getNormal(callData), callData.pokedRenderer, glRenderWindow)
41
41
  });
42
42
  }
@@ -20,9 +20,9 @@ function vtkPickerManipulator(publicAPI, model) {
20
20
  } else {
21
21
  model.position = null;
22
22
  }
23
- return {
23
+ return model._addWorldDeltas({
24
24
  worldCoords: model.position
25
- };
25
+ });
26
26
  };
27
27
  }
28
28
 
@@ -15,7 +15,7 @@ function intersectDisplayWithPlane(x, y, planeOrigin, planeNormal, renderer, glR
15
15
  function vtkPlaneManipulator(publicAPI, model) {
16
16
  // Set our className
17
17
  model.classHierarchy.push('vtkPlaneManipulator');
18
- publicAPI.handleEvent = (callData, glRenderWindow) => ({
18
+ publicAPI.handleEvent = (callData, glRenderWindow) => model._addWorldDeltas({
19
19
  worldCoords: intersectDisplayWithPlane(callData.position.x, callData.position.y, publicAPI.getOrigin(callData), publicAPI.getNormal(callData), callData.pokedRenderer, glRenderWindow)
20
20
  });
21
21
  }
@@ -36,9 +36,9 @@ function vtkTrackballManipulator(publicAPI, model) {
36
36
  const newDirection = trackballRotate(prevX, prevY, callData.position.x, callData.position.y, publicAPI.getOrigin(callData), publicAPI.getNormal(callData), callData.pokedRenderer);
37
37
  prevX = callData.position.x;
38
38
  prevY = callData.position.y;
39
- return {
39
+ return model._addWorldDeltas({
40
40
  worldCoords: newDirection
41
- };
41
+ });
42
42
  };
43
43
  publicAPI.reset = callData => {
44
44
  prevX = callData.position.x;
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
  import vtkPointPicker from '../../../Rendering/Core/PointPicker.js';
3
4
 
4
5
  const MAX_POINTS = 3;
@@ -33,10 +34,10 @@ function widgetBehavior(publicAPI, model) {
33
34
  picker.initializePickList();
34
35
  picker.setPickList(publicAPI.getNestedProps());
35
36
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
37
+ const {
38
+ worldCoords
39
+ } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
36
40
  if (model.activeState === model.widgetState.getMoveHandle() && model.widgetState.getHandleList().length < MAX_POINTS && manipulator) {
37
- const {
38
- worldCoords
39
- } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
40
41
  // Commit handle to location
41
42
  const moveHandle = model.widgetState.getMoveHandle();
42
43
  moveHandle.setOrigin(...worldCoords);
@@ -62,11 +63,17 @@ function widgetBehavior(publicAPI, model) {
62
63
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
63
64
  if (manipulator && model.pickable && model.dragable && model.activeState && model.activeState.getActive() && !ignoreKey(callData)) {
64
65
  const {
65
- worldCoords
66
+ worldCoords,
67
+ worldDelta
66
68
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
67
- if (worldCoords.length && (model.activeState === model.widgetState.getMoveHandle() || model._isDragging) && model.activeState.setOrigin // e.g. the line is pickable but not draggable
68
- ) {
69
- model.activeState.setOrigin(worldCoords);
69
+ const isHandleMoving = model.activeState === model.widgetState.getMoveHandle() || model._isDragging;
70
+ if (isHandleMoving && worldCoords.length && model.activeState.setOrigin) {
71
+ const curOrigin = model.activeState.getOrigin();
72
+ if (curOrigin) {
73
+ model.activeState.setOrigin(add(curOrigin, worldDelta, []));
74
+ } else {
75
+ model.activeState.setOrigin(worldCoords);
76
+ }
70
77
  publicAPI.invokeInteractionEvent();
71
78
  return macro.EVENT_ABORT;
72
79
  }
@@ -1,14 +1,17 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
  import { handleTypeFromName, AXES, calculateCropperCenter, calculateDirection, transformVec3 } from './helpers.js';
3
4
 
4
5
  function widgetBehavior(publicAPI, model) {
5
6
  model._isDragging = false;
6
7
  publicAPI.setDisplayCallback = callback => model.representations[0].setDisplayCallback(callback);
7
- publicAPI.handleLeftButtonPress = () => {
8
+ publicAPI.handleLeftButtonPress = callData => {
8
9
  if (!model.activeState || !model.activeState.getActive() || !model.pickable) {
9
10
  return macro.VOID;
10
11
  }
11
12
  if (model.dragable) {
13
+ // updates worldDelta
14
+ model.activeState.getManipulator().handleEvent(callData, model._apiSpecificRenderWindow);
12
15
  model._isDragging = true;
13
16
  model._apiSpecificRenderWindow.setCursor('grabbing');
14
17
  model._interactor.requestAnimation(publicAPI);
@@ -42,16 +45,23 @@ function widgetBehavior(publicAPI, model) {
42
45
  const planes = model.widgetState.getCroppingPlanes().getPlanes();
43
46
  const indexToWorldT = model.widgetState.getIndexToWorldT();
44
47
  let worldCoords = [];
48
+ let worldDelta = [];
45
49
  if (type === 'corners') {
46
50
  // manipulator should be a plane manipulator
47
- worldCoords = manipulator.handleEvent(callData, model._apiSpecificRenderWindow).worldCoords;
51
+ ({
52
+ worldCoords,
53
+ worldDelta
54
+ } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow));
48
55
  }
49
56
  if (type === 'faces') {
50
57
  // get center of current crop box
51
58
  const worldCenter = calculateCropperCenter(planes, indexToWorldT);
52
59
  manipulator.setHandleOrigin(worldCenter);
53
60
  manipulator.setHandleNormal(calculateDirection(model.activeState.getOrigin(), worldCenter));
54
- worldCoords = manipulator.handleEvent(callData, model._apiSpecificRenderWindow).worldCoords;
61
+ ({
62
+ worldCoords,
63
+ worldDelta
64
+ } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow));
55
65
  }
56
66
  if (type === 'edges') {
57
67
  // constrain to a plane with a normal parallel to the edge
@@ -61,9 +71,12 @@ function widgetBehavior(publicAPI, model) {
61
71
  // get center of current crop box
62
72
  const worldCenter = calculateCropperCenter(planes, indexToWorldT);
63
73
  manipulator.setHandleNormal(calculateDirection(handle.getOrigin(), worldCenter));
64
- worldCoords = manipulator.handleEvent(callData, model._apiSpecificRenderWindow).worldCoords;
74
+ ({
75
+ worldCoords,
76
+ worldDelta
77
+ } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow));
65
78
  }
66
- if (worldCoords.length) {
79
+ if (worldCoords.length && worldDelta.length) {
67
80
  // transform worldCoords to indexCoords, and then update the croppingPlanes() state with setPlanes().
68
81
  const worldToIndexT = model.widgetState.getWorldToIndexT();
69
82
  const indexCoords = transformVec3(worldCoords, worldToIndexT);
@@ -74,7 +87,7 @@ function widgetBehavior(publicAPI, model) {
74
87
  planes[i * 2 + 1] = indexCoords[i];
75
88
  }
76
89
  }
77
- model.activeState.setOrigin(...worldCoords);
90
+ model.activeState.setOrigin(add(model.activeState.getOrigin(), worldDelta, []));
78
91
  model.widgetState.getCroppingPlanes().setPlanes(...planes);
79
92
  return macro.EVENT_ABORT;
80
93
  }
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../macros2.js';
2
+ import { k as add } from '../../Common/Core/Math/index.js';
2
3
  import vtkAbstractWidgetFactory from '../Core/AbstractWidgetFactory.js';
3
4
  import vtkImplicitPlaneRepresentation from '../Representations/ImplicitPlaneRepresentation.js';
4
5
  import vtkLineManipulator from '../Manipulators/LineManipulator.js';
@@ -13,6 +14,8 @@ import { ViewTypes } from '../Core/WidgetManager/Constants.js';
13
14
  function widgetBehavior(publicAPI, model) {
14
15
  model.classHierarchy.push('vtkPlaneWidget');
15
16
  model._isDragging = false;
17
+ // used to track the constrained widget position
18
+ model._draggingWidgetOrigin = [0, 0, 0];
16
19
  publicAPI.setDisplayCallback = callback => model.representations[0].setDisplayCallback(callback);
17
20
  publicAPI.updateCursor = () => {
18
21
  switch (model.activeState.getUpdateMethodName()) {
@@ -38,7 +41,11 @@ function widgetBehavior(publicAPI, model) {
38
41
  model.planeManipulator.setWidgetOrigin(model.widgetState.getOrigin());
39
42
  model.trackballManipulator.reset(callData); // setup trackball delta
40
43
 
44
+ // updates worldDelta
45
+ model.lineManipulator.handleEvent(callData, model._apiSpecificRenderWindow);
46
+ model.planeManipulator.handleEvent(callData, model._apiSpecificRenderWindow);
41
47
  if (model.dragable) {
48
+ model._draggingWidgetOrigin = model.widgetState.getOrigin();
42
49
  model._isDragging = true;
43
50
  model._apiSpecificRenderWindow.setCursor('grabbing');
44
51
  model._interactor.requestAnimation(publicAPI);
@@ -80,10 +87,14 @@ function widgetBehavior(publicAPI, model) {
80
87
  publicAPI.updateFromOrigin = callData => {
81
88
  model.planeManipulator.setWidgetNormal(model.widgetState.getNormal());
82
89
  const {
83
- worldCoords
90
+ worldCoords,
91
+ worldDelta
84
92
  } = model.planeManipulator.handleEvent(callData, model._apiSpecificRenderWindow);
85
- if (model.widgetState.containsPoint(worldCoords)) {
86
- model.activeState.setOrigin(worldCoords);
93
+ add(model._draggingWidgetOrigin, worldDelta, model._draggingWidgetOrigin);
94
+
95
+ // test containment of interaction coords
96
+ if (model.widgetState.containsPoint(...worldCoords)) {
97
+ model.activeState.setOrigin(model._draggingWidgetOrigin);
87
98
  }
88
99
  };
89
100
 
@@ -93,10 +104,11 @@ function widgetBehavior(publicAPI, model) {
93
104
  // Move origin along normal axis
94
105
  model.lineManipulator.setWidgetNormal(model.activeState.getNormal());
95
106
  const {
96
- worldCoords
107
+ worldDelta
97
108
  } = model.lineManipulator.handleEvent(callData, model._apiSpecificRenderWindow);
98
- if (model.widgetState.containsPoint(...worldCoords)) {
99
- model.activeState.setOrigin(worldCoords);
109
+ add(model._draggingWidgetOrigin, worldDelta, model._draggingWidgetOrigin);
110
+ if (model.widgetState.containsPoint(...model._draggingWidgetOrigin)) {
111
+ model.activeState.setOrigin(model._draggingWidgetOrigin);
100
112
  }
101
113
  };
102
114
 
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
 
3
4
  function widgetBehavior(publicAPI, model) {
4
5
  model.classHierarchy.push('vtkLabelWidgetProp');
@@ -37,10 +38,10 @@ function widgetBehavior(publicAPI, model) {
37
38
  return macro.VOID;
38
39
  }
39
40
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
41
+ const {
42
+ worldCoords
43
+ } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
40
44
  if (model.activeState === model.widgetState.getMoveHandle() && manipulator) {
41
- const {
42
- worldCoords
43
- } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
44
45
  // Commit handle to location
45
46
  const moveHandle = model.widgetState.getMoveHandle();
46
47
  moveHandle.setOrigin(worldCoords);
@@ -87,14 +88,17 @@ function widgetBehavior(publicAPI, model) {
87
88
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
88
89
  if (manipulator && model.pickable && model.dragable && model.activeState && model.activeState.getActive() && !ignoreKey(callData)) {
89
90
  const {
90
- worldCoords
91
+ worldCoords,
92
+ worldDelta
91
93
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
92
- if (worldCoords.length && (model.activeState === model.widgetState.getMoveHandle() || model._isDragging)) {
93
- model.activeState.setOrigin(worldCoords);
94
- model.widgetState.getText().setOrigin(model.activeState.getOrigin());
95
- publicAPI.invokeInteractionEvent();
96
- return macro.EVENT_ABORT;
97
- }
94
+ const isHandleMoving = model.widgetState.getMoveHandle() === model.activeState || model._isDragging;
95
+ if (!isHandleMoving || !worldCoords.length || !worldDelta.length) return macro.VOID;
96
+ const curOrigin = model.activeState.getOrigin();
97
+ const newOrigin = curOrigin ? add(curOrigin, worldDelta, []) : worldCoords;
98
+ model.activeState.setOrigin(newOrigin);
99
+ model.widgetState.getText().setOrigin(newOrigin);
100
+ publicAPI.invokeInteractionEvent();
101
+ return macro.EVENT_ABORT;
98
102
  }
99
103
  return macro.VOID;
100
104
  };
@@ -1,6 +1,6 @@
1
1
  import Constants from './Constants.js';
2
2
  import { m as macro } from '../../../macros2.js';
3
- import { s as subtract, k as add, l as normalize } from '../../../Common/Core/Math/index.js';
3
+ import { k as add, s as subtract, l as normalize } from '../../../Common/Core/Math/index.js';
4
4
  import { getNumberOfPlacedHandles, isHandlePlaced, calculateTextPosition, updateTextPosition, getPoint } from './helpers.js';
5
5
 
6
6
  const {
@@ -47,7 +47,7 @@ function widgetBehavior(publicAPI, model) {
47
47
  function updateCursor(callData) {
48
48
  model._isDragging = true;
49
49
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
50
- model.previousPosition = manipulator.handleEvent(callData, model._apiSpecificRenderWindow).worldCoords;
50
+ manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
51
51
  model._apiSpecificRenderWindow.setCursor('grabbing');
52
52
  model._interactor.requestAnimation(publicAPI);
53
53
  }
@@ -181,22 +181,32 @@ function widgetBehavior(publicAPI, model) {
181
181
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
182
182
  if (manipulator && model.pickable && model.dragable && model.activeState && model.activeState.getActive() && !ignoreKey(callData)) {
183
183
  const {
184
- worldCoords
184
+ worldCoords,
185
+ worldDelta
185
186
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
186
- const translation = model.previousPosition ? subtract(worldCoords, model.previousPosition, []) : [0, 0, 0];
187
- model.previousPosition = worldCoords;
188
- if (
187
+ const isHandleMoving =
189
188
  // is placing first or second handle
190
189
  model.activeState === model.widgetState.getMoveHandle() ||
191
190
  // is dragging already placed first or second handle
192
- model._isDragging) {
193
- if (model.activeState.setOrigin) {
194
- model.activeState.setOrigin(worldCoords);
191
+ model._isDragging;
192
+
193
+ // the line state doesn't have setOrigin
194
+ const isDraggingLine = !model.activeState.setOrigin;
195
+ if (isHandleMoving) {
196
+ if (!isDraggingLine) {
197
+ const curOrigin = model.activeState.getOrigin();
198
+ if (curOrigin) {
199
+ model.activeState.setOrigin(add(model.activeState.getOrigin(), worldDelta, []));
200
+ } else {
201
+ model.activeState.setOrigin(worldCoords);
202
+ }
195
203
  publicAPI.updateHandleVisibility(publicAPI.getHandleIndex(model.activeState));
196
204
  } else {
197
- // Dragging line
198
- publicAPI.getHandle(0).setOrigin(add(publicAPI.getHandle(0).getOrigin(), translation, []));
199
- publicAPI.getHandle(1).setOrigin(add(publicAPI.getHandle(1).getOrigin(), translation, []));
205
+ // Dragging line; move all handles
206
+ for (let i = 0; i < 2; i++) {
207
+ const handleOrigin = publicAPI.getHandle(i).getOrigin();
208
+ publicAPI.getHandle(i).setOrigin(add(handleOrigin, worldDelta, []));
209
+ }
200
210
  }
201
211
  publicAPI.updateHandleOrientations();
202
212
  updateTextPosition(model);
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
 
3
4
  function widgetBehavior(publicAPI, model) {
4
5
  model.classHierarchy.push('vtkPolyLineWidgetProp');
@@ -23,11 +24,20 @@ function widgetBehavior(publicAPI, model) {
23
24
  return macro.VOID;
24
25
  }
25
26
  const {
26
- worldCoords
27
+ worldCoords,
28
+ worldDelta
27
29
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
28
- if (worldCoords.length && (model.activeState === model.widgetState.getMoveHandle() || model._isDragging) && model.activeState.setOrigin // e.g. the line is pickable but not draggable
29
- ) {
30
- model.activeState.setOrigin(worldCoords);
30
+ const isHandleMoving = model.activeState === model.widgetState.getMoveHandle() || model._isDragging;
31
+
32
+ // the line is pickable but not draggable
33
+ const isPickingLine = !model.activeState.setOrigin;
34
+ if (worldCoords.length && isHandleMoving && !isPickingLine) {
35
+ const curOrigin = model.activeState.getOrigin();
36
+ if (curOrigin) {
37
+ model.activeState.setOrigin(add(curOrigin, worldDelta, []));
38
+ } else {
39
+ model.activeState.setOrigin(worldCoords);
40
+ }
31
41
  publicAPI.invokeInteractionEvent();
32
42
  return macro.EVENT_ABORT;
33
43
  }
@@ -73,6 +83,8 @@ function widgetBehavior(publicAPI, model) {
73
83
  newHandle.setScale1(moveHandle.getScale1());
74
84
  newHandle.setManipulator(manipulator);
75
85
  } else if (model.dragable) {
86
+ // Update worldDelta
87
+ manipulator.handleEvent(e, model._apiSpecificRenderWindow);
76
88
  model._isDragging = true;
77
89
  model._apiSpecificRenderWindow.setCursor('grabbing');
78
90
  model._interactor.requestAnimation(publicAPI);
@@ -53,11 +53,6 @@ function vtkPolyLineWidget(publicAPI, model) {
53
53
  // initialization
54
54
  // --------------------------------------------------------------------------
55
55
 
56
- model.widgetState.onBoundsChange(bounds => {
57
- const center = [(bounds[0] + bounds[1]) * 0.5, (bounds[2] + bounds[3]) * 0.5, (bounds[4] + bounds[5]) * 0.5];
58
- model.widgetState.getMoveHandle().setOrigin(center);
59
- });
60
-
61
56
  // Default manipulator
62
57
  publicAPI.setManipulator(model.manipulator || vtkPlanePointManipulator.newInstance({
63
58
  useCameraFocalPoint: true,
@@ -5,7 +5,6 @@ function widgetBehavior(publicAPI, model) {
5
5
  const moveHandle = model.widgetState.getMoveHandle();
6
6
  moveHandle.setVisible(true);
7
7
  model._isDragging = false;
8
- model.previousPosition = null;
9
8
  function isValidHandle(handle) {
10
9
  return handle === moveHandle;
11
10
  }
@@ -32,7 +31,6 @@ function widgetBehavior(publicAPI, model) {
32
31
  if (model.activeState === moveHandle) {
33
32
  if (!moveHandle.getOrigin() && worldCoords) {
34
33
  moveHandle.setOrigin(worldCoords);
35
- model.previousPosition = [...worldCoords];
36
34
  }
37
35
  }
38
36
  model._isDragging = true;
@@ -46,7 +44,6 @@ function widgetBehavior(publicAPI, model) {
46
44
  return macro.VOID;
47
45
  }
48
46
  if (isPlaced()) {
49
- model.previousPosition = null;
50
47
  model._widgetManager.enablePicking();
51
48
  model._apiSpecificRenderWindow.setCursor('pointer');
52
49
  model._isDragging = false;
@@ -63,10 +60,7 @@ function widgetBehavior(publicAPI, model) {
63
60
  }
64
61
  if (!model.activeState) throw Error('no activestate');
65
62
  const worldCoords = currentWorldCoords(e);
66
- if (worldCoords) {
67
- model.activeState.setOrigin(worldCoords);
68
- model.previousPosition = worldCoords;
69
- }
63
+ model.activeState.setOrigin(worldCoords);
70
64
  return macro.VOID;
71
65
  };
72
66
  publicAPI.grabFocus = () => {
@@ -295,7 +295,8 @@ function widgetBehavior(publicAPI, model) {
295
295
  model.shapeHandle.setDirection(normal);
296
296
  }
297
297
  const {
298
- worldCoords
298
+ worldCoords,
299
+ worldDelta
299
300
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
300
301
  if (!worldCoords.length) {
301
302
  return macro.VOID;
@@ -311,11 +312,11 @@ function widgetBehavior(publicAPI, model) {
311
312
  }
312
313
  } else if (model._isDragging) {
313
314
  if (model.activeState === model.point1Handle) {
314
- model.point1Handle.setOrigin(worldCoords);
315
- model.point1 = worldCoords;
315
+ vtkMath.add(model.point1Handle.getOrigin(), worldDelta, model.point1);
316
+ model.point1Handle.setOrigin(model.point1);
316
317
  } else {
317
- model.point2Handle.setOrigin(worldCoords);
318
- model.point2 = worldCoords;
318
+ vtkMath.add(model.point2Handle.getOrigin(), worldDelta, model.point2);
319
+ model.point2Handle.setOrigin(model.point2);
319
320
  }
320
321
  publicAPI.updateShapeBounds();
321
322
  publicAPI.invokeInteractionEvent();
@@ -332,10 +333,10 @@ function widgetBehavior(publicAPI, model) {
332
333
  if (!model.activeState || !model.activeState.getActive() || !model.pickable || !manipulator) {
333
334
  return macro.VOID;
334
335
  }
336
+ const {
337
+ worldCoords
338
+ } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
335
339
  if (model.hasFocus) {
336
- const {
337
- worldCoords
338
- } = manipulator.handleEvent(e, model._apiSpecificRenderWindow);
339
340
  if (!model.point1) {
340
341
  model.point1Handle.setOrigin(worldCoords);
341
342
  publicAPI.placePoint1(model.point1Handle.getOrigin());
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
  import { vec3 } from 'gl-matrix';
3
4
 
4
5
  function widgetBehavior(publicAPI, model) {
@@ -10,8 +11,6 @@ function widgetBehavior(publicAPI, model) {
10
11
 
11
12
  // Set while moving the center or border handle.
12
13
  model._isDragging = false;
13
- // The last world coordinate of the mouse cursor during dragging.
14
- model.previousPosition = null;
15
14
  model.classHierarchy.push('vtkSphereWidgetProp');
16
15
  moveHandle.setVisible(true);
17
16
  centerHandle.setVisible(false);
@@ -47,7 +46,7 @@ function widgetBehavior(publicAPI, model) {
47
46
  }
48
47
  function currentWorldCoords(e) {
49
48
  const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
50
- return manipulator.handleEvent(e, model._apiSpecificRenderWindow).worldCoords;
49
+ return manipulator.handleEvent(e, model._apiSpecificRenderWindow);
51
50
  }
52
51
 
53
52
  // Update the sphere's center and radius. Example:
@@ -75,19 +74,22 @@ function widgetBehavior(publicAPI, model) {
75
74
  model.activeState = null;
76
75
  return macro.VOID;
77
76
  }
78
- const worldCoords = currentWorldCoords(e);
77
+ const {
78
+ worldCoords
79
+ } = currentWorldCoords(e);
79
80
  if (model.activeState === moveHandle) {
80
81
  // Initial sphere placement.
81
82
  if (!centerHandle.getOrigin()) {
82
83
  centerHandle.setOrigin(worldCoords);
83
84
  } else if (!borderHandle.getOrigin()) {
84
85
  borderHandle.setOrigin(worldCoords);
86
+ publicAPI.loseFocus();
85
87
  }
86
88
  updateSphere();
89
+ return macro.EVENT_ABORT;
87
90
  }
88
91
  model._isDragging = true;
89
92
  model._apiSpecificRenderWindow.setCursor('grabbing');
90
- model.previousPosition = [...currentWorldCoords(e)];
91
93
  publicAPI.invokeStartInteractionEvent();
92
94
  return macro.EVENT_ABORT;
93
95
  };
@@ -97,7 +99,6 @@ function widgetBehavior(publicAPI, model) {
97
99
  return macro.VOID;
98
100
  }
99
101
  if (isPlaced()) {
100
- model.previousPosition = null;
101
102
  model._widgetManager.enablePicking();
102
103
  model._apiSpecificRenderWindow.setCursor('pointer');
103
104
  model._isDragging = false;
@@ -108,43 +109,33 @@ function widgetBehavior(publicAPI, model) {
108
109
  return macro.EVENT_ABORT;
109
110
  };
110
111
  publicAPI.handleMouseMove = e => {
111
- if (!model._isDragging) {
112
- model.activeState = null;
113
- return macro.VOID;
112
+ if (!model.activeState) return macro.VOID;
113
+ const {
114
+ worldCoords,
115
+ worldDelta
116
+ } = currentWorldCoords(e);
117
+ if (model.hasFocus) {
118
+ model.activeState.setOrigin(worldCoords);
119
+ } else if (model._isDragging) {
120
+ model.activeState.setOrigin(add(model.activeState.getOrigin(), worldDelta, []));
114
121
  }
115
- if (!model.activeState) throw Error('no activestate');
116
- const worldCoords = currentWorldCoords(e);
117
- model.activeState.setOrigin(worldCoords);
118
- if (model.activeState === centerHandle) {
119
- // When the sphere is fully placed, and the user is moving the
120
- // center, we move the whole sphere.
121
- if (borderHandle.getOrigin()) {
122
- if (!model.previousPosition) {
123
- // !previousPosition here happens only immediately
124
- // after grabFocus, but grabFocus resets
125
- // borderHandle.origin.
126
- throw Error(`no pos ${model.activeState} ${model.previousPosition}`);
127
- }
128
- const translation = vec3.sub(vec3.create(), worldCoords, model.previousPosition);
129
- borderHandle.setOrigin(vec3.add(vec3.create(), borderHandle.getOrigin(), translation));
130
- }
131
- }
132
- model.previousPosition = worldCoords;
133
122
  updateSphere();
134
123
  return macro.VOID;
135
124
  };
125
+ const superGrabFocus = publicAPI.grabFocus;
136
126
  publicAPI.grabFocus = () => {
127
+ superGrabFocus();
137
128
  moveHandle.setVisible(true);
138
129
  centerHandle.setVisible(false);
139
130
  borderHandle.setVisible(false);
140
131
  centerHandle.setOrigin(null);
141
132
  borderHandle.setOrigin(null);
142
- model._isDragging = true;
143
133
  model.activeState = moveHandle;
144
134
  model._interactor.render();
145
135
  };
136
+ const superLoseFocus = publicAPI.loseFocus;
146
137
  publicAPI.loseFocus = () => {
147
- model._isDragging = false;
138
+ superLoseFocus();
148
139
  model.activeState = null;
149
140
  };
150
141
  }
@@ -1,4 +1,5 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
+ import { k as add } from '../../../Common/Core/Math/index.js';
2
3
  import { vec3 } from 'gl-matrix';
3
4
 
4
5
  function widgetBehavior(publicAPI, model) {
@@ -153,9 +154,13 @@ function widgetBehavior(publicAPI, model) {
153
154
  // --------------------------------------------------------------------------
154
155
 
155
156
  publicAPI.handleLeftButtonPress = e => {
156
- if (!model.activeState || !model.activeState.getActive() || !model.pickable) {
157
+ const manipulator = model.activeState?.getManipulator?.() ?? model.manipulator;
158
+ if (!manipulator || !model.activeState || !model.activeState.getActive() || !model.pickable) {
157
159
  return macro.VOID;
158
160
  }
161
+
162
+ // update worldDelta
163
+ manipulator.handleEvent(e, model._apiSpecificRenderWindow);
159
164
  if (model.activeState === model.moveHandle) {
160
165
  if (model.widgetState.getHandleList().length === 0) {
161
166
  addPoint();
@@ -230,7 +235,8 @@ function widgetBehavior(publicAPI, model) {
230
235
  return macro.VOID;
231
236
  }
232
237
  const {
233
- worldCoords
238
+ worldCoords,
239
+ worldDelta
234
240
  } = manipulator.handleEvent(callData, model._apiSpecificRenderWindow);
235
241
  const hoveredHandle = getHoveredHandle();
236
242
  if (hoveredHandle) {
@@ -245,8 +251,14 @@ function widgetBehavior(publicAPI, model) {
245
251
  if (model.lastHandle) {
246
252
  model.lastHandle.setVisible(true);
247
253
  }
248
- if (worldCoords.length && (model._isDragging || model.activeState === model.moveHandle)) {
249
- model.activeState.setOrigin(worldCoords);
254
+ const isHandleMoving = model._isDragging || model.activeState === model.moveHandle;
255
+ if (worldCoords.length && isHandleMoving) {
256
+ const curOrigin = model.activeState.getOrigin();
257
+ if (curOrigin) {
258
+ model.activeState.setOrigin(add(curOrigin, worldDelta, []));
259
+ } else {
260
+ model.activeState.setOrigin(worldCoords);
261
+ }
250
262
  if (model._isDragging) {
251
263
  model.draggedPoint = true;
252
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "30.5.3",
3
+ "version": "30.6.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",