three-gamepad-controls 0.10.6 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
2
  import { GamepadControls } from "./gamepad-controls.js";
3
- import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
4
3
  import { Matrix4, Quaternion, Vector2, Vector3 } from "three";
5
4
  //#region src/gamepad-transform-controls.ts
6
5
  /**
@@ -70,7 +69,6 @@ const PROJECTED_AXIS_EPSILON = .001;
70
69
  var GamepadTransformControls = class extends GamepadControls {
71
70
  #controls;
72
71
  #options;
73
- #pressedButtons;
74
72
  #activeAxisByMode;
75
73
  #viewSize;
76
74
  #parentInverse;
@@ -117,7 +115,6 @@ var GamepadTransformControls = class extends GamepadControls {
117
115
  ...DEFAULT_TRANSFORM_OPTIONS,
118
116
  ...options
119
117
  };
120
- this.#pressedButtons = /* @__PURE__ */ new Set();
121
118
  this.#activeAxisByMode = {
122
119
  translate: "X",
123
120
  rotate: "X",
@@ -158,10 +155,9 @@ var GamepadTransformControls = class extends GamepadControls {
158
155
  * translate, rotate, scale, and reset behavior.
159
156
  *
160
157
  * @param deltaTime - Seconds since the last frame.
161
- * @param gamepad - Fresh gamepad snapshot provided by the base class.
162
158
  */
163
- onUpdate(deltaTime, gamepad) {
164
- const startedButtons = this.#getStartedButtons(gamepad);
159
+ onUpdate(deltaTime) {
160
+ const startedButtons = this.#getStartedButtons();
165
161
  this.#handleModeAndAxisButtons(startedButtons);
166
162
  if (startedButtons.has(this.#options.buttonReset)) this.#resetActiveTransform();
167
163
  const controls = this.#controls;
@@ -173,8 +169,8 @@ var GamepadTransformControls = class extends GamepadControls {
173
169
  this.#endTransform(true);
174
170
  return;
175
171
  }
176
- const transformX = applyGamepadDeadzone(gamepad.axes[this.#options.axisTransformX] ?? 0, this.#options.deadzone);
177
- const transformY = applyGamepadDeadzone(gamepad.axes[this.#options.axisTransformY] ?? 0, this.#options.deadzone);
172
+ const transformX = this.gamepadInput.axis(this.#options.axisTransformX, { deadzone: this.#options.deadzone });
173
+ const transformY = this.gamepadInput.axis(this.#options.axisTransformY, { deadzone: this.#options.deadzone });
178
174
  if (transformX === 0 && transformY === 0) {
179
175
  this.#endTransform(false);
180
176
  return;
@@ -190,7 +186,6 @@ var GamepadTransformControls = class extends GamepadControls {
190
186
  */
191
187
  dispose() {
192
188
  this.#endTransform(true);
193
- this.#pressedButtons.clear();
194
189
  super.dispose();
195
190
  }
196
191
  /**
@@ -200,9 +195,13 @@ var GamepadTransformControls = class extends GamepadControls {
200
195
  */
201
196
  onGamepadDisconnected(gamepad) {
202
197
  this.#endTransform(true);
203
- this.#pressedButtons.clear();
204
198
  super.onGamepadDisconnected(gamepad);
205
199
  }
200
+ /**
201
+ * Applies mode, space, and axis button transitions from the current frame.
202
+ *
203
+ * @param startedButtons - Button indices that transitioned to pressed.
204
+ */
206
205
  #handleModeAndAxisButtons(startedButtons) {
207
206
  const { buttonTranslate, buttonRotate, buttonScale, buttonToggleSpace, buttonAxisX, buttonAxisY, buttonAxisZ, buttonAxisComposite, buttonAxisPrevious, buttonAxisNext } = this.#options;
208
207
  if (startedButtons.has(buttonTranslate)) this.#setMode("translate");
@@ -216,30 +215,57 @@ var GamepadTransformControls = class extends GamepadControls {
216
215
  if (startedButtons.has(buttonAxisPrevious)) this.#cycleAxis(-1);
217
216
  if (startedButtons.has(buttonAxisNext)) this.#cycleAxis(1);
218
217
  }
218
+ /**
219
+ * Switches TransformControls mode and refreshes the active axis.
220
+ *
221
+ * @param mode - TransformControls mode to activate.
222
+ */
219
223
  #setMode(mode) {
220
224
  if (this.#controls.mode === mode) return;
221
225
  this.#endTransform(false);
222
226
  this.#controls.setMode(mode);
223
227
  this.#ensureValidAxis();
224
228
  }
229
+ /**
230
+ * Toggles TransformControls between local and world transform space.
231
+ */
225
232
  #toggleSpace() {
226
233
  const nextSpace = this.#controls.space === "world" ? "local" : "world";
227
234
  this.#endTransform(false);
228
235
  this.#controls.setSpace(nextSpace);
229
236
  }
237
+ /**
238
+ * Selects an explicit axis when it is valid for the current mode.
239
+ *
240
+ * @param axis - Axis requested by the gamepad button mapping.
241
+ */
230
242
  #selectAxis(axis) {
231
243
  if (!this.#isAxisAllowed(this.#controls.mode, axis)) return;
232
244
  this.#endTransform(false);
233
245
  this.#activeAxisByMode[this.#controls.mode] = axis;
234
246
  this.#ensureValidAxis();
235
247
  }
248
+ /**
249
+ * Cycles through composite axes available in the current mode.
250
+ */
236
251
  #cycleCompositeAxis() {
237
- const validAxes = COMPOSITE_AXES[this.#controls.mode].filter((axis) => this.#isAxisVisible(axis));
252
+ const validAxes = this.#getVisibleAxes(COMPOSITE_AXES[this.#controls.mode]);
238
253
  this.#cycleThroughAxes(validAxes, 1);
239
254
  }
255
+ /**
256
+ * Cycles through all valid axes for the current mode.
257
+ *
258
+ * @param direction - `1` for next axis, `-1` for previous axis.
259
+ */
240
260
  #cycleAxis(direction) {
241
261
  this.#cycleThroughAxes(this.#getValidAxes(this.#controls.mode), direction);
242
262
  }
263
+ /**
264
+ * Moves the active axis through a candidate axis list.
265
+ *
266
+ * @param axes - Candidate axes to cycle through.
267
+ * @param direction - `1` for next axis, `-1` for previous axis.
268
+ */
243
269
  #cycleThroughAxes(axes, direction) {
244
270
  if (axes.length === 0) {
245
271
  this.#setActiveAxis(null);
@@ -252,6 +278,11 @@ var GamepadTransformControls = class extends GamepadControls {
252
278
  this.#activeAxisByMode[this.#controls.mode] = axes[nextIndex] ?? null;
253
279
  this.#ensureValidAxis();
254
280
  }
281
+ /**
282
+ * Ensures the highlighted TransformControls axis is valid and visible.
283
+ *
284
+ * @returns The active valid axis, or `null` when no axis is available.
285
+ */
255
286
  #ensureValidAxis() {
256
287
  const mode = this.#controls.mode;
257
288
  const current = this.#activeAxisByMode[mode];
@@ -261,16 +292,51 @@ var GamepadTransformControls = class extends GamepadControls {
261
292
  if (this.#controls.axis !== nextAxis) this.#controls.axis = nextAxis;
262
293
  return nextAxis;
263
294
  }
295
+ /**
296
+ * Updates both the remembered axis for the current mode and the control axis.
297
+ *
298
+ * @param axis - Axis to activate, or `null` to clear selection.
299
+ */
264
300
  #setActiveAxis(axis) {
265
301
  this.#activeAxisByMode[this.#controls.mode] = axis;
266
302
  if (this.#controls.axis !== axis) this.#controls.axis = axis;
267
303
  }
304
+ /**
305
+ * Returns all visible axes supported by a TransformControls mode.
306
+ *
307
+ * @param mode - Mode whose axes should be inspected.
308
+ * @returns Visible axes for the mode.
309
+ */
268
310
  #getValidAxes(mode) {
269
- return MODE_AXES[mode].filter((axis) => this.#isAxisVisible(axis));
311
+ return this.#getVisibleAxes(MODE_AXES[mode]);
312
+ }
313
+ /**
314
+ * Filters a list of axes to those enabled by TransformControls visibility flags.
315
+ *
316
+ * @param axes - Axes to inspect.
317
+ * @returns Axes whose component visibility flags are enabled.
318
+ */
319
+ #getVisibleAxes(axes) {
320
+ const visibleAxes = [];
321
+ for (const axis of axes) if (this.#isAxisVisible(axis)) visibleAxes.push(axis);
322
+ return visibleAxes;
270
323
  }
324
+ /**
325
+ * Checks whether an axis can be used in a mode and is currently visible.
326
+ *
327
+ * @param mode - TransformControls mode to validate against.
328
+ * @param axis - Axis to validate.
329
+ * @returns `true` when the axis is supported and visible.
330
+ */
271
331
  #isAxisAllowed(mode, axis) {
272
332
  return MODE_AXES[mode].includes(axis) && this.#isAxisVisible(axis);
273
333
  }
334
+ /**
335
+ * Reads TransformControls visibility flags for an axis or plane.
336
+ *
337
+ * @param axis - Axis whose visibility should be checked.
338
+ * @returns `true` when all components required by the axis are visible.
339
+ */
274
340
  #isAxisVisible(axis) {
275
341
  const controls = this.#controls;
276
342
  switch (axis) {
@@ -285,6 +351,11 @@ var GamepadTransformControls = class extends GamepadControls {
285
351
  case "E": return true;
286
352
  }
287
353
  }
354
+ /**
355
+ * Starts a TransformControls drag interaction for the active object and axis.
356
+ *
357
+ * @returns `true` when a transform interaction was started.
358
+ */
288
359
  #startTransform() {
289
360
  const controls = this.#controls;
290
361
  const object = controls.object;
@@ -298,6 +369,11 @@ var GamepadTransformControls = class extends GamepadControls {
298
369
  });
299
370
  return true;
300
371
  }
372
+ /**
373
+ * Ends an active TransformControls drag interaction.
374
+ *
375
+ * @param clearAxis - Whether to clear the highlighted axis after ending.
376
+ */
301
377
  #endTransform(clearAxis) {
302
378
  const controls = this.#controls;
303
379
  if (this.#isTransforming) {
@@ -310,6 +386,9 @@ var GamepadTransformControls = class extends GamepadControls {
310
386
  }
311
387
  if (clearAxis) this.#setActiveAxis(null);
312
388
  }
389
+ /**
390
+ * Resets the active object to TransformControls' captured drag start state.
391
+ */
313
392
  #resetActiveTransform() {
314
393
  const object = this.#controls.object;
315
394
  if (!this.#isTransforming || object === void 0) return;
@@ -320,6 +399,11 @@ var GamepadTransformControls = class extends GamepadControls {
320
399
  this.#freeRotationX = 0;
321
400
  this.#freeRotationY = 0;
322
401
  }
402
+ /**
403
+ * Captures object and control state needed to apply gamepad transforms.
404
+ *
405
+ * @param object - Object attached to TransformControls.
406
+ */
323
407
  #captureTransformStart(object) {
324
408
  object.updateWorldMatrix(true, false);
325
409
  object.parent?.updateWorldMatrix(true, false);
@@ -339,6 +423,11 @@ var GamepadTransformControls = class extends GamepadControls {
339
423
  this.#freeRotationX = 0;
340
424
  this.#freeRotationY = 0;
341
425
  }
426
+ /**
427
+ * Captures the attached object's parent world transform for local conversions.
428
+ *
429
+ * @param object - Object attached to TransformControls.
430
+ */
342
431
  #captureParentTransform(object) {
343
432
  if (object.parent === null) {
344
433
  this.#parentPosition.set(0, 0, 0);
@@ -351,6 +440,14 @@ var GamepadTransformControls = class extends GamepadControls {
351
440
  object.parent.matrixWorld.decompose(this.#parentPosition, this.#parentQuaternion, this.#parentScale);
352
441
  this.#parentQuaternionInv.copy(this.#parentQuaternion).invert();
353
442
  }
443
+ /**
444
+ * Dispatches the current stick input to the active TransformControls mode.
445
+ *
446
+ * @param deltaTime - Seconds since the last frame.
447
+ * @param transformX - Horizontal transform input after dead zone processing.
448
+ * @param transformY - Vertical transform input after dead zone processing.
449
+ * @returns `true` when the attached object changed.
450
+ */
354
451
  #applyCurrentTransform(deltaTime, transformX, transformY) {
355
452
  switch (this.#controls.mode) {
356
453
  case "translate": return this.#applyTranslate(deltaTime, transformX, transformY);
@@ -358,6 +455,14 @@ var GamepadTransformControls = class extends GamepadControls {
358
455
  case "scale": return this.#applyScale(deltaTime, transformX, transformY);
359
456
  }
360
457
  }
458
+ /**
459
+ * Applies translation in the selected axis, plane, or screen-facing plane.
460
+ *
461
+ * @param deltaTime - Seconds since the last frame.
462
+ * @param transformX - Horizontal transform input after dead zone processing.
463
+ * @param transformY - Vertical transform input after dead zone processing.
464
+ * @returns `true` when the attached object moved.
465
+ */
361
466
  #applyTranslate(deltaTime, transformX, transformY) {
362
467
  const controls = this.#controls;
363
468
  const object = controls.object;
@@ -377,6 +482,15 @@ var GamepadTransformControls = class extends GamepadControls {
377
482
  this.#applyAccumulatedPosition(object, axis, scale);
378
483
  return true;
379
484
  }
485
+ /**
486
+ * Accumulates a world-space translation delta along selected axis letters.
487
+ *
488
+ * @param axis - Transform axis or plane currently selected.
489
+ * @param space - Transform space used to resolve axis directions.
490
+ * @param transformX - Horizontal transform input after dead zone processing.
491
+ * @param transformY - Vertical transform input after dead zone processing.
492
+ * @param distance - World-space distance scale for the frame.
493
+ */
380
494
  #addAxisTranslation(axis, space, transformX, transformY, distance) {
381
495
  for (const letter of [
382
496
  "X",
@@ -388,6 +502,13 @@ var GamepadTransformControls = class extends GamepadControls {
388
502
  this.#worldDelta.addScaledVector(this.#axisWorld, this.#getProjectedAxisInput(this.#axisWorld, transformX, transformY, this.#getDominantInput(transformX, -transformY)) * distance);
389
503
  }
390
504
  }
505
+ /**
506
+ * Applies accumulated translation with snapping and bounds.
507
+ *
508
+ * @param object - Object attached to TransformControls.
509
+ * @param axis - Transform axis or plane currently selected.
510
+ * @param space - Transform space used for snapping.
511
+ */
391
512
  #applyAccumulatedPosition(object, axis, space) {
392
513
  this.#snappedPosition.copy(this.#accumulatedPosition);
393
514
  if (this.#controls.translationSnap !== null) this.#snapPosition(object, axis, space, this.#controls.translationSnap);
@@ -397,6 +518,14 @@ var GamepadTransformControls = class extends GamepadControls {
397
518
  object.position.copy(this.#snappedPosition);
398
519
  object.updateMatrixWorld();
399
520
  }
521
+ /**
522
+ * Snaps the pending position in world or local transform space.
523
+ *
524
+ * @param object - Object attached to TransformControls.
525
+ * @param axis - Axis letters that should be snapped.
526
+ * @param space - Transform space used for snapping.
527
+ * @param snap - Snap interval.
528
+ */
400
529
  #snapPosition(object, axis, space, snap) {
401
530
  if (snap <= 0) return;
402
531
  if (space === "world") {
@@ -418,6 +547,14 @@ var GamepadTransformControls = class extends GamepadControls {
418
547
  ]) if (axis.includes(letter)) this.#setVectorComponent(this.#snappedPosition, letter, this.#snapValue(this.#getVectorComponent(this.#snappedPosition, letter), snap));
419
548
  this.#snappedPosition.applyQuaternion(this.#quaternionStart);
420
549
  }
550
+ /**
551
+ * Applies rotation for the selected axis or free-rotation mode.
552
+ *
553
+ * @param deltaTime - Seconds since the last frame.
554
+ * @param transformX - Horizontal transform input after dead zone processing.
555
+ * @param transformY - Vertical transform input after dead zone processing.
556
+ * @returns `true` when the attached object rotated.
557
+ */
421
558
  #applyRotate(deltaTime, transformX, transformY) {
422
559
  const controls = this.#controls;
423
560
  const object = controls.object;
@@ -450,11 +587,23 @@ var GamepadTransformControls = class extends GamepadControls {
450
587
  object.updateMatrixWorld();
451
588
  return true;
452
589
  }
590
+ /**
591
+ * Applies a rotation around a world-space axis while preserving local parent space.
592
+ *
593
+ * @param object - Object attached to TransformControls.
594
+ * @param worldAxis - World-space axis to rotate around.
595
+ * @param angle - Rotation amount in radians from the drag start.
596
+ */
453
597
  #applyWorldRotation(object, worldAxis, angle) {
454
598
  this.#axisLocal.copy(worldAxis).applyQuaternion(this.#parentQuaternionInv).normalize();
455
599
  this.#rotationQuaternion.setFromAxisAngle(this.#axisLocal, angle);
456
600
  object.quaternion.copy(this.#rotationQuaternion).multiply(this.#quaternionStart).normalize();
457
601
  }
602
+ /**
603
+ * Applies screen-relative free rotation from accumulated stick input.
604
+ *
605
+ * @param object - Object attached to TransformControls.
606
+ */
458
607
  #applyFreeRotation(object) {
459
608
  const angleX = this.#snapRotation(this.#freeRotationX);
460
609
  const angleY = this.#snapRotation(this.#freeRotationY);
@@ -467,6 +616,14 @@ var GamepadTransformControls = class extends GamepadControls {
467
616
  object.quaternion.copy(this.#rotationQuaternion2).multiply(this.#rotationQuaternion).multiply(this.#quaternionStart).normalize();
468
617
  object.updateMatrixWorld();
469
618
  }
619
+ /**
620
+ * Applies scale along the selected axis or uniformly across all axes.
621
+ *
622
+ * @param deltaTime - Seconds since the last frame.
623
+ * @param transformX - Horizontal transform input after dead zone processing.
624
+ * @param transformY - Vertical transform input after dead zone processing.
625
+ * @returns `true` when the attached object scaled.
626
+ */
470
627
  #applyScale(deltaTime, transformX, transformY) {
471
628
  const controls = this.#controls;
472
629
  const object = controls.object;
@@ -489,11 +646,24 @@ var GamepadTransformControls = class extends GamepadControls {
489
646
  object.updateMatrixWorld();
490
647
  return true;
491
648
  }
649
+ /**
650
+ * Projects stick input onto the selected scale axis.
651
+ *
652
+ * @param axis - Transform axis or plane currently selected.
653
+ * @param transformX - Horizontal transform input after dead zone processing.
654
+ * @param transformY - Vertical transform input after dead zone processing.
655
+ * @returns Signed scale input for the current frame.
656
+ */
492
657
  #getProjectedScaleInput(axis, transformX, transformY) {
493
658
  if (axis !== "X" && axis !== "Y" && axis !== "Z") return this.#getDominantInput(transformX, -transformY);
494
659
  this.#getTransformAxisWorld(axis, "local", this.#axisWorld);
495
660
  return this.#getProjectedAxisInput(this.#axisWorld, transformX, transformY, this.#getDominantInput(transformX, -transformY));
496
661
  }
662
+ /**
663
+ * Refreshes camera vectors, object world position, and viewport scale.
664
+ *
665
+ * @param object - Object attached to TransformControls.
666
+ */
497
667
  #updateCameraState(object) {
498
668
  const camera = this.#controls.camera;
499
669
  camera.updateMatrixWorld();
@@ -506,6 +676,11 @@ var GamepadTransformControls = class extends GamepadControls {
506
676
  this.#eye.copy(this.#cameraWorldPosition).sub(this.#worldPosition).normalize();
507
677
  this.#updateViewSizeAtObjectDepth(camera);
508
678
  }
679
+ /**
680
+ * Computes world-space viewport size at the attached object's depth.
681
+ *
682
+ * @param camera - TransformControls camera.
683
+ */
509
684
  #updateViewSizeAtObjectDepth(camera) {
510
685
  if (this.#isOrthographicCamera(camera)) {
511
686
  this.#viewSize.set(Math.abs(camera.right - camera.left) / camera.zoom, Math.abs(camera.top - camera.bottom) / camera.zoom);
@@ -519,16 +694,41 @@ var GamepadTransformControls = class extends GamepadControls {
519
694
  }
520
695
  this.#viewSize.set(1, 1);
521
696
  }
697
+ /**
698
+ * Resolves a transform axis letter to a normalized world-space direction.
699
+ *
700
+ * @param axis - Axis letter to resolve.
701
+ * @param space - Transform space used to orient the axis.
702
+ * @param target - Vector that receives the axis direction.
703
+ * @returns The normalized target vector.
704
+ */
522
705
  #getTransformAxisWorld(axis, space, target) {
523
706
  this.#setUnitAxis(axis, target);
524
707
  if (space === "local") target.applyQuaternion(this.#worldQuaternionStart);
525
708
  return target.normalize();
526
709
  }
710
+ /**
711
+ * Projects stick input onto the screen-space tangent of a rotation axis.
712
+ *
713
+ * @param axisWorld - Rotation axis in world space.
714
+ * @param transformX - Horizontal transform input after dead zone processing.
715
+ * @param transformY - Vertical transform input after dead zone processing.
716
+ * @returns Signed rotation input for the current frame.
717
+ */
527
718
  #getRotationAxisInput(axisWorld, transformX, transformY) {
528
719
  this.#axisWorld2.crossVectors(axisWorld, this.#eye);
529
720
  if (this.#axisWorld2.lengthSq() < PROJECTED_AXIS_EPSILON) return this.#getDominantInput(transformX, -transformY);
530
721
  return this.#getProjectedAxisInput(this.#axisWorld2.normalize(), transformX, transformY, this.#getDominantInput(transformX, -transformY));
531
722
  }
723
+ /**
724
+ * Projects two-axis stick input onto a world axis as seen by the camera.
725
+ *
726
+ * @param axisWorld - World-space axis to project onto the screen.
727
+ * @param transformX - Horizontal transform input after dead zone processing.
728
+ * @param transformY - Vertical transform input after dead zone processing.
729
+ * @param fallback - Value to use when the axis has no stable screen projection.
730
+ * @returns Signed input along the projected axis.
731
+ */
532
732
  #getProjectedAxisInput(axisWorld, transformX, transformY, fallback) {
533
733
  const screenX = axisWorld.dot(this.#cameraRight);
534
734
  const screenY = -axisWorld.dot(this.#cameraUp);
@@ -536,11 +736,24 @@ var GamepadTransformControls = class extends GamepadControls {
536
736
  if (length < PROJECTED_AXIS_EPSILON) return fallback;
537
737
  return (transformX * screenX + transformY * screenY) / length;
538
738
  }
739
+ /**
740
+ * Converts a world-space movement delta into the object's parent-local space.
741
+ *
742
+ * @param worldDelta - World-space delta to convert.
743
+ * @param target - Vector that receives the local delta.
744
+ * @returns The target vector containing the local delta.
745
+ */
539
746
  #worldDeltaToLocalDelta(worldDelta, target) {
540
747
  target.copy(worldDelta).applyQuaternion(this.#parentQuaternionInv);
541
748
  this.#divideByParentScale(target);
542
749
  return target;
543
750
  }
751
+ /**
752
+ * Converts a local position to world space into the reusable world position.
753
+ *
754
+ * @param object - Object whose parent space contains the local position.
755
+ * @param localPosition - Local position to convert.
756
+ */
544
757
  #localPositionToWorld(object, localPosition) {
545
758
  if (object.parent === null) {
546
759
  this.#worldPosition.copy(localPosition);
@@ -549,6 +762,12 @@ var GamepadTransformControls = class extends GamepadControls {
549
762
  object.parent.updateWorldMatrix(true, false);
550
763
  this.#worldPosition.copy(localPosition).applyMatrix4(object.parent.matrixWorld);
551
764
  }
765
+ /**
766
+ * Converts a world position to object parent-local space into snapped position.
767
+ *
768
+ * @param object - Object whose parent space should receive the result.
769
+ * @param worldPosition - World-space position to convert.
770
+ */
552
771
  #worldPositionToLocal(object, worldPosition) {
553
772
  if (object.parent === null) {
554
773
  this.#snappedPosition.copy(worldPosition);
@@ -558,11 +777,23 @@ var GamepadTransformControls = class extends GamepadControls {
558
777
  this.#parentInverse.copy(object.parent.matrixWorld).invert();
559
778
  this.#snappedPosition.copy(worldPosition).applyMatrix4(this.#parentInverse);
560
779
  }
780
+ /**
781
+ * Removes captured parent scale from a local-space delta.
782
+ *
783
+ * @param target - Vector to adjust in place.
784
+ */
561
785
  #divideByParentScale(target) {
562
786
  target.x = this.#parentScale.x === 0 ? 0 : target.x / this.#parentScale.x;
563
787
  target.y = this.#parentScale.y === 0 ? 0 : target.y / this.#parentScale.y;
564
788
  target.z = this.#parentScale.z === 0 ? 0 : target.z / this.#parentScale.z;
565
789
  }
790
+ /**
791
+ * Writes a unit axis vector into a target vector.
792
+ *
793
+ * @param axis - Axis letter to write.
794
+ * @param target - Vector that receives the unit axis.
795
+ * @returns The target vector.
796
+ */
566
797
  #setUnitAxis(axis, target) {
567
798
  switch (axis) {
568
799
  case "X": return target.set(1, 0, 0);
@@ -570,6 +801,13 @@ var GamepadTransformControls = class extends GamepadControls {
570
801
  case "Z": return target.set(0, 0, 1);
571
802
  }
572
803
  }
804
+ /**
805
+ * Reads one component from a vector by axis letter.
806
+ *
807
+ * @param vector - Vector to inspect.
808
+ * @param axis - Component axis to read.
809
+ * @returns The selected component value.
810
+ */
573
811
  #getVectorComponent(vector, axis) {
574
812
  switch (axis) {
575
813
  case "X": return vector.x;
@@ -577,6 +815,13 @@ var GamepadTransformControls = class extends GamepadControls {
577
815
  case "Z": return vector.z;
578
816
  }
579
817
  }
818
+ /**
819
+ * Writes one component on a vector by axis letter.
820
+ *
821
+ * @param vector - Vector to modify.
822
+ * @param axis - Component axis to write.
823
+ * @param value - Component value to assign.
824
+ */
580
825
  #setVectorComponent(vector, axis, value) {
581
826
  switch (axis) {
582
827
  case "X":
@@ -590,34 +835,85 @@ var GamepadTransformControls = class extends GamepadControls {
590
835
  return;
591
836
  }
592
837
  }
838
+ /**
839
+ * Applies TransformControls rotation snapping to an angle.
840
+ *
841
+ * @param value - Angle in radians.
842
+ * @returns Snapped angle, or the original angle when snapping is disabled.
843
+ */
593
844
  #snapRotation(value) {
594
845
  const snap = this.#controls.rotationSnap;
595
846
  return snap === null || snap <= 0 ? value : this.#snapValue(value, snap);
596
847
  }
848
+ /**
849
+ * Snaps a numeric value to the nearest interval.
850
+ *
851
+ * @param value - Value to snap.
852
+ * @param snap - Snap interval.
853
+ * @returns Value rounded to the nearest snap interval.
854
+ */
597
855
  #snapValue(value, snap) {
598
856
  return Math.round(value / snap) * snap;
599
857
  }
858
+ /**
859
+ * Snaps a scale component while avoiding zero scale.
860
+ *
861
+ * @param value - Scale component to snap.
862
+ * @param snap - Snap interval.
863
+ * @returns Snapped scale component.
864
+ */
600
865
  #snapScale(value, snap) {
601
866
  return this.#snapValue(value, snap) || snap;
602
867
  }
868
+ /**
869
+ * Chooses the larger-magnitude stick component for ambiguous transforms.
870
+ *
871
+ * @param inputX - Horizontal input component.
872
+ * @param inputY - Vertical input component.
873
+ * @returns The input component with the larger absolute magnitude.
874
+ */
603
875
  #getDominantInput(inputX, inputY) {
604
876
  return Math.abs(inputX) >= Math.abs(inputY) ? inputX : inputY;
605
877
  }
606
- #getStartedButtons(gamepad) {
878
+ /**
879
+ * Returns configured button indices that were newly pressed this frame.
880
+ *
881
+ * @returns Button indices that transitioned to pressed.
882
+ */
883
+ #getStartedButtons() {
607
884
  const startedButtons = /* @__PURE__ */ new Set();
608
- for (let index = 0; index < gamepad.buttons.length; index += 1) {
609
- const pressed = getGamepadButtonPressed(gamepad, index);
610
- const wasPressed = this.#pressedButtons.has(index);
611
- if (pressed) {
612
- this.#pressedButtons.add(index);
613
- if (!wasPressed) startedButtons.add(index);
614
- } else this.#pressedButtons.delete(index);
615
- }
885
+ const input = this.gamepadInput;
886
+ const buttons = [
887
+ this.#options.buttonTranslate,
888
+ this.#options.buttonRotate,
889
+ this.#options.buttonScale,
890
+ this.#options.buttonToggleSpace,
891
+ this.#options.buttonAxisX,
892
+ this.#options.buttonAxisY,
893
+ this.#options.buttonAxisZ,
894
+ this.#options.buttonAxisComposite,
895
+ this.#options.buttonAxisPrevious,
896
+ this.#options.buttonAxisNext,
897
+ this.#options.buttonReset
898
+ ];
899
+ for (const button of buttons) if (input.wasPressed(button)) startedButtons.add(button);
616
900
  return startedButtons;
617
901
  }
902
+ /**
903
+ * Narrows a Three.js camera to `PerspectiveCamera`.
904
+ *
905
+ * @param camera - Camera to inspect.
906
+ * @returns `true` when the camera is perspective.
907
+ */
618
908
  #isPerspectiveCamera(camera) {
619
909
  return camera.isPerspectiveCamera === true;
620
910
  }
911
+ /**
912
+ * Narrows a Three.js camera to `OrthographicCamera`.
913
+ *
914
+ * @param camera - Camera to inspect.
915
+ * @returns `true` when the camera is orthographic.
916
+ */
621
917
  #isOrthographicCamera(camera) {
622
918
  return camera.isOrthographicCamera === true;
623
919
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadAxisKey, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue } from "./core.js";
2
+ import { GamepadAxisOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions, GamepadStick } from "./gamepad-input.js";
2
3
  import { GamepadControls, GamepadControlsEventMap } from "./gamepad-controls.js";
3
4
  import { GamepadArcballControls, GamepadArcballControlsOptions } from "./gamepad-arcball-controls.js";
4
5
  import { GamepadDragControls, GamepadDragControlsOptions } from "./gamepad-drag-controls.js";
@@ -9,4 +10,4 @@ import { GamepadMapControls } from "./gamepad-map-controls.js";
9
10
  import { GamepadPointerLockControls, GamepadPointerLockControlsOptions } from "./gamepad-pointer-lock-controls.js";
10
11
  import { GamepadTrackballControls, GamepadTrackballControlsOptions } from "./gamepad-trackball-controls.js";
11
12
  import { GamepadTransformControls, GamepadTransformControlsOptions } from "./gamepad-transform-controls.js";
12
- export { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadArcballControlsOptions, GamepadAxisKey, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue, GamepadControls, GamepadControlsEventMap, GamepadDragControls, GamepadDragControlsOptions, GamepadFirstPersonControls, GamepadFirstPersonControlsOptions, GamepadFlyControls, GamepadFlyControlsOptions, GamepadMapControls, GamepadOrbitControls, GamepadOrbitControlsOptions, GamepadPointerLockControls, GamepadPointerLockControlsOptions, GamepadTrackballControls, GamepadTrackballControlsOptions, GamepadTransformControls, GamepadTransformControlsOptions };
13
+ export { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadArcballControlsOptions, GamepadAxisKey, GamepadAxisOptions, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue, GamepadControls, GamepadControlsEventMap, GamepadDragControls, GamepadDragControlsOptions, GamepadFirstPersonControls, GamepadFirstPersonControlsOptions, GamepadFlyControls, GamepadFlyControlsOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions, GamepadMapControls, GamepadOrbitControls, GamepadOrbitControlsOptions, GamepadPointerLockControls, GamepadPointerLockControlsOptions, GamepadStick, GamepadTrackballControls, GamepadTrackballControlsOptions, GamepadTransformControls, GamepadTransformControlsOptions };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { GamepadInput } from "./gamepad-input.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { GamepadArcballControls } from "./gamepad-arcball-controls.js";
4
5
  import { GamepadDragControls } from "./gamepad-drag-controls.js";
@@ -9,4 +10,4 @@ import { GamepadMapControls } from "./gamepad-map-controls.js";
9
10
  import { GamepadPointerLockControls } from "./gamepad-pointer-lock-controls.js";
10
11
  import { GamepadTrackballControls } from "./gamepad-trackball-controls.js";
11
12
  import { GamepadTransformControls } from "./gamepad-transform-controls.js";
12
- export { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadControls, GamepadDragControls, GamepadFirstPersonControls, GamepadFlyControls, GamepadMapControls, GamepadOrbitControls, GamepadPointerLockControls, GamepadTrackballControls, GamepadTransformControls };
13
+ export { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadArcballControls, GamepadControls, GamepadDragControls, GamepadFirstPersonControls, GamepadFlyControls, GamepadInput, GamepadMapControls, GamepadOrbitControls, GamepadPointerLockControls, GamepadTrackballControls, GamepadTransformControls };
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.10.6",
4
+ "version": "0.12.0",
5
5
  "homepage": "https://github.com/luckasnix/three-gamepad-controls#readme",
6
6
  "author": {
7
7
  "name": "Kasnix",
@@ -51,14 +51,14 @@
51
51
  "@commitlint/config-conventional": "21.0.2",
52
52
  "@commitlint/types": "21.0.1",
53
53
  "@types/node": "24.13.2",
54
- "@types/three": "0.184.1",
54
+ "@types/three": "0.184.0",
55
55
  "husky": "9.1.7",
56
56
  "three": "0.184.0",
57
57
  "tsdown": "0.22.2",
58
58
  "typescript": "6.0.3"
59
59
  },
60
60
  "peerDependencies": {
61
- "@types/three": ">=0.184.1",
61
+ "@types/three": ">=0.184.0",
62
62
  "three": ">=0.184.0"
63
63
  },
64
64
  "scripts": {