three-gamepad-controls 0.19.0 → 0.21.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.
- package/README.md +3 -0
- package/dist/gamepad-arcball-controls.js +13 -12
- package/dist/gamepad-drag-controls.js +15 -16
- package/dist/gamepad-stick-processing.d.ts +81 -45
- package/dist/gamepad-stick-processing.js +144 -105
- package/dist/gamepad-transform-controls.js +29 -34
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Gamepad support for [Three.js](https://threejs.org) controls, built on top of [Web Gamepad API](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API).
|
|
4
4
|
|
|
5
|
+
> [!WARNING]
|
|
6
|
+
> This library has not reached version 1.0.0 and should not yet be considered stable. Until 1.0.0, minor releases may include breaking changes.
|
|
7
|
+
|
|
5
8
|
## Architecture
|
|
6
9
|
|
|
7
10
|

|
|
@@ -154,9 +154,9 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
154
154
|
const controls = this.#controls;
|
|
155
155
|
controls.updateMatrixState();
|
|
156
156
|
this.#previousUp.copy(controls.object.up);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return
|
|
157
|
+
this.#applyTransform(controls.rotate(axis, angle));
|
|
158
|
+
controls.object.up.copy(this.#previousUp).applyAxisAngle(axis, -angle);
|
|
159
|
+
return true;
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Applies gamepad pan by converting stick input to Arcball trackball points.
|
|
@@ -174,7 +174,8 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
174
174
|
controls.updateMatrixState();
|
|
175
175
|
this.#panStart.set(0, 0, 0);
|
|
176
176
|
this.#panEnd.set(panX * distance, panY * distance, 0);
|
|
177
|
-
|
|
177
|
+
this.#applyTransform(controls.pan(this.#panStart, this.#panEnd));
|
|
178
|
+
return true;
|
|
178
179
|
}
|
|
179
180
|
/**
|
|
180
181
|
* Applies trigger-driven zoom around Arcball's gizmo center.
|
|
@@ -191,7 +192,10 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
191
192
|
const size = controls.scaleFactor ** (zoom * zoomSpeed * deltaTime * ZOOM_NOTCHES_PER_SECOND);
|
|
192
193
|
if (!Number.isFinite(size) || size <= 0 || size === 1) return false;
|
|
193
194
|
controls.updateMatrixState();
|
|
194
|
-
|
|
195
|
+
const transformation = controls.scale(size, controls._gizmos.position);
|
|
196
|
+
if (transformation === void 0) return false;
|
|
197
|
+
this.#applyTransform(transformation);
|
|
198
|
+
return true;
|
|
195
199
|
}
|
|
196
200
|
/**
|
|
197
201
|
* Applies shoulder-button rotation around the current camera view axis.
|
|
@@ -209,9 +213,9 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
209
213
|
controls.updateMatrixState();
|
|
210
214
|
controls.object.getWorldDirection(controls._rotationAxis);
|
|
211
215
|
this.#previousUp.copy(controls.object.up);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return
|
|
216
|
+
this.#applyTransform(controls.zRotate(controls._gizmos.position, angle));
|
|
217
|
+
controls.object.up.copy(this.#previousUp).applyAxisAngle(controls._rotationAxis, angle);
|
|
218
|
+
return true;
|
|
215
219
|
}
|
|
216
220
|
/**
|
|
217
221
|
* Focuses ArcballControls on the given point when one was consumed.
|
|
@@ -230,14 +234,11 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
230
234
|
/**
|
|
231
235
|
* Applies a transformation returned by an Arcball runtime helper.
|
|
232
236
|
*
|
|
233
|
-
* @param transformation - Arcball transformation matrices
|
|
234
|
-
* @returns `true` when a transformation was applied.
|
|
237
|
+
* @param transformation - Arcball transformation matrices to apply.
|
|
235
238
|
*/
|
|
236
239
|
#applyTransform(transformation) {
|
|
237
|
-
if (transformation === void 0) return false;
|
|
238
240
|
this.#controls.applyTransformMatrix(transformation);
|
|
239
241
|
this.#controls.updateMatrixState();
|
|
240
|
-
return true;
|
|
241
242
|
}
|
|
242
243
|
/**
|
|
243
244
|
* Consumes a focus-button press and resolves the viewport center hit point.
|
|
@@ -80,12 +80,13 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
80
80
|
this.#clearHover();
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
const selected = this.#selected;
|
|
84
|
+
if (selected !== null) {
|
|
84
85
|
if (selectStarted) {
|
|
85
86
|
this.#releaseSelected();
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
|
-
this.#updateSelected(deltaTime);
|
|
89
|
+
this.#updateSelected(selected, deltaTime);
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
92
|
const hit = this.#intersectCenter();
|
|
@@ -114,17 +115,16 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
114
115
|
/**
|
|
115
116
|
* Updates the selected object from gamepad drag and rotation input.
|
|
116
117
|
*
|
|
118
|
+
* @param selected - Object selected for the current update.
|
|
117
119
|
* @param deltaTime - Seconds since the last frame.
|
|
118
120
|
*/
|
|
119
|
-
#updateSelected(deltaTime) {
|
|
120
|
-
const selected = this.#selected;
|
|
121
|
-
if (selected === null) return;
|
|
121
|
+
#updateSelected(selected, deltaTime) {
|
|
122
122
|
const { dragSpeed, rotateSpeed, dragStick, rotateStick } = this.#options;
|
|
123
123
|
const input = this.gamepadInput;
|
|
124
124
|
const drag = input.stick(dragStick.xAxis, dragStick.yAxis, dragStick.pipeline);
|
|
125
125
|
const rotate = input.stick(rotateStick.xAxis, rotateStick.yAxis, rotateStick.pipeline);
|
|
126
|
-
const dragged = this.#applyDrag(deltaTime, drag.x, drag.y, dragSpeed);
|
|
127
|
-
const rotated = this.#applyRotation(deltaTime, rotate.x, rotate.y, rotateSpeed);
|
|
126
|
+
const dragged = this.#applyDrag(selected, deltaTime, drag.x, drag.y, dragSpeed);
|
|
127
|
+
const rotated = this.#applyRotation(selected, deltaTime, rotate.x, rotate.y, rotateSpeed);
|
|
128
128
|
if (dragged || rotated) this.#controls.dispatchEvent({
|
|
129
129
|
type: "drag",
|
|
130
130
|
object: selected
|
|
@@ -133,34 +133,35 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
133
133
|
/**
|
|
134
134
|
* Moves the selected object in the camera-facing plane.
|
|
135
135
|
*
|
|
136
|
+
* @param selected - Object selected for the current update.
|
|
136
137
|
* @param deltaTime - Seconds since the last frame.
|
|
137
138
|
* @param dragX - Horizontal drag input after dead zone processing.
|
|
138
139
|
* @param dragY - Vertical drag input after dead zone processing.
|
|
139
140
|
* @param dragSpeed - User-configured drag speed multiplier.
|
|
140
141
|
* @returns `true` when the selected object moved.
|
|
141
142
|
*/
|
|
142
|
-
#applyDrag(deltaTime, dragX, dragY, dragSpeed) {
|
|
143
|
-
if (
|
|
143
|
+
#applyDrag(selected, deltaTime, dragX, dragY, dragSpeed) {
|
|
144
|
+
if (dragX === 0 && dragY === 0) return false;
|
|
144
145
|
this.#updateCameraAxes();
|
|
145
146
|
this.#updateViewSizeAtSelectedDepth();
|
|
146
147
|
const scale = dragSpeed * deltaTime;
|
|
147
148
|
this.#selectedWorldPosition.addScaledVector(this.#cameraRight, dragX * this.#viewSize.x * scale);
|
|
148
149
|
this.#selectedWorldPosition.addScaledVector(this.#cameraUp, -dragY * this.#viewSize.y * scale);
|
|
149
|
-
this.#applySelectedWorldPosition();
|
|
150
|
+
this.#applySelectedWorldPosition(selected);
|
|
150
151
|
return true;
|
|
151
152
|
}
|
|
152
153
|
/**
|
|
153
154
|
* Rotates the selected object around camera-relative world axes.
|
|
154
155
|
*
|
|
156
|
+
* @param selected - Object selected for the current update.
|
|
155
157
|
* @param deltaTime - Seconds since the last frame.
|
|
156
158
|
* @param rotateX - Horizontal rotation input after dead zone processing.
|
|
157
159
|
* @param rotateY - Vertical rotation input after dead zone processing.
|
|
158
160
|
* @param rotateSpeed - User-configured rotation speed multiplier.
|
|
159
161
|
* @returns `true` when the selected object rotated.
|
|
160
162
|
*/
|
|
161
|
-
#applyRotation(deltaTime, rotateX, rotateY, rotateSpeed) {
|
|
162
|
-
|
|
163
|
-
if (selected === null || rotateX === 0 && rotateY === 0) return false;
|
|
163
|
+
#applyRotation(selected, deltaTime, rotateX, rotateY, rotateSpeed) {
|
|
164
|
+
if (rotateX === 0 && rotateY === 0) return false;
|
|
164
165
|
this.#updateCameraAxes();
|
|
165
166
|
const scale = this.#controls.rotateSpeed * rotateSpeed * deltaTime * Math.PI;
|
|
166
167
|
if (rotateX !== 0) selected.rotateOnWorldAxis(this.#cameraUp, rotateX * scale);
|
|
@@ -252,9 +253,7 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
252
253
|
}
|
|
253
254
|
return group;
|
|
254
255
|
}
|
|
255
|
-
#applySelectedWorldPosition() {
|
|
256
|
-
const selected = this.#selected;
|
|
257
|
-
if (selected === null) return;
|
|
256
|
+
#applySelectedWorldPosition(selected) {
|
|
258
257
|
if (selected.parent === null) {
|
|
259
258
|
selected.position.copy(this.#selectedWorldPosition);
|
|
260
259
|
selected.updateMatrixWorld();
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
//#region src/gamepad-stick-processing.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Geometry used when processing two-dimensional stick values.
|
|
4
|
+
*
|
|
5
|
+
* Axial processing transforms each component independently. Radial processing
|
|
6
|
+
* transforms vector magnitude while preserving direction.
|
|
7
|
+
*/
|
|
8
|
+
type GamepadStickProcessingMode = "axial" | "radial";
|
|
9
|
+
/**
|
|
10
|
+
* Built-in response curve applied to stick components or magnitude.
|
|
11
|
+
*/
|
|
12
|
+
type GamepadResponseCurve = "linear" | "quadratic" | "cubic";
|
|
13
|
+
/**
|
|
14
|
+
* Stick component selection used by inversion processing.
|
|
4
15
|
*/
|
|
5
|
-
type
|
|
16
|
+
type GamepadStickInversionAxis = "x" | "y" | "both";
|
|
6
17
|
/**
|
|
7
18
|
* Two-dimensional gamepad stick value.
|
|
8
19
|
*/
|
|
@@ -16,6 +27,10 @@ type GamepadStick = {
|
|
|
16
27
|
*/
|
|
17
28
|
y: number;
|
|
18
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Pure custom transformation applied to a gamepad stick.
|
|
32
|
+
*/
|
|
33
|
+
type GamepadStickTransform = (value: Readonly<GamepadStick>) => GamepadStick;
|
|
19
34
|
/**
|
|
20
35
|
* Pure, stateless transformation applied to a gamepad stick.
|
|
21
36
|
*
|
|
@@ -32,28 +47,23 @@ type GamepadStickProcessor = {
|
|
|
32
47
|
process(value: Readonly<GamepadStick>): GamepadStick;
|
|
33
48
|
};
|
|
34
49
|
/**
|
|
35
|
-
*
|
|
50
|
+
* Options for {@link gamepadStickPipeline}.
|
|
36
51
|
*/
|
|
37
|
-
type
|
|
52
|
+
type GamepadStickPipelineOptions = {
|
|
38
53
|
/**
|
|
39
|
-
*
|
|
54
|
+
* Default geometry for processing methods that support axial and radial modes.
|
|
55
|
+
* @default "axial"
|
|
40
56
|
*/
|
|
41
|
-
|
|
57
|
+
mode?: GamepadStickProcessingMode;
|
|
42
58
|
};
|
|
43
59
|
/**
|
|
44
|
-
* Options for {@link
|
|
60
|
+
* Options for {@link GamepadStickPipeline.deadzone}.
|
|
45
61
|
*/
|
|
46
|
-
type
|
|
62
|
+
type GamepadDeadzoneOptions = {
|
|
47
63
|
/**
|
|
48
|
-
*
|
|
49
|
-
* @default 0.1
|
|
64
|
+
* Geometry for this dead zone, overriding the pipeline default.
|
|
50
65
|
*/
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Whether to process each component or the vector magnitude.
|
|
54
|
-
* @default "axial"
|
|
55
|
-
*/
|
|
56
|
-
mode?: GamepadDeadzoneMode;
|
|
66
|
+
mode?: GamepadStickProcessingMode;
|
|
57
67
|
/**
|
|
58
68
|
* Whether to remap values outside the dead zone to the full output range.
|
|
59
69
|
* @default false
|
|
@@ -61,19 +71,58 @@ type GamepadDeadzoneProcessorOptions = {
|
|
|
61
71
|
rescale?: boolean;
|
|
62
72
|
};
|
|
63
73
|
/**
|
|
64
|
-
* Options for {@link
|
|
74
|
+
* Options for {@link GamepadStickPipeline.curve}.
|
|
65
75
|
*/
|
|
66
|
-
type
|
|
76
|
+
type GamepadResponseCurveOptions = {
|
|
67
77
|
/**
|
|
68
|
-
*
|
|
69
|
-
|
|
78
|
+
* Geometry for this response curve, overriding the pipeline default.
|
|
79
|
+
*/
|
|
80
|
+
mode?: GamepadStickProcessingMode;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Ordered, reusable sequence of stateless stick processors.
|
|
84
|
+
*
|
|
85
|
+
* Every configuration method returns a new frozen pipeline and leaves the
|
|
86
|
+
* current pipeline unchanged.
|
|
87
|
+
*/
|
|
88
|
+
type GamepadStickPipeline = GamepadStickProcessor & {
|
|
89
|
+
/**
|
|
90
|
+
* Appends an axial or radial dead zone.
|
|
91
|
+
*
|
|
92
|
+
* @param threshold - Dead zone threshold.
|
|
93
|
+
* @param options - Optional dead zone configuration.
|
|
94
|
+
* @returns New pipeline containing the dead zone.
|
|
70
95
|
*/
|
|
71
|
-
|
|
96
|
+
deadzone(threshold?: number, options?: GamepadDeadzoneOptions): GamepadStickPipeline;
|
|
72
97
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
98
|
+
* Appends an axial or radial response curve.
|
|
99
|
+
*
|
|
100
|
+
* @param curve - Curve used to transform normalized magnitudes.
|
|
101
|
+
* @param options - Optional response curve configuration.
|
|
102
|
+
* @returns New pipeline containing the response curve.
|
|
103
|
+
*/
|
|
104
|
+
curve(curve: GamepadResponseCurve, options?: GamepadResponseCurveOptions): GamepadStickPipeline;
|
|
105
|
+
/**
|
|
106
|
+
* Appends component inversion.
|
|
107
|
+
*
|
|
108
|
+
* @param axis - Component or components to invert.
|
|
109
|
+
* @returns New pipeline containing the inversion.
|
|
110
|
+
*/
|
|
111
|
+
invert(axis: GamepadStickInversionAxis): GamepadStickPipeline;
|
|
112
|
+
/**
|
|
113
|
+
* Appends a custom stick transformation.
|
|
114
|
+
*
|
|
115
|
+
* @param operation - Pure transformation applied to the current value.
|
|
116
|
+
* @returns New pipeline containing the transformation.
|
|
75
117
|
*/
|
|
76
|
-
|
|
118
|
+
transform(operation: GamepadStickTransform): GamepadStickPipeline;
|
|
119
|
+
/**
|
|
120
|
+
* Appends a reusable stick processor, including another pipeline.
|
|
121
|
+
*
|
|
122
|
+
* @param processor - Processor to execute next.
|
|
123
|
+
* @returns New pipeline containing the processor.
|
|
124
|
+
*/
|
|
125
|
+
pipe(processor: GamepadStickProcessor): GamepadStickPipeline;
|
|
77
126
|
};
|
|
78
127
|
/**
|
|
79
128
|
* Partial binding for a two-dimensional stick action.
|
|
@@ -99,29 +148,16 @@ type GamepadStickBindingOptions = {
|
|
|
99
148
|
*/
|
|
100
149
|
type GamepadStickBinding = Required<GamepadStickBindingOptions>;
|
|
101
150
|
/**
|
|
102
|
-
* Creates an
|
|
103
|
-
*
|
|
104
|
-
* The processor list is copied once. Processor errors are not caught, and a
|
|
105
|
-
* processor's result is passed directly to the next processor.
|
|
106
|
-
*
|
|
107
|
-
* @param processors - Processors in execution order.
|
|
108
|
-
* @returns Reusable stick pipeline.
|
|
109
|
-
*/
|
|
110
|
-
declare const createGamepadStickPipeline: (...processors: readonly GamepadStickProcessor[]) => GamepadStickPipeline;
|
|
111
|
-
/**
|
|
112
|
-
* Creates an axial or radial dead zone processor.
|
|
151
|
+
* Creates an immutable, fluent stick processing pipeline.
|
|
113
152
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
|
|
117
|
-
declare const createGamepadDeadzoneProcessor: (options?: GamepadDeadzoneProcessorOptions) => GamepadStickProcessor;
|
|
118
|
-
/**
|
|
119
|
-
* Creates a processor that independently inverts stick components.
|
|
153
|
+
* The configured mode becomes the default for dead zones and response curves.
|
|
154
|
+
* Each fluent method returns a new pipeline, so intermediate pipelines remain
|
|
155
|
+
* reusable. Processor errors are not caught.
|
|
120
156
|
*
|
|
121
|
-
* @param options - Optional
|
|
122
|
-
* @returns
|
|
157
|
+
* @param options - Optional pipeline defaults.
|
|
158
|
+
* @returns Frozen stick pipeline.
|
|
123
159
|
*/
|
|
124
|
-
declare const
|
|
160
|
+
declare const gamepadStickPipeline: (options?: GamepadStickPipelineOptions) => GamepadStickPipeline;
|
|
125
161
|
/**
|
|
126
162
|
* Historical stick processing used when no custom pipeline is supplied.
|
|
127
163
|
*/
|
|
@@ -135,4 +171,4 @@ declare const DEFAULT_GAMEPAD_STICK_PIPELINE: GamepadStickPipeline;
|
|
|
135
171
|
*/
|
|
136
172
|
declare const resolveGamepadStickBinding: (defaults: GamepadStickBinding, options?: GamepadStickBindingOptions) => GamepadStickBinding;
|
|
137
173
|
//#endregion
|
|
138
|
-
export { DEFAULT_GAMEPAD_STICK_PIPELINE,
|
|
174
|
+
export { DEFAULT_GAMEPAD_STICK_PIPELINE, GamepadDeadzoneOptions, GamepadResponseCurve, GamepadResponseCurveOptions, GamepadStick, GamepadStickBinding, GamepadStickBindingOptions, GamepadStickInversionAxis, GamepadStickPipeline, GamepadStickPipelineOptions, GamepadStickProcessingMode, GamepadStickProcessor, GamepadStickTransform, gamepadStickPipeline, resolveGamepadStickBinding };
|
|
@@ -1,12 +1,50 @@
|
|
|
1
1
|
//#region src/gamepad-stick-processing.ts
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
const DEFAULT_GAMEPAD_STICK_PROCESSING_MODE = "axial";
|
|
3
|
+
const DEFAULT_GAMEPAD_DEADZONE_THRESHOLD = .1;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a canonical stick result and preserves the original value when
|
|
6
|
+
* processing produced no change.
|
|
7
|
+
*
|
|
8
|
+
* @param source - Original stick value.
|
|
9
|
+
* @param x - Processed horizontal component.
|
|
10
|
+
* @param y - Processed vertical component.
|
|
11
|
+
* @returns Canonical processed stick value.
|
|
12
|
+
*/
|
|
13
|
+
const createGamepadStickResult = (source, x, y) => {
|
|
14
|
+
const canonicalX = x === 0 ? 0 : x;
|
|
15
|
+
const canonicalY = y === 0 ? 0 : y;
|
|
16
|
+
if (canonicalX === source.x && canonicalY === source.y && !Object.is(source.x, -0) && !Object.is(source.y, -0)) return source;
|
|
17
|
+
return {
|
|
18
|
+
x: canonicalX,
|
|
19
|
+
y: canonicalY
|
|
20
|
+
};
|
|
6
21
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Transforms the magnitude of a signed scalar while preserving its sign.
|
|
24
|
+
*
|
|
25
|
+
* @param value - Signed scalar value.
|
|
26
|
+
* @param mapMagnitude - Magnitude transformation.
|
|
27
|
+
* @returns Transformed signed value.
|
|
28
|
+
*/
|
|
29
|
+
const mapGamepadSignedMagnitude = (value, mapMagnitude) => {
|
|
30
|
+
if (value === 0) return 0;
|
|
31
|
+
return Math.sign(value) * mapMagnitude(Math.abs(value));
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Transforms stick magnitude while preserving vector direction.
|
|
35
|
+
*
|
|
36
|
+
* @param value - Stick value to transform.
|
|
37
|
+
* @param mapMagnitude - Magnitude transformation.
|
|
38
|
+
* @returns Stick with transformed magnitude.
|
|
39
|
+
*/
|
|
40
|
+
const mapGamepadStickMagnitude = (value, mapMagnitude) => {
|
|
41
|
+
const magnitude = Math.hypot(value.x, value.y);
|
|
42
|
+
if (magnitude === 0) return createGamepadStickResult(value, 0, 0);
|
|
43
|
+
const mappedMagnitude = mapMagnitude(magnitude);
|
|
44
|
+
if (mappedMagnitude === magnitude) return createGamepadStickResult(value, value.x, value.y);
|
|
45
|
+
if (mappedMagnitude === 0) return createGamepadStickResult(value, 0, 0);
|
|
46
|
+
const scale = mappedMagnitude / magnitude;
|
|
47
|
+
return createGamepadStickResult(value, value.x * scale, value.y * scale);
|
|
10
48
|
};
|
|
11
49
|
/**
|
|
12
50
|
* Applies a dead zone and remaps the remaining magnitude to `[0, 1]`.
|
|
@@ -20,128 +58,129 @@ const rescaleGamepadDeadzoneMagnitude = (magnitude, threshold) => {
|
|
|
20
58
|
return Math.min((magnitude - threshold) / (1 - threshold), 1);
|
|
21
59
|
};
|
|
22
60
|
/**
|
|
23
|
-
* Applies
|
|
61
|
+
* Applies a built-in curve to a normalized magnitude.
|
|
24
62
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @
|
|
63
|
+
* Magnitudes above `1` remain unchanged so response curves do not implicitly
|
|
64
|
+
* clamp or normalize custom processor output.
|
|
65
|
+
*
|
|
66
|
+
* @param magnitude - Non-negative component or vector magnitude.
|
|
67
|
+
* @param curve - Curve to apply.
|
|
68
|
+
* @returns Curved magnitude.
|
|
29
69
|
*/
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
const applyGamepadResponseCurve = (magnitude, curve) => {
|
|
71
|
+
if (magnitude > 1) return magnitude;
|
|
72
|
+
switch (curve) {
|
|
73
|
+
case "linear": return magnitude;
|
|
74
|
+
case "quadratic": return magnitude ** 2;
|
|
75
|
+
case "cubic": return magnitude ** 3;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Creates an axial or radial dead zone processor.
|
|
80
|
+
*
|
|
81
|
+
* @param threshold - Dead zone threshold.
|
|
82
|
+
* @param mode - Whether to process components or vector magnitude.
|
|
83
|
+
* @param rescale - Whether to remap values outside the dead zone.
|
|
84
|
+
* @returns Frozen dead zone processor.
|
|
85
|
+
*/
|
|
86
|
+
const createDeadzoneProcessor = (threshold, mode, rescale) => {
|
|
87
|
+
const mapMagnitude = (magnitude) => {
|
|
88
|
+
if (magnitude < threshold) return 0;
|
|
89
|
+
return rescale ? rescaleGamepadDeadzoneMagnitude(magnitude, threshold) : magnitude;
|
|
90
|
+
};
|
|
91
|
+
return Object.freeze({ process(value) {
|
|
92
|
+
if (mode === "axial") return createGamepadStickResult(value, mapGamepadSignedMagnitude(value.x, mapMagnitude), mapGamepadSignedMagnitude(value.y, mapMagnitude));
|
|
93
|
+
return mapGamepadStickMagnitude(value, mapMagnitude);
|
|
94
|
+
} });
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Creates an axial or radial response curve processor.
|
|
98
|
+
*
|
|
99
|
+
* @param curve - Curve applied to normalized magnitudes.
|
|
100
|
+
* @param mode - Whether to process components or vector magnitude.
|
|
101
|
+
* @returns Frozen response curve processor.
|
|
102
|
+
*/
|
|
103
|
+
const createResponseCurveProcessor = (curve, mode) => {
|
|
104
|
+
const mapMagnitude = (magnitude) => applyGamepadResponseCurve(magnitude, curve);
|
|
105
|
+
return Object.freeze({ process(value) {
|
|
106
|
+
if (mode === "axial") return createGamepadStickResult(value, mapGamepadSignedMagnitude(value.x, mapMagnitude), mapGamepadSignedMagnitude(value.y, mapMagnitude));
|
|
107
|
+
return mapGamepadStickMagnitude(value, mapMagnitude);
|
|
108
|
+
} });
|
|
38
109
|
};
|
|
39
110
|
/**
|
|
40
|
-
* Creates
|
|
111
|
+
* Creates a processor that inverts selected stick components.
|
|
41
112
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
113
|
+
* @param axis - Component or components to invert.
|
|
114
|
+
* @returns Frozen inversion processor.
|
|
115
|
+
*/
|
|
116
|
+
const createInversionProcessor = (axis) => {
|
|
117
|
+
const invertX = axis === "x" || axis === "both";
|
|
118
|
+
const invertY = axis === "y" || axis === "both";
|
|
119
|
+
return Object.freeze({ process(value) {
|
|
120
|
+
const x = value.x === 0 ? 0 : invertX ? -value.x : value.x;
|
|
121
|
+
const y = value.y === 0 ? 0 : invertY ? -value.y : value.y;
|
|
122
|
+
return createGamepadStickResult(value, x, y);
|
|
123
|
+
} });
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Creates a frozen custom transformation processor.
|
|
44
127
|
*
|
|
128
|
+
* @param operation - Transformation applied to the current pipeline value.
|
|
129
|
+
* @returns Frozen custom processor.
|
|
130
|
+
*/
|
|
131
|
+
const createTransformProcessor = (operation) => {
|
|
132
|
+
return Object.freeze({ process: operation });
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Creates an immutable pipeline over a private processor sequence.
|
|
136
|
+
*
|
|
137
|
+
* @param mode - Default processing mode for new built-in stages.
|
|
45
138
|
* @param processors - Processors in execution order.
|
|
46
|
-
* @returns
|
|
139
|
+
* @returns Frozen stick pipeline.
|
|
47
140
|
*/
|
|
48
|
-
const
|
|
141
|
+
const createPipeline = (mode, processors) => {
|
|
49
142
|
const pipelineProcessors = Object.freeze([...processors]);
|
|
143
|
+
const append = (processor) => createPipeline(mode, [...pipelineProcessors, processor]);
|
|
50
144
|
return Object.freeze({
|
|
51
|
-
processors: pipelineProcessors,
|
|
52
145
|
process(value) {
|
|
53
146
|
let processed = value;
|
|
54
147
|
for (const processor of pipelineProcessors) processed = processor.process(processed);
|
|
55
148
|
return processed;
|
|
149
|
+
},
|
|
150
|
+
deadzone(threshold = DEFAULT_GAMEPAD_DEADZONE_THRESHOLD, options) {
|
|
151
|
+
return append(createDeadzoneProcessor(threshold, options?.mode ?? mode, options?.rescale ?? false));
|
|
152
|
+
},
|
|
153
|
+
curve(curve, options) {
|
|
154
|
+
return append(createResponseCurveProcessor(curve, options?.mode ?? mode));
|
|
155
|
+
},
|
|
156
|
+
invert(axis) {
|
|
157
|
+
return append(createInversionProcessor(axis));
|
|
158
|
+
},
|
|
159
|
+
transform(operation) {
|
|
160
|
+
return append(createTransformProcessor(operation));
|
|
161
|
+
},
|
|
162
|
+
pipe(processor) {
|
|
163
|
+
return append(processor);
|
|
56
164
|
}
|
|
57
165
|
});
|
|
58
166
|
};
|
|
59
167
|
/**
|
|
60
|
-
* Creates an
|
|
168
|
+
* Creates an immutable, fluent stick processing pipeline.
|
|
61
169
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
|
|
65
|
-
const createGamepadDeadzoneProcessor = (options) => {
|
|
66
|
-
const { threshold, mode, rescale } = {
|
|
67
|
-
...DEFAULT_DEADZONE_PROCESSOR_OPTIONS,
|
|
68
|
-
...options
|
|
69
|
-
};
|
|
70
|
-
return Object.freeze({ process(value) {
|
|
71
|
-
if (mode === "axial") {
|
|
72
|
-
const x = applyGamepadAxialDeadzone(value.x, threshold, rescale);
|
|
73
|
-
const y = applyGamepadAxialDeadzone(value.y, threshold, rescale);
|
|
74
|
-
if (x === value.x && y === value.y && !Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
75
|
-
return {
|
|
76
|
-
x,
|
|
77
|
-
y
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
const magnitude = Math.hypot(value.x, value.y);
|
|
81
|
-
if (magnitude < threshold) {
|
|
82
|
-
if (value.x === 0 && value.y === 0 && !Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
83
|
-
return {
|
|
84
|
-
x: 0,
|
|
85
|
-
y: 0
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
if (!rescale) {
|
|
89
|
-
if (!Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
90
|
-
return {
|
|
91
|
-
x: value.x === 0 ? 0 : value.x,
|
|
92
|
-
y: value.y === 0 ? 0 : value.y
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
const rescaledMagnitude = rescaleGamepadDeadzoneMagnitude(magnitude, threshold);
|
|
96
|
-
if (rescaledMagnitude === 0 || magnitude === 0) {
|
|
97
|
-
if (value.x === 0 && value.y === 0 && !Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
98
|
-
return {
|
|
99
|
-
x: 0,
|
|
100
|
-
y: 0
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
const scale = rescaledMagnitude / magnitude;
|
|
104
|
-
const x = value.x === 0 ? 0 : value.x * scale;
|
|
105
|
-
const y = value.y === 0 ? 0 : value.y * scale;
|
|
106
|
-
if (x === value.x && y === value.y && !Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
107
|
-
return {
|
|
108
|
-
x,
|
|
109
|
-
y
|
|
110
|
-
};
|
|
111
|
-
} });
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* Creates a processor that independently inverts stick components.
|
|
170
|
+
* The configured mode becomes the default for dead zones and response curves.
|
|
171
|
+
* Each fluent method returns a new pipeline, so intermediate pipelines remain
|
|
172
|
+
* reusable. Processor errors are not caught.
|
|
115
173
|
*
|
|
116
|
-
* @param options - Optional
|
|
117
|
-
* @returns
|
|
174
|
+
* @param options - Optional pipeline defaults.
|
|
175
|
+
* @returns Frozen stick pipeline.
|
|
118
176
|
*/
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
...DEFAULT_INVERSION_PROCESSOR_OPTIONS,
|
|
122
|
-
...options
|
|
123
|
-
};
|
|
124
|
-
return Object.freeze({ process(value) {
|
|
125
|
-
if (!invertX && !invertY) {
|
|
126
|
-
if (!Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
127
|
-
return {
|
|
128
|
-
x: value.x === 0 ? 0 : value.x,
|
|
129
|
-
y: value.y === 0 ? 0 : value.y
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const x = value.x === 0 ? 0 : invertX ? -value.x : value.x;
|
|
133
|
-
const y = value.y === 0 ? 0 : invertY ? -value.y : value.y;
|
|
134
|
-
if (x === value.x && y === value.y && !Object.is(value.x, -0) && !Object.is(value.y, -0)) return value;
|
|
135
|
-
return {
|
|
136
|
-
x,
|
|
137
|
-
y
|
|
138
|
-
};
|
|
139
|
-
} });
|
|
177
|
+
const gamepadStickPipeline = (options) => {
|
|
178
|
+
return createPipeline(options?.mode ?? DEFAULT_GAMEPAD_STICK_PROCESSING_MODE, []);
|
|
140
179
|
};
|
|
141
180
|
/**
|
|
142
181
|
* Historical stick processing used when no custom pipeline is supplied.
|
|
143
182
|
*/
|
|
144
|
-
const DEFAULT_GAMEPAD_STICK_PIPELINE =
|
|
183
|
+
const DEFAULT_GAMEPAD_STICK_PIPELINE = gamepadStickPipeline().deadzone();
|
|
145
184
|
/**
|
|
146
185
|
* Resolves a partial stick binding over an action-specific default.
|
|
147
186
|
*
|
|
@@ -157,4 +196,4 @@ const resolveGamepadStickBinding = (defaults, options) => {
|
|
|
157
196
|
};
|
|
158
197
|
};
|
|
159
198
|
//#endregion
|
|
160
|
-
export { DEFAULT_GAMEPAD_STICK_PIPELINE,
|
|
199
|
+
export { DEFAULT_GAMEPAD_STICK_PIPELINE, gamepadStickPipeline, resolveGamepadStickBinding };
|
|
@@ -162,11 +162,13 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
162
162
|
this.#handleModeAndAxisButtons(startedButtons);
|
|
163
163
|
if (startedButtons.has(this.#options.buttonReset)) this.#resetActiveTransform();
|
|
164
164
|
const controls = this.#controls;
|
|
165
|
-
|
|
165
|
+
const object = controls.object;
|
|
166
|
+
if (!controls.enabled || object === void 0) {
|
|
166
167
|
this.#endTransform(true);
|
|
167
168
|
return;
|
|
168
169
|
}
|
|
169
|
-
|
|
170
|
+
const axis = this.#ensureValidAxis();
|
|
171
|
+
if (axis === null) {
|
|
170
172
|
this.#endTransform(true);
|
|
171
173
|
return;
|
|
172
174
|
}
|
|
@@ -176,8 +178,8 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
176
178
|
this.#endTransform(false);
|
|
177
179
|
return;
|
|
178
180
|
}
|
|
179
|
-
if (!this.#isTransforming
|
|
180
|
-
if (this.#applyCurrentTransform(deltaTime, transform.x, transform.y)) {
|
|
181
|
+
if (!this.#isTransforming) this.#startTransform(object);
|
|
182
|
+
if (this.#applyCurrentTransform(object, axis, deltaTime, transform.x, transform.y)) {
|
|
181
183
|
controls.dispatchEvent({ type: "change" });
|
|
182
184
|
controls.dispatchEvent({ type: "objectChange" });
|
|
183
185
|
}
|
|
@@ -270,7 +272,7 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
270
272
|
const current = this.#activeAxisByMode[this.#controls.mode];
|
|
271
273
|
const currentIndex = current === null ? -1 : axes.indexOf(current);
|
|
272
274
|
const nextIndex = currentIndex === -1 ? 0 : (currentIndex + direction + axes.length) % axes.length;
|
|
273
|
-
this.#activeAxisByMode[this.#controls.mode] = axes[nextIndex]
|
|
275
|
+
this.#activeAxisByMode[this.#controls.mode] = axes[nextIndex];
|
|
274
276
|
this.#ensureValidAxis();
|
|
275
277
|
}
|
|
276
278
|
/**
|
|
@@ -349,12 +351,10 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
349
351
|
/**
|
|
350
352
|
* Starts a TransformControls drag interaction for the active object and axis.
|
|
351
353
|
*
|
|
352
|
-
* @
|
|
354
|
+
* @param object - Object attached to TransformControls for this update.
|
|
353
355
|
*/
|
|
354
|
-
#startTransform() {
|
|
356
|
+
#startTransform(object) {
|
|
355
357
|
const controls = this.#controls;
|
|
356
|
-
const object = controls.object;
|
|
357
|
-
if (object === void 0 || controls.axis === null) return false;
|
|
358
358
|
this.#captureTransformStart(object);
|
|
359
359
|
controls.dragging = true;
|
|
360
360
|
this.#isTransforming = true;
|
|
@@ -362,7 +362,6 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
362
362
|
type: "mouseDown",
|
|
363
363
|
mode: controls.mode
|
|
364
364
|
});
|
|
365
|
-
return true;
|
|
366
365
|
}
|
|
367
366
|
/**
|
|
368
367
|
* Ends an active TransformControls drag interaction.
|
|
@@ -435,35 +434,36 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
435
434
|
/**
|
|
436
435
|
* Dispatches the current stick input to the active TransformControls mode.
|
|
437
436
|
*
|
|
437
|
+
* @param object - Object attached to TransformControls for this update.
|
|
438
|
+
* @param axis - Valid axis selected for the active mode.
|
|
438
439
|
* @param deltaTime - Seconds since the last frame.
|
|
439
440
|
* @param transformX - Horizontal transform input after dead zone processing.
|
|
440
441
|
* @param transformY - Vertical transform input after dead zone processing.
|
|
441
442
|
* @returns `true` when the attached object changed.
|
|
442
443
|
*/
|
|
443
|
-
#applyCurrentTransform(deltaTime, transformX, transformY) {
|
|
444
|
+
#applyCurrentTransform(object, axis, deltaTime, transformX, transformY) {
|
|
444
445
|
switch (this.#controls.mode) {
|
|
445
|
-
case "translate": return this.#applyTranslate(deltaTime, transformX, transformY);
|
|
446
|
-
case "rotate": return this.#applyRotate(deltaTime, transformX, transformY);
|
|
447
|
-
case "scale": return this.#applyScale(deltaTime, transformX, transformY);
|
|
446
|
+
case "translate": return this.#applyTranslate(object, axis, deltaTime, transformX, transformY);
|
|
447
|
+
case "rotate": return this.#applyRotate(object, axis, deltaTime, transformX, transformY);
|
|
448
|
+
case "scale": return this.#applyScale(object, axis, deltaTime, transformX, transformY);
|
|
448
449
|
}
|
|
449
450
|
}
|
|
450
451
|
/**
|
|
451
452
|
* Applies translation in the selected axis, plane, or screen-facing plane.
|
|
452
453
|
*
|
|
454
|
+
* @param object - Object attached to TransformControls for this update.
|
|
455
|
+
* @param axis - Valid translation axis selected for this update.
|
|
453
456
|
* @param deltaTime - Seconds since the last frame.
|
|
454
457
|
* @param transformX - Horizontal transform input after dead zone processing.
|
|
455
458
|
* @param transformY - Vertical transform input after dead zone processing.
|
|
456
459
|
* @returns `true` when the attached object moved.
|
|
457
460
|
*/
|
|
458
|
-
#applyTranslate(deltaTime, transformX, transformY) {
|
|
461
|
+
#applyTranslate(object, axis, deltaTime, transformX, transformY) {
|
|
459
462
|
const controls = this.#controls;
|
|
460
|
-
const object = controls.object;
|
|
461
|
-
const axis = controls.axis;
|
|
462
|
-
if (object === void 0 || axis === null) return false;
|
|
463
463
|
this.#updateCameraState(object);
|
|
464
464
|
this.#worldDelta.set(0, 0, 0);
|
|
465
|
-
const scale =
|
|
466
|
-
const speed =
|
|
465
|
+
const scale = axis === "XYZ" ? "world" : controls.space;
|
|
466
|
+
const speed = axis === "XYZ" ? this.#options.translateSpeed * deltaTime : (this.#viewSize.x + this.#viewSize.y) / 2 * this.#options.translateSpeed * deltaTime;
|
|
467
467
|
if (axis === "XYZ") {
|
|
468
468
|
this.#worldDelta.addScaledVector(this.#cameraRight, transformX * this.#viewSize.x * speed);
|
|
469
469
|
this.#worldDelta.addScaledVector(this.#cameraUp, -transformY * this.#viewSize.y * speed);
|
|
@@ -542,16 +542,15 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
542
542
|
/**
|
|
543
543
|
* Applies rotation for the selected axis or free-rotation mode.
|
|
544
544
|
*
|
|
545
|
+
* @param object - Object attached to TransformControls for this update.
|
|
546
|
+
* @param axis - Valid rotation axis selected for this update.
|
|
545
547
|
* @param deltaTime - Seconds since the last frame.
|
|
546
548
|
* @param transformX - Horizontal transform input after dead zone processing.
|
|
547
549
|
* @param transformY - Vertical transform input after dead zone processing.
|
|
548
550
|
* @returns `true` when the attached object rotated.
|
|
549
551
|
*/
|
|
550
|
-
#applyRotate(deltaTime, transformX, transformY) {
|
|
552
|
+
#applyRotate(object, axis, deltaTime, transformX, transformY) {
|
|
551
553
|
const controls = this.#controls;
|
|
552
|
-
const object = controls.object;
|
|
553
|
-
const axis = controls.axis;
|
|
554
|
-
if (object === void 0 || axis === null) return false;
|
|
555
554
|
this.#updateCameraState(object);
|
|
556
555
|
const angleScale = this.#options.rotateSpeed * deltaTime * Math.PI;
|
|
557
556
|
if (axis === "XYZE") {
|
|
@@ -564,11 +563,11 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
564
563
|
if (axis === "E") {
|
|
565
564
|
this.#axisWorld.copy(this.#cameraForward).normalize();
|
|
566
565
|
input = this.#getDominantInput(transformX, -transformY);
|
|
567
|
-
} else
|
|
566
|
+
} else {
|
|
568
567
|
const space = controls.space;
|
|
569
568
|
this.#getTransformAxisWorld(axis, space, this.#axisWorld);
|
|
570
569
|
input = this.#getRotationAxisInput(this.#axisWorld, transformX, transformY);
|
|
571
|
-
}
|
|
570
|
+
}
|
|
572
571
|
this.#rotationAmount += input * angleScale;
|
|
573
572
|
const angle = this.#snapRotation(this.#rotationAmount);
|
|
574
573
|
if (axis !== "E" && controls.space === "local") {
|
|
@@ -611,16 +610,15 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
611
610
|
/**
|
|
612
611
|
* Applies scale along the selected axis or uniformly across all axes.
|
|
613
612
|
*
|
|
613
|
+
* @param object - Object attached to TransformControls for this update.
|
|
614
|
+
* @param axis - Valid scale axis selected for this update.
|
|
614
615
|
* @param deltaTime - Seconds since the last frame.
|
|
615
616
|
* @param transformX - Horizontal transform input after dead zone processing.
|
|
616
617
|
* @param transformY - Vertical transform input after dead zone processing.
|
|
617
618
|
* @returns `true` when the attached object scaled.
|
|
618
619
|
*/
|
|
619
|
-
#applyScale(deltaTime, transformX, transformY) {
|
|
620
|
+
#applyScale(object, axis, deltaTime, transformX, transformY) {
|
|
620
621
|
const controls = this.#controls;
|
|
621
|
-
const object = controls.object;
|
|
622
|
-
const axis = controls.axis;
|
|
623
|
-
if (object === void 0 || axis === null) return false;
|
|
624
622
|
this.#updateCameraState(object);
|
|
625
623
|
const input = axis === "XYZ" ? this.#getDominantInput(transformX, -transformY) : this.#getProjectedScaleInput(axis, transformX, transformY);
|
|
626
624
|
const factor = Math.exp(input * this.#options.scaleSpeed * deltaTime);
|
|
@@ -647,7 +645,6 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
647
645
|
* @returns Signed scale input for the current frame.
|
|
648
646
|
*/
|
|
649
647
|
#getProjectedScaleInput(axis, transformX, transformY) {
|
|
650
|
-
if (axis !== "X" && axis !== "Y" && axis !== "Z") return this.#getDominantInput(transformX, -transformY);
|
|
651
648
|
this.#getTransformAxisWorld(axis, "local", this.#axisWorld);
|
|
652
649
|
return this.#getProjectedAxisInput(this.#axisWorld, transformX, transformY, this.#getDominantInput(transformX, -transformY));
|
|
653
650
|
}
|
|
@@ -775,9 +772,7 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
775
772
|
* @param target - Vector to adjust in place.
|
|
776
773
|
*/
|
|
777
774
|
#divideByParentScale(target) {
|
|
778
|
-
target.
|
|
779
|
-
target.y = this.#parentScale.y === 0 ? 0 : target.y / this.#parentScale.y;
|
|
780
|
-
target.z = this.#parentScale.z === 0 ? 0 : target.z / this.#parentScale.z;
|
|
775
|
+
target.divide(this.#parentScale);
|
|
781
776
|
}
|
|
782
777
|
/**
|
|
783
778
|
* Writes a unit axis vector into a target vector.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadAxisKey, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue, MAX_GAMEPAD_INDEX, MIN_GAMEPAD_INDEX } from "./core.js";
|
|
2
|
-
import { DEFAULT_GAMEPAD_STICK_PIPELINE,
|
|
2
|
+
import { DEFAULT_GAMEPAD_STICK_PIPELINE, GamepadDeadzoneOptions, GamepadResponseCurve, GamepadResponseCurveOptions, GamepadStick, GamepadStickBinding, GamepadStickBindingOptions, GamepadStickInversionAxis, GamepadStickPipeline, GamepadStickPipelineOptions, GamepadStickProcessingMode, GamepadStickProcessor, GamepadStickTransform, gamepadStickPipeline, resolveGamepadStickBinding } from "./gamepad-stick-processing.js";
|
|
3
3
|
import { GamepadAxisOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions } from "./gamepad-input.js";
|
|
4
4
|
import { GamepadControls, GamepadControlsEventMap, GamepadControlsOptions } from "./gamepad-controls.js";
|
|
5
5
|
import { GamepadArcballControls, GamepadArcballControlsOptions } from "./gamepad-arcball-controls.js";
|
|
@@ -11,4 +11,4 @@ import { GamepadMapControls } from "./gamepad-map-controls.js";
|
|
|
11
11
|
import { GamepadPointerLockControls, GamepadPointerLockControlsOptions } from "./gamepad-pointer-lock-controls.js";
|
|
12
12
|
import { GamepadTrackballControls, GamepadTrackballControlsOptions } from "./gamepad-trackball-controls.js";
|
|
13
13
|
import { GamepadTransformControls, GamepadTransformControlsOptions } from "./gamepad-transform-controls.js";
|
|
14
|
-
export { DEFAULT_GAMEPAD_STICK_PIPELINE, GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadArcballControlsOptions, GamepadAxisKey, GamepadAxisOptions, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue, GamepadControls, GamepadControlsEventMap, GamepadControlsOptions,
|
|
14
|
+
export { DEFAULT_GAMEPAD_STICK_PIPELINE, GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadArcballControlsOptions, GamepadAxisKey, GamepadAxisOptions, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue, GamepadControls, GamepadControlsEventMap, GamepadControlsOptions, GamepadDeadzoneOptions, GamepadDragControls, GamepadDragControlsOptions, GamepadFirstPersonControls, GamepadFirstPersonControlsOptions, GamepadFlyControls, GamepadFlyControlsOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions, GamepadMapControls, GamepadOrbitControls, GamepadOrbitControlsOptions, GamepadPointerLockControls, GamepadPointerLockControlsOptions, GamepadResponseCurve, GamepadResponseCurveOptions, GamepadStick, GamepadStickBinding, GamepadStickBindingOptions, GamepadStickInversionAxis, GamepadStickPipeline, GamepadStickPipelineOptions, GamepadStickProcessingMode, GamepadStickProcessor, GamepadStickTransform, GamepadTrackballControls, GamepadTrackballControlsOptions, GamepadTransformControls, GamepadTransformControlsOptions, MAX_GAMEPAD_INDEX, MIN_GAMEPAD_INDEX, gamepadStickPipeline, resolveGamepadStickBinding };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON, MAX_GAMEPAD_INDEX, MIN_GAMEPAD_INDEX } from "./core.js";
|
|
2
|
-
import { DEFAULT_GAMEPAD_STICK_PIPELINE,
|
|
2
|
+
import { DEFAULT_GAMEPAD_STICK_PIPELINE, gamepadStickPipeline, resolveGamepadStickBinding } from "./gamepad-stick-processing.js";
|
|
3
3
|
import { GamepadInput } from "./gamepad-input.js";
|
|
4
4
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
5
5
|
import { GamepadArcballControls } from "./gamepad-arcball-controls.js";
|
|
@@ -11,4 +11,4 @@ import { GamepadMapControls } from "./gamepad-map-controls.js";
|
|
|
11
11
|
import { GamepadPointerLockControls } from "./gamepad-pointer-lock-controls.js";
|
|
12
12
|
import { GamepadTrackballControls } from "./gamepad-trackball-controls.js";
|
|
13
13
|
import { GamepadTransformControls } from "./gamepad-transform-controls.js";
|
|
14
|
-
export { DEFAULT_GAMEPAD_STICK_PIPELINE, GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadControls, GamepadDragControls, GamepadFirstPersonControls, GamepadFlyControls, GamepadInput, GamepadMapControls, GamepadOrbitControls, GamepadPointerLockControls, GamepadTrackballControls, GamepadTransformControls, MAX_GAMEPAD_INDEX, MIN_GAMEPAD_INDEX,
|
|
14
|
+
export { DEFAULT_GAMEPAD_STICK_PIPELINE, GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadControls, GamepadDragControls, GamepadFirstPersonControls, GamepadFlyControls, GamepadInput, GamepadMapControls, GamepadOrbitControls, GamepadPointerLockControls, GamepadTrackballControls, GamepadTransformControls, MAX_GAMEPAD_INDEX, MIN_GAMEPAD_INDEX, gamepadStickPipeline, resolveGamepadStickBinding };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-gamepad-controls",
|
|
3
3
|
"description": "Gamepad support for Three.js controls.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.21.0",
|
|
5
5
|
"homepage": "https://github.com/luckasnix/three-gamepad-controls#readme",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Kasnix",
|
|
@@ -46,16 +46,19 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@biomejs/biome": "2.5.
|
|
49
|
+
"@biomejs/biome": "2.5.6",
|
|
50
50
|
"@commitlint/cli": "21.2.1",
|
|
51
51
|
"@commitlint/config-conventional": "21.2.0",
|
|
52
52
|
"@commitlint/types": "21.2.0",
|
|
53
53
|
"@types/node": "24.13.3",
|
|
54
54
|
"@types/three": "0.184.0",
|
|
55
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
55
56
|
"husky": "9.1.7",
|
|
57
|
+
"jsdom": "29.1.1",
|
|
56
58
|
"three": "0.184.0",
|
|
57
|
-
"tsdown": "0.22.
|
|
58
|
-
"typescript": "7.0.2"
|
|
59
|
+
"tsdown": "0.22.14",
|
|
60
|
+
"typescript": "7.0.2",
|
|
61
|
+
"vitest": "4.1.10"
|
|
59
62
|
},
|
|
60
63
|
"peerDependencies": {
|
|
61
64
|
"@types/three": ">=0.184.0",
|
|
@@ -70,6 +73,9 @@
|
|
|
70
73
|
"lint:write": "biome lint --write",
|
|
71
74
|
"check-all": "biome check",
|
|
72
75
|
"write-all": "biome check --write",
|
|
73
|
-
"check-ci": "biome ci"
|
|
76
|
+
"check-ci": "biome ci",
|
|
77
|
+
"test": "vitest run",
|
|
78
|
+
"test:watch": "vitest",
|
|
79
|
+
"test:coverage": "vitest run --coverage"
|
|
74
80
|
}
|
|
75
81
|
}
|