@opsydyn/effect-three 0.3.0 → 0.6.4

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +15 -1
  3. package/dist/{index-8cf8t6by.js → index-re3rrfwd.js} +2935 -104
  4. package/dist/index-re3rrfwd.js.map +56 -0
  5. package/dist/index-ttwy0znv.js +145 -0
  6. package/dist/index-ttwy0znv.js.map +12 -0
  7. package/dist/index.js +557 -147
  8. package/dist/index.js.map +8 -6
  9. package/dist/kernel/bodies.d.ts +20 -1
  10. package/dist/kernel/bodies.d.ts.map +1 -1
  11. package/dist/kernel/boxSelect.d.ts +22 -0
  12. package/dist/kernel/boxSelect.d.ts.map +1 -0
  13. package/dist/kernel/build.d.ts +165 -7
  14. package/dist/kernel/build.d.ts.map +1 -1
  15. package/dist/kernel/cameraShake.d.ts.map +1 -1
  16. package/dist/kernel/describeScene.d.ts +50 -0
  17. package/dist/kernel/describeScene.d.ts.map +1 -0
  18. package/dist/kernel/description.d.ts +223 -1
  19. package/dist/kernel/description.d.ts.map +1 -1
  20. package/dist/kernel/diff.d.ts +15 -0
  21. package/dist/kernel/diff.d.ts.map +1 -1
  22. package/dist/kernel/equality.d.ts +11 -1
  23. package/dist/kernel/equality.d.ts.map +1 -1
  24. package/dist/kernel/gizmo.d.ts +69 -0
  25. package/dist/kernel/gizmo.d.ts.map +1 -0
  26. package/dist/kernel/history.d.ts +61 -0
  27. package/dist/kernel/history.d.ts.map +1 -0
  28. package/dist/kernel/index.d.ts +19 -6
  29. package/dist/kernel/index.d.ts.map +1 -1
  30. package/dist/kernel/index.js +124 -3
  31. package/dist/kernel/index.js.map +1 -1
  32. package/dist/kernel/inspect.d.ts +85 -0
  33. package/dist/kernel/inspect.d.ts.map +1 -0
  34. package/dist/kernel/lineGeometry.d.ts +25 -0
  35. package/dist/kernel/lineGeometry.d.ts.map +1 -0
  36. package/dist/kernel/lockstep.d.ts +114 -0
  37. package/dist/kernel/lockstep.d.ts.map +1 -0
  38. package/dist/kernel/loop.d.ts.map +1 -1
  39. package/dist/kernel/materials.d.ts.map +1 -1
  40. package/dist/kernel/paintOrder.d.ts +53 -0
  41. package/dist/kernel/paintOrder.d.ts.map +1 -0
  42. package/dist/kernel/physics.d.ts +39 -0
  43. package/dist/kernel/physics.d.ts.map +1 -1
  44. package/dist/kernel/pivot.d.ts +54 -0
  45. package/dist/kernel/pivot.d.ts.map +1 -0
  46. package/dist/kernel/pointBuffers.d.ts +17 -0
  47. package/dist/kernel/pointBuffers.d.ts.map +1 -0
  48. package/dist/kernel/pointer.d.ts +31 -1
  49. package/dist/kernel/pointer.d.ts.map +1 -1
  50. package/dist/kernel/preview.d.ts +32 -0
  51. package/dist/kernel/preview.d.ts.map +1 -0
  52. package/dist/kernel/renderKit.d.ts +21 -1
  53. package/dist/kernel/renderKit.d.ts.map +1 -1
  54. package/dist/kernel/renderer.d.ts +6 -0
  55. package/dist/kernel/renderer.d.ts.map +1 -1
  56. package/dist/kernel/sceneChanges.d.ts +87 -0
  57. package/dist/kernel/sceneChanges.d.ts.map +1 -0
  58. package/dist/kernel/sceneGraph.d.ts +9 -1
  59. package/dist/kernel/sceneGraph.d.ts.map +1 -1
  60. package/dist/kernel/sceneIndex.d.ts +5 -0
  61. package/dist/kernel/sceneIndex.d.ts.map +1 -1
  62. package/dist/kernel/seeded.d.ts +9 -0
  63. package/dist/kernel/seeded.d.ts.map +1 -0
  64. package/dist/kernel/shots.d.ts +26 -0
  65. package/dist/kernel/shots.d.ts.map +1 -0
  66. package/dist/kernel/tape.d.ts +38 -0
  67. package/dist/kernel/tape.d.ts.map +1 -0
  68. package/dist/kernel/timeline.d.ts +60 -0
  69. package/dist/kernel/timeline.d.ts.map +1 -0
  70. package/dist/kernel/trail.d.ts +31 -0
  71. package/dist/kernel/trail.d.ts.map +1 -0
  72. package/dist/kernel/viewportGizmo.d.ts +25 -0
  73. package/dist/kernel/viewportGizmo.d.ts.map +1 -0
  74. package/dist/runtime/Inspector.d.ts +20 -0
  75. package/dist/runtime/Inspector.d.ts.map +1 -0
  76. package/dist/runtime/SceneFile.d.ts +462 -0
  77. package/dist/runtime/SceneFile.d.ts.map +1 -0
  78. package/dist/runtime/SceneSchema.d.ts +404 -0
  79. package/dist/runtime/SceneSchema.d.ts.map +1 -1
  80. package/dist/runtime/errors.d.ts +9 -0
  81. package/dist/runtime/errors.d.ts.map +1 -1
  82. package/dist/runtime/index.d.ts +3 -1
  83. package/dist/runtime/index.d.ts.map +1 -1
  84. package/dist/webgl/index.d.ts.map +1 -1
  85. package/dist/webgl/index.js +228 -3
  86. package/dist/webgl/index.js.map +6 -4
  87. package/dist/webgl/line.d.ts +4 -0
  88. package/dist/webgl/line.d.ts.map +1 -0
  89. package/dist/webgl/points.d.ts +4 -0
  90. package/dist/webgl/points.d.ts.map +1 -0
  91. package/dist/webgpu/index.d.ts.map +1 -1
  92. package/dist/webgpu/index.js +206 -26
  93. package/dist/webgpu/index.js.map +6 -4
  94. package/dist/webgpu/line.d.ts +4 -0
  95. package/dist/webgpu/line.d.ts.map +1 -0
  96. package/dist/webgpu/points.d.ts +4 -0
  97. package/dist/webgpu/points.d.ts.map +1 -0
  98. package/package.json +4 -2
  99. package/src/kernel/bodies.ts +299 -17
  100. package/src/kernel/boxSelect.ts +73 -0
  101. package/src/kernel/build.ts +374 -1
  102. package/src/kernel/cameraShake.ts +3 -13
  103. package/src/kernel/describeScene.ts +224 -0
  104. package/src/kernel/description.ts +243 -0
  105. package/src/kernel/diff.ts +39 -0
  106. package/src/kernel/equality.ts +123 -0
  107. package/src/kernel/gizmo.ts +478 -0
  108. package/src/kernel/history.ts +118 -0
  109. package/src/kernel/index.ts +110 -1
  110. package/src/kernel/inspect.ts +214 -0
  111. package/src/kernel/lineGeometry.ts +75 -0
  112. package/src/kernel/lockstep.ts +237 -0
  113. package/src/kernel/loop.ts +7 -0
  114. package/src/kernel/materials.ts +3 -0
  115. package/src/kernel/paintOrder.ts +274 -0
  116. package/src/kernel/physics.ts +86 -1
  117. package/src/kernel/pivot.ts +418 -0
  118. package/src/kernel/pointBuffers.ts +115 -0
  119. package/src/kernel/pointer.ts +98 -17
  120. package/src/kernel/preview.ts +467 -0
  121. package/src/kernel/renderKit.ts +23 -1
  122. package/src/kernel/renderer.ts +6 -0
  123. package/src/kernel/sceneChanges.ts +265 -0
  124. package/src/kernel/sceneGraph.ts +354 -11
  125. package/src/kernel/sceneIndex.ts +8 -0
  126. package/src/kernel/seeded.ts +18 -0
  127. package/src/kernel/shots.ts +167 -0
  128. package/src/kernel/tape.ts +124 -0
  129. package/src/kernel/timeline.ts +93 -0
  130. package/src/kernel/trail.ts +170 -0
  131. package/src/kernel/viewportGizmo.ts +194 -0
  132. package/src/runtime/Inspector.ts +43 -0
  133. package/src/runtime/SceneFile.ts +46 -0
  134. package/src/runtime/SceneSchema.ts +319 -0
  135. package/src/runtime/errors.ts +6 -0
  136. package/src/runtime/index.ts +3 -1
  137. package/src/webgl/index.ts +4 -0
  138. package/src/webgl/line.ts +66 -0
  139. package/src/webgl/points.ts +205 -0
  140. package/src/webgpu/index.ts +4 -0
  141. package/src/webgpu/line.ts +67 -0
  142. package/src/webgpu/points.ts +193 -0
  143. package/dist/index-8cf8t6by.js.map +0 -41
@@ -26,10 +26,15 @@ export type Modifiers = {
26
26
  readonly alt: boolean;
27
27
  };
28
28
 
29
+ /** What pressed: `PointerEvent.pointerType`. A finger is less steady than a mouse or pen. */
30
+ export type PointerDevice = 'mouse' | 'pen' | 'touch';
31
+
29
32
  /**
30
33
  * Pointer input in normalised device coordinates. `button` follows `MouseEvent.button`.
31
34
  * `pointer` is the input's `PointerEvent.pointerId` (0 when absent): with two pointers
32
- * pressed (two fingers), the second only takes part in pinches.
35
+ * pressed (two fingers), the second only takes part in pinches. A press's `device` is
36
+ * what pressed (a mouse when absent); it decides how far a press may travel and still be
37
+ * a click on nothing.
33
38
  */
34
39
  export type PointerInput =
35
40
  | {
@@ -45,6 +50,7 @@ export type PointerInput =
45
50
  readonly button: number;
46
51
  readonly pointer?: number;
47
52
  readonly modifiers?: Modifiers;
53
+ readonly device?: PointerDevice;
48
54
  }
49
55
  | {
50
56
  readonly _tag: 'PointerUp';
@@ -209,6 +215,18 @@ export type SceneEvent =
209
215
  }
210
216
  /** A drag, wheel or pinch on the canvas, when the scene claims gestures. */
211
217
  | { readonly _tag: 'Gesture'; readonly gesture: Gesture }
218
+ /**
219
+ * A gesture that moved has ended: released, or the pointer left. Only after a `Drag`
220
+ * or `Pinch`, with the press's button, modifiers, mesh and view: the moment to finish
221
+ * what the gesture drew, such as a selection box.
222
+ */
223
+ | {
224
+ readonly _tag: 'GestureEnd';
225
+ readonly button: number;
226
+ readonly modifiers: Modifiers;
227
+ readonly key?: string;
228
+ readonly view?: string;
229
+ }
212
230
  /**
213
231
  * A gesture is about to begin on a camera that has turned by itself (`autoRotate`):
214
232
  * where it got to, so the Model can keep it (`autoRotated(camera, detail)`) and the
@@ -273,6 +291,19 @@ export type SceneEvent =
273
291
  readonly maxForceDirection: Vec3;
274
292
  readonly maxForceMagnitude: number;
275
293
  }
294
+ /**
295
+ * A world on `physics({ until })` reached that step and waits there: its run, its
296
+ * step count, and a checksum of every body's pose, which peers running the same inputs
297
+ * share (ADR-014). It comes after the collisions of the steps that led there. At
298
+ * `physics({ snapshotAt })` it carries a snapshot of the world, to start another `from`.
299
+ */
300
+ | {
301
+ readonly _tag: 'PhysicsReached';
302
+ readonly run: number;
303
+ readonly step: number;
304
+ readonly checksum: string;
305
+ readonly snapshot?: string;
306
+ }
276
307
  /**
277
308
  * A measured label's laid-out size changed (`scene({ measure })`, after its glyphs are
278
309
  * laid out): in its own units, before its scale, for sizing what goes behind it, as
@@ -405,14 +436,17 @@ export const createPicker = (graph: SceneGraph): Picker => {
405
436
  const rayOrigin = new Vector3();
406
437
  const rayDirection = new Vector3();
407
438
 
408
- /** The nearest keyed hit along the raycaster's ray, reporting `view` when there is one. */
409
- const nearestHit = (view: string | undefined): Hit | undefined => {
439
+ /**
440
+ * The nearest keyed hit in `on` (the scene's graph or its HUD's) along the raycaster's
441
+ * ray, reporting `view` when there is one.
442
+ */
443
+ const nearestHit = (on: SceneGraph, view: string | undefined): Hit | undefined => {
410
444
  // Recursive, so an interactive group makes every mesh inside it pickable.
411
- for (const intersection of raycaster.intersectObjects(graph.interactiveObjects(), true)) {
445
+ for (const intersection of raycaster.intersectObjects(on.interactiveObjects(), true)) {
412
446
  // Hidden things are not hit: a level of detail not showing, as three's `LOD.raycast`.
413
447
  if (!shown(intersection.object)) continue;
414
- const owner = keyedOwner(graph, intersection.object);
415
- const key = owner === undefined ? undefined : graph.keyOf(owner);
448
+ const owner = keyedOwner(on, intersection.object);
449
+ const key = owner === undefined ? undefined : on.keyOf(owner);
416
450
  if (owner !== undefined && key !== undefined) {
417
451
  normal.set(0, 0, 0);
418
452
  if (intersection.face) {
@@ -421,13 +455,13 @@ export const createPicker = (graph: SceneGraph): Picker => {
421
455
  const instance =
422
456
  intersection.instanceId === undefined
423
457
  ? undefined
424
- : graph.instanceKeyOf(intersection.object, intersection.instanceId);
458
+ : on.instanceKeyOf(intersection.object, intersection.instanceId);
425
459
  return {
426
460
  key,
427
461
  ...(instance === undefined ? {} : { instance }),
428
462
  ...partOf(owner, intersection.object),
429
463
  ...(view === undefined ? {} : { view }),
430
- path: pathOf(graph, owner),
464
+ path: pathOf(on, owner),
431
465
  point: toVec3(intersection.point),
432
466
  distance: intersection.distance,
433
467
  normal: toVec3(normal),
@@ -439,6 +473,17 @@ export const createPicker = (graph: SceneGraph): Picker => {
439
473
  return undefined;
440
474
  };
441
475
 
476
+ /** A hit on the HUD's interactive meshes, which are over everything else. */
477
+ const hudHit = (x: number, y: number): Hit | undefined => {
478
+ const hud = graph.hud();
479
+ if (hud === undefined || hud.graph.interactiveObjects().length === 0) return undefined;
480
+ hud.graph.scene.updateMatrixWorld();
481
+ hud.graph.camera.updateMatrixWorld();
482
+ ndc.set(x, y);
483
+ raycaster.setFromCamera(ndc, hud.graph.camera);
484
+ return nearestHit(hud.graph, undefined);
485
+ };
486
+
442
487
  const currentRay = (): Ray => ({
443
488
  origin: toVec3(raycaster.ray.origin),
444
489
  direction: toVec3(raycaster.ray.direction),
@@ -472,10 +517,12 @@ export const createPicker = (graph: SceneGraph): Picker => {
472
517
  holdCameras: graph.holdCameras,
473
518
  turned: graph.turned,
474
519
  pick: (x, y) => {
520
+ const hud = hudHit(x, y);
521
+ if (hud !== undefined) return hud;
475
522
  if (graph.interactiveObjects().length === 0) return undefined;
476
523
  const view = aim(x, y);
477
524
  if (view === false) return undefined;
478
- return nearestHit(view);
525
+ return nearestHit(graph, view);
479
526
  },
480
527
  pickRay: (ray) => {
481
528
  if (graph.interactiveObjects().length === 0) return undefined;
@@ -484,7 +531,7 @@ export const createPicker = (graph: SceneGraph): Picker => {
484
531
  rayOrigin.fromArray(ray.origin),
485
532
  rayDirection.fromArray(ray.direction).normalize(),
486
533
  );
487
- return nearestHit(undefined);
534
+ return nearestHit(graph, undefined);
488
535
  },
489
536
  viewport: graph.viewport,
490
537
  };
@@ -502,14 +549,24 @@ export type PointerTracker = {
502
549
 
503
550
  const none: ReadonlyArray<SceneEvent> = [];
504
551
 
505
- /** A click moving further than this, in CSS pixels, is a drag and never a miss. */
506
- const missTolerance = 2;
552
+ /**
553
+ * How far, in CSS pixels, a press on nothing may travel and still be a miss rather than a
554
+ * drag. React Three Fiber allows 2 (`events.ts`, `delta <= 2`, at pinned `d74622e`), but
555
+ * measures it on the browser's `click`, which a browser only synthesises from a tap that
556
+ * kept within its own, larger touch slop. This kernel works from pointer down and up, so
557
+ * it gives a finger its own allowance: a steady tap wanders a few pixels, a drag far more.
558
+ */
559
+ const missTolerances = { mouse: 2, pen: 2, touch: 10 } as const satisfies Record<
560
+ PointerDevice,
561
+ number
562
+ >;
507
563
 
508
564
  type Press = {
509
565
  readonly key: string | undefined;
510
566
  readonly button: number;
511
567
  readonly x: number;
512
568
  readonly y: number;
569
+ readonly device: PointerDevice;
513
570
  };
514
571
 
515
572
  /** The press a gesture began with: its button, modifiers, mesh and region. */
@@ -532,6 +589,8 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
532
589
  const pressedPointers = new Map<number, Point>();
533
590
  let primary: number | undefined;
534
591
  let gesturePress: GesturePress | undefined;
592
+ // Whether the gesture press has moved: only then does its release end a gesture.
593
+ let gestureMoved = false;
535
594
  let hovered: string | undefined;
536
595
  // The latest press, kept after release: double-click and context-menu events follow it.
537
596
  let press: Press | undefined;
@@ -569,7 +628,10 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
569
628
  }
570
629
  return;
571
630
  }
572
- if (from?.button === event.button && travel(from, event.x, event.y) <= missTolerance) {
631
+ if (
632
+ from?.button === event.button &&
633
+ travel(from, event.x, event.y) <= missTolerances[from.device]
634
+ ) {
573
635
  events.push({ _tag: 'PointerMissed', button: event.button });
574
636
  }
575
637
  };
@@ -624,6 +686,7 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
624
686
  pressedPointers.clear();
625
687
  primary = id;
626
688
  pressedPointers.set(id, { x: event.x, y: event.y });
689
+ gestureMoved = false;
627
690
  const region = picker.region(event.x, event.y);
628
691
  reportTurned(region, events);
629
692
  // Cameras turning by themselves hold still while it is pressed, as `OrbitControls`.
@@ -641,6 +704,7 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
641
704
  const from = pressedPointers.get(id);
642
705
  if (from === undefined || gesturePress === undefined) return;
643
706
  pressedPointers.set(id, to);
707
+ gestureMoved = true;
644
708
  const { view, width, height, x, y } = gesturePress;
645
709
  const region = { ...(view === undefined ? {} : { view }), width, height, x, y };
646
710
  const moved = pixels(from, to);
@@ -680,11 +744,22 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
680
744
  });
681
745
  };
682
746
 
683
- const endGestures = (): void => {
747
+ const endGestures = (events: Array<SceneEvent>): void => {
684
748
  if (gesturePress !== undefined) picker.holdCameras(false);
749
+ if (gesturePress !== undefined && gestureMoved) {
750
+ const { button, modifiers, key, view } = gesturePress;
751
+ events.push({
752
+ _tag: 'GestureEnd',
753
+ button,
754
+ modifiers,
755
+ ...(key === undefined ? {} : { key }),
756
+ ...(view === undefined ? {} : { view }),
757
+ });
758
+ }
685
759
  pressedPointers.clear();
686
760
  primary = undefined;
687
761
  gesturePress = undefined;
762
+ gestureMoved = false;
688
763
  };
689
764
 
690
765
  /**
@@ -714,7 +789,7 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
714
789
  if (isSecondary(event, id)) return secondary(event, id);
715
790
  const events: Array<SceneEvent> = [];
716
791
  if (event._tag === 'PointerLeave') {
717
- endGestures();
792
+ endGestures(events);
718
793
  // A cancelled or abandoned press still ends its drag, at the last known position.
719
794
  if (position !== undefined) endDrag(captured(), position.x, position.y, events);
720
795
  dragged = false;
@@ -738,7 +813,13 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
738
813
  moveGesture(id, event, events);
739
814
  break;
740
815
  case 'PointerDown':
741
- press = { key: hit?.key, button: event.button, x: event.x, y: event.y };
816
+ press = {
817
+ key: hit?.key,
818
+ button: event.button,
819
+ x: event.x,
820
+ y: event.y,
821
+ device: event.device ?? 'mouse',
822
+ };
742
823
  pressing = true;
743
824
  dragged = false;
744
825
  if (hit !== undefined) events.push({ _tag: 'PointerDown', button: event.button, ...hit });
@@ -748,7 +829,7 @@ export const createPointerTracker = (picker: Picker): PointerTracker => {
748
829
  release(hit, event, events);
749
830
  // Any release ends capture: a mouse's buttons share one pointer.
750
831
  endDrag(drag, event.x, event.y, events);
751
- endGestures();
832
+ endGestures(events);
752
833
  break;
753
834
  case 'DoubleClick':
754
835
  if (hit !== undefined && pressed(hit)) {