@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
@@ -1,3 +1,72 @@
1
+ import {
2
+ seededRandom,
3
+ edgePoints,
4
+ edgeLine,
5
+ animatedLook
6
+ } from "./index-ttwy0znv.js";
7
+ var __defProp = Object.defineProperty;
8
+ var __returnValue = (v) => v;
9
+ function __exportSetter(name, newValue) {
10
+ this[name] = __returnValue.bind(null, newValue);
11
+ }
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, {
15
+ get: all[name],
16
+ enumerable: true,
17
+ configurable: true,
18
+ set: __exportSetter.bind(all, name)
19
+ });
20
+ };
21
+
22
+ // src/kernel/history.ts
23
+ var exports_history = {};
24
+ __export(exports_history, {
25
+ branches: () => branches,
26
+ canRedo: () => canRedo,
27
+ canUndo: () => canUndo,
28
+ chooseBranch: () => chooseBranch,
29
+ depth: () => depth,
30
+ jumpTo: () => jumpTo,
31
+ make: () => make,
32
+ pathTo: () => pathTo,
33
+ present: () => present,
34
+ presentEntry: () => presentEntry,
35
+ record: () => record,
36
+ redo: () => redo,
37
+ undo: () => undo
38
+ });
39
+
40
+ // src/kernel/lockstep.ts
41
+ var exports_lockstep = {};
42
+ __export(exports_lockstep, {
43
+ admit: () => admit,
44
+ advance: () => advance,
45
+ join: () => join,
46
+ leaderAt: () => leaderAt,
47
+ leave: () => leave,
48
+ make: () => make2,
49
+ membersAt: () => membersAt,
50
+ receive: () => receive,
51
+ remove: () => remove,
52
+ send: () => send,
53
+ submit: () => submit,
54
+ waitingOn: () => waitingOn,
55
+ welcomeDue: () => welcomeDue
56
+ });
57
+
58
+ // src/kernel/timeline.ts
59
+ var exports_timeline = {};
60
+ __export(exports_timeline, {
61
+ due: () => due,
62
+ extend: () => extend,
63
+ make: () => make3,
64
+ reach: () => reach,
65
+ restart: () => restart,
66
+ schedule: () => schedule,
67
+ until: () => until
68
+ });
69
+
1
70
  // src/kernel/description.ts
2
71
  var handJoints2 = [
3
72
  "wrist",
@@ -125,6 +194,8 @@ var applyShaded = (material, description, mapChanged = false) => {
125
194
  };
126
195
  var applyBasic = (material, description, textures) => {
127
196
  material.color.setHex(description.color);
197
+ material.depthTest = description.depthTest;
198
+ material.depthWrite = description.depthTest;
128
199
  const mapChanged = applyMap(material, description.map, textures);
129
200
  if (applySurface(material, description) || mapChanged)
130
201
  material.needsUpdate = true;
@@ -489,8 +560,25 @@ var jointsEqual = (a, b) => {
489
560
  var labelTypeEquals = (a, b) => a.text === b.text && a.font === b.font && Object.is(a.fontSize, b.fontSize) && a.color === b.color && Object.is(a.fillOpacity, b.fillOpacity) && Object.is(a.anchorX, b.anchorX) && Object.is(a.anchorY, b.anchorY) && Object.is(a.maxWidth, b.maxWidth) && Object.is(a.lineHeight, b.lineHeight) && Object.is(a.letterSpacing, b.letterSpacing) && a.textAlign === b.textAlign && Object.is(a.outlineWidth, b.outlineWidth) && a.outlineColor === b.outlineColor && Object.is(a.outlineOpacity, b.outlineOpacity) && Object.is(a.outlineBlur, b.outlineBlur) && Object.is(a.curveRadius, b.curveRadius) && a.colorRanges.length === b.colorRanges.length && a.colorRanges.every((range, i) => colorRangeEquals(range, b.colorRanges[i]));
490
561
  var colorRangeEquals = (a, b) => b !== undefined && a.start === b.start && a.color === b.color;
491
562
  var labelMaterialEquals = (a, b) => a === b || a !== null && b !== null && materialRefEquals2(a, b);
563
+ var pointLookEquals = (a, b) => {
564
+ if (a === b)
565
+ return true;
566
+ switch (a._tag) {
567
+ case "Dots":
568
+ return b._tag === "Dots" && a.positions.length === b.positions.length && a.positions.every((point, index) => vec3Equals2(point, b.positions[index] ?? point)) && numbersEqual(a.colors, b.colors) && numbersEqual(a.sizes, b.sizes) && Object.is(a.color, b.color) && Object.is(a.size, b.size) && a.sizeAttenuation === b.sizeAttenuation && a.round === b.round && Object.is(a.opacity, b.opacity) && a.transparent === b.transparent;
569
+ case "Sparkles":
570
+ return b._tag === "Sparkles" && a.count === b.count && Object.is(a.speed, b.speed) && Object.is(a.opacity, b.opacity) && vec3Equals2(a.scale, b.scale) && Object.is(a.size, b.size) && Object.is(a.color, b.color) && vec3Equals2(a.noise, b.noise) && Object.is(a.seed, b.seed);
571
+ case "Stars":
572
+ return b._tag === "Stars" && Object.is(a.radius, b.radius) && Object.is(a.depth, b.depth) && a.count === b.count && Object.is(a.factor, b.factor) && Object.is(a.saturation, b.saturation) && a.fade === b.fade && Object.is(a.speed, b.speed) && Object.is(a.seed, b.seed);
573
+ }
574
+ };
575
+ var trailEquals = (a, b) => a === b || a !== null && b !== null && Object.is(a.width, b.width) && Object.is(a.length, b.length) && Object.is(a.decay, b.decay) && a.local === b.local && Object.is(a.stride, b.stride) && Object.is(a.interval, b.interval) && Object.is(a.color, b.color) && a.taper === b.taper && a.fade === b.fade;
576
+ var edgesEquals = (a, b) => a === b || a !== null && b !== null && Object.is(a.threshold, b.threshold) && Object.is(a.color, b.color) && Object.is(a.width, b.width) && a.worldUnits === b.worldUnits && a.dashed === b.dashed && Object.is(a.dashSize, b.dashSize) && Object.is(a.gapSize, b.gapSize) && Object.is(a.opacity, b.opacity) && a.transparent === b.transparent;
577
+ var lineLookEquals = (a, b) => a.segments === b.segments && Object.is(a.color, b.color) && Object.is(a.width, b.width) && a.worldUnits === b.worldUnits && a.dashed === b.dashed && Object.is(a.dashSize, b.dashSize) && Object.is(a.gapSize, b.gapSize) && Object.is(a.dashScale, b.dashScale) && Object.is(a.dashOffset, b.dashOffset) && Object.is(a.opacity, b.opacity) && a.transparent === b.transparent && (a.points === b.points || a.points.length === b.points.length && a.points.every((point, index) => vec3Equals2(point, b.points[index] ?? point))) && (a.vertexColors === b.vertexColors || a.vertexColors !== null && b.vertexColors !== null && a.vertexColors.length === b.vertexColors.length && a.vertexColors.every((color, index) => Object.is(color, b.vertexColors?.[index])));
492
578
  var shaderGridLookEquals = (a, b) => Object.is(a.size[0], b.size[0]) && Object.is(a.size[1], b.size[1]) && Object.is(a.cellSize, b.cellSize) && Object.is(a.cellThickness, b.cellThickness) && a.cellColor === b.cellColor && Object.is(a.sectionSize, b.sectionSize) && Object.is(a.sectionThickness, b.sectionThickness) && a.sectionColor === b.sectionColor && a.infinite === b.infinite && a.followCamera === b.followCamera && Object.is(a.fadeDistance, b.fadeDistance) && Object.is(a.fadeStrength, b.fadeStrength) && Object.is(a.fadeFrom, b.fadeFrom) && a.side === b.side;
493
- var physicsEquals2 = (a, b) => a === b || a !== null && b !== null && vec3Equals2(a.gravity, b.gravity) && a.paused === b.paused && Object.is(a.timeStep, b.timeStep) && a.interpolate === b.interpolate && a.debug === b.debug && jointsEqual(a.joints, b.joints);
579
+ var assetEquals = (a, b) => b !== undefined && a.kind === b.kind && a.id === b.id && a.url === b.url && a.levels.length === b.levels.length && a.levels.every((level, i) => level === b.levels[i]);
580
+ var assetsEqual2 = (a, b) => a === b || a.length === b.length && a.every((asset, i) => assetEquals(asset, b[i]));
581
+ var physicsEquals2 = (a, b) => a === b || a !== null && b !== null && vec3Equals2(a.gravity, b.gravity) && a.paused === b.paused && Object.is(a.timeStep, b.timeStep) && a.interpolate === b.interpolate && a.until === b.until && a.run === b.run && a.snapshotAt === b.snapshotAt && a.keep === b.keep && (a.from === b.from || a.from !== null && b.from !== null && a.from.step === b.from.step && a.from.snapshot === b.from.snapshot) && a.debug === b.debug && jointsEqual(a.joints, b.joints);
494
582
  var xrAnchorEquals = (a, b) => b !== undefined && a.id === b.id && vec3Equals2(a.position, b.position) && vec3Equals2(a.rotation, b.rotation);
495
583
  var xrEquals2 = (a, b) => a === b || a !== null && b !== null && vec3Equals2(a.origin, b.origin) && Object.is(a.heading, b.heading) && a.referenceSpace === b.referenceSpace && Object.is(a.framebufferScale, b.framebufferScale) && a.hitTest === b.hitTest && a.showInputs === b.showInputs && (a.glide === b.glide || a.glide !== null && b.glide !== null && Object.is(a.glide.speed, b.glide.speed) && a.glide.turn === b.glide.turn && Object.is(a.glide.turnSpeed, b.glide.turnSpeed) && Object.is(a.glide.snapDegrees, b.glide.snapDegrees)) && a.planes === b.planes && a.meshes === b.meshes && a.domOverlay === b.domOverlay && a.anchors.length === b.anchors.length && a.anchors.every((anchor, i) => xrAnchorEquals(anchor, b.anchors[i]));
496
584
  var xrSpaceEquals = (a, b) => {
@@ -658,6 +746,7 @@ var applyTween2 = (object, tween, elapsedSeconds) => {
658
746
  return false;
659
747
  };
660
748
  // src/kernel/build.ts
749
+ import { CatmullRomCurve3, Color as Color2, CubicBezierCurve3, QuadraticBezierCurve3, Vector3 as Vector33 } from "three";
661
750
  var origin = [0, 0, 0];
662
751
  var unitScale = [1, 1, 1];
663
752
  var noRotation = { _tag: "Fixed", euler: origin };
@@ -774,7 +863,8 @@ var basic2 = (options = {}) => ({
774
863
  opacity: options.opacity ?? 1,
775
864
  transparent: options.transparent ?? false,
776
865
  side: options.side ?? "front",
777
- wireframe: options.wireframe ?? false
866
+ wireframe: options.wireframe ?? false,
867
+ depthTest: options.depthTest ?? true
778
868
  });
779
869
  var lambert2 = (options = {}) => ({
780
870
  _tag: "Lambert",
@@ -888,9 +978,105 @@ var group2 = (options) => ({
888
978
  children: options.children ?? [],
889
979
  body: options.body ?? null,
890
980
  follow: options.follow ?? null,
891
- detail: options.detail ?? null
981
+ detail: options.detail ?? null,
982
+ trail: options.trail ?? null
892
983
  });
893
984
  var detailed2 = (options) => ({ distances: options.distances, hysteresis: options.hysteresis ?? 0 });
985
+ var line2 = (options) => ({
986
+ _tag: "Line",
987
+ key: options.key,
988
+ position: options.position ?? origin,
989
+ rotation: options.rotation ?? noRotation,
990
+ scale: options.scale ?? unitScale,
991
+ transition: options.transition ?? instantTransition,
992
+ behaviour: options.behaviour ?? null,
993
+ interactive: options.interactive ?? false,
994
+ points: options.points,
995
+ segments: options.segments ?? false,
996
+ color: options.color ?? 16777215,
997
+ vertexColors: options.vertexColors ?? null,
998
+ width: options.width ?? 1,
999
+ worldUnits: options.worldUnits ?? false,
1000
+ dashed: options.dashed ?? false,
1001
+ dashSize: options.dashSize ?? 1,
1002
+ gapSize: options.gapSize ?? 1,
1003
+ dashScale: options.dashScale ?? 1,
1004
+ dashOffset: options.dashOffset ?? 0,
1005
+ opacity: options.opacity ?? 1,
1006
+ transparent: options.transparent ?? false
1007
+ });
1008
+ var asVector = (point) => new Vector33(point[0], point[1], point[2]);
1009
+ var asVec3 = (point) => [point.x, point.y, point.z];
1010
+ var placed = (options, look) => ({
1011
+ _tag: "Points",
1012
+ key: options.key,
1013
+ position: options.position ?? origin,
1014
+ rotation: options.rotation ?? noRotation,
1015
+ scale: options.scale ?? unitScale,
1016
+ transition: options.transition ?? instantTransition,
1017
+ behaviour: options.behaviour ?? null,
1018
+ look
1019
+ });
1020
+ var points2 = (options) => placed(options, {
1021
+ _tag: "Dots",
1022
+ positions: options.positions,
1023
+ colors: options.colors ?? null,
1024
+ sizes: options.sizes ?? null,
1025
+ color: options.color ?? 16777215,
1026
+ size: options.size ?? 1,
1027
+ sizeAttenuation: options.sizeAttenuation ?? true,
1028
+ round: options.round ?? true,
1029
+ opacity: options.opacity ?? 1,
1030
+ transparent: options.transparent ?? false
1031
+ });
1032
+ var sparkles2 = (options) => {
1033
+ const spread = options.spread ?? 1;
1034
+ const noise = options.noise ?? 1;
1035
+ return placed(options, {
1036
+ _tag: "Sparkles",
1037
+ count: options.count ?? 100,
1038
+ speed: options.speed ?? 1,
1039
+ opacity: options.opacity ?? 1,
1040
+ scale: typeof spread === "number" ? [spread, spread, spread] : spread,
1041
+ size: options.size ?? null,
1042
+ color: options.color ?? 16777215,
1043
+ noise: typeof noise === "number" ? [noise, noise, noise] : noise,
1044
+ seed: options.seed ?? 1
1045
+ });
1046
+ };
1047
+ var stars2 = (options) => placed(options, {
1048
+ _tag: "Stars",
1049
+ radius: options.radius ?? 100,
1050
+ depth: options.depth ?? 50,
1051
+ count: options.count ?? 5000,
1052
+ factor: options.factor ?? 4,
1053
+ saturation: options.saturation ?? 0,
1054
+ fade: options.fade ?? false,
1055
+ speed: options.speed ?? 1,
1056
+ seed: options.seed ?? 1
1057
+ });
1058
+ var catmullRomPoints2 = (points, options = {}) => new CatmullRomCurve3(points.map(asVector), options.closed ?? false, options.curveType ?? "centripetal", options.tension ?? 0.5).getPoints(options.segments ?? 20).map(asVec3);
1059
+ var catmullRomPointAt2 = (points, t, options = {}) => asVec3(new CatmullRomCurve3(points.map(asVector), options.closed ?? false, options.curveType ?? "centripetal", options.tension ?? 0.5).getPoint(Math.min(1, Math.max(0, t))));
1060
+ var catmullRomColors2 = (colors, options = {}) => {
1061
+ const segments = options.segments ?? 20;
1062
+ if (colors.length < 2 || colors.length === segments + 1)
1063
+ return colors;
1064
+ const stops = colors.map((color) => new Color2(color));
1065
+ const first = stops[0];
1066
+ if (options.closed === true && first !== undefined)
1067
+ stops.push(first.clone());
1068
+ const divisions = segments / (stops.length - 1);
1069
+ const at = (index) => (stops[Math.floor(index / divisions)] ?? new Color2).clone().lerp(stops[Math.floor(index / divisions) + 1] ?? new Color2, index % divisions / divisions).getHex();
1070
+ const blended = Array.from({ length: segments - 1 }, (_, step) => at(step + 1));
1071
+ return [colors[0] ?? 0, ...blended, stops.at(-1)?.getHex() ?? 0];
1072
+ };
1073
+ var cubicBezierPoints2 = (options) => new CubicBezierCurve3(asVector(options.start), asVector(options.midA), asVector(options.midB), asVector(options.end)).getPoints(options.segments ?? 20).map(asVec3);
1074
+ var quadraticBezierPoints2 = (options) => {
1075
+ const [, startY] = options.start;
1076
+ const [endX, , endZ] = options.end;
1077
+ const mid = options.mid ?? [endX, startY, endZ];
1078
+ return new QuadraticBezierCurve3(asVector(options.start), asVector(mid), asVector(options.end)).getPoints(options.segments ?? 20).map(asVec3);
1079
+ };
894
1080
  var headSpace2 = () => ({ _tag: "Head" });
895
1081
  var jointSpace2 = (hand, joint) => ({
896
1082
  _tag: "Joint",
@@ -925,7 +1111,31 @@ var mesh2 = (options) => ({
925
1111
  interactive: options.interactive ?? false,
926
1112
  castShadow: options.castShadow ?? false,
927
1113
  receiveShadow: options.receiveShadow ?? false,
928
- body: options.body ?? null
1114
+ body: options.body ?? null,
1115
+ edges: options.edges ?? null,
1116
+ trail: options.trail ?? null
1117
+ });
1118
+ var trail2 = (options = {}) => ({
1119
+ width: options.width ?? 0.2,
1120
+ length: options.length ?? 1,
1121
+ decay: options.decay ?? 1,
1122
+ local: options.local ?? false,
1123
+ stride: options.stride ?? 0,
1124
+ interval: options.interval ?? 1,
1125
+ color: options.color ?? 16738740,
1126
+ taper: options.taper ?? "none",
1127
+ fade: options.fade ?? false
1128
+ });
1129
+ var edges2 = (options = {}) => ({
1130
+ threshold: options.threshold ?? 15,
1131
+ color: options.color ?? 16777215,
1132
+ width: options.width ?? 1,
1133
+ worldUnits: options.worldUnits ?? false,
1134
+ dashed: options.dashed ?? false,
1135
+ dashSize: options.dashSize ?? 1,
1136
+ gapSize: options.gapSize ?? 1,
1137
+ opacity: options.opacity ?? 1,
1138
+ transparent: options.transparent ?? false
929
1139
  });
930
1140
  var body2 = (options = {}) => ({
931
1141
  type: options.type ?? "dynamic",
@@ -979,6 +1189,11 @@ var physics2 = (options = {}) => ({
979
1189
  paused: options.paused ?? false,
980
1190
  timeStep: options.timeStep ?? 1 / 60,
981
1191
  interpolate: options.interpolate ?? true,
1192
+ until: options.until ?? null,
1193
+ run: options.run ?? 0,
1194
+ snapshotAt: options.snapshotAt ?? null,
1195
+ from: options.from ?? null,
1196
+ keep: options.keep ?? 0,
982
1197
  debug: options.debug ?? false,
983
1198
  joints: options.joints ?? noJoints
984
1199
  });
@@ -1280,6 +1495,13 @@ var contactShadows2 = (options = {}) => {
1280
1495
  };
1281
1496
  var noOverlays = [];
1282
1497
  var noMeasures = [];
1498
+ var noAssets = [];
1499
+ var noLevels = [];
1500
+ var assetOf = (kind) => ({ id, url, levels }) => ({ kind, id, url, levels: levels ?? noLevels });
1501
+ var modelAsset2 = assetOf("model");
1502
+ var textureAsset2 = assetOf("texture");
1503
+ var environmentAsset2 = assetOf("environment");
1504
+ var fontAsset2 = assetOf("font");
1283
1505
  var defaultZIndexRange = [16777271, 0];
1284
1506
  var overlay2 = (options) => ({
1285
1507
  key: options.key,
@@ -1291,11 +1513,17 @@ var overlay2 = (options) => ({
1291
1513
  zIndexRange: options.zIndexRange ?? defaultZIndexRange,
1292
1514
  view: options.view ?? null
1293
1515
  });
1516
+ var hud2 = (options) => ({
1517
+ alignment: options.alignment ?? "bottom-right",
1518
+ margin: options.margin ?? [80, 80],
1519
+ children: options.children ?? []
1520
+ });
1294
1521
  var scene2 = (options) => ({
1295
1522
  camera: options.camera ?? perspectiveCamera2(),
1296
1523
  gestures: options.gestures ?? "page",
1297
1524
  views: options.views ?? noViews,
1298
1525
  renderTextures: options.renderTextures ?? noRenderTextures,
1526
+ hud: options.hud ?? null,
1299
1527
  overlays: options.overlays ?? noOverlays,
1300
1528
  environment: options.environment ?? null,
1301
1529
  sky: options.sky ?? null,
@@ -1303,6 +1531,7 @@ var scene2 = (options) => ({
1303
1531
  physics: options.physics ?? null,
1304
1532
  xr: options.xr ?? null,
1305
1533
  measure: options.measure ?? noMeasures,
1534
+ assets: options.assets ?? noAssets,
1306
1535
  render: options.render ?? defaultRenderSettings,
1307
1536
  resources: {
1308
1537
  geometries: options.resources?.geometries ?? noResources.geometries,
@@ -1347,6 +1576,10 @@ var indexScene2 = (description, behaviours = new Set) => {
1347
1576
  if (duplicateView !== undefined) {
1348
1577
  return { _tag: "Invalid", error: { _tag: "DuplicateViewKey", key: duplicateView } };
1349
1578
  }
1579
+ const duplicateAsset = firstDuplicateInstance(description.assets.map((asset) => ({ key: asset.id })));
1580
+ if (duplicateAsset !== undefined) {
1581
+ return { _tag: "Invalid", error: { _tag: "DuplicateAssetId", id: duplicateAsset } };
1582
+ }
1350
1583
  const duplicateOverlay = firstDuplicateInstance(description.overlays);
1351
1584
  if (duplicateOverlay !== undefined) {
1352
1585
  return { _tag: "Invalid", error: { _tag: "DuplicateOverlayKey", key: duplicateOverlay } };
@@ -1480,6 +1713,8 @@ var changedFields2 = (prev, next) => {
1480
1713
  fields.push("follow");
1481
1714
  if (!detailEquals(prev.detail, next.detail))
1482
1715
  fields.push("detail");
1716
+ if (!trailEquals(prev.trail, next.trail))
1717
+ fields.push("trail");
1483
1718
  return fields;
1484
1719
  }
1485
1720
  case "Mesh": {
@@ -1507,6 +1742,10 @@ var changedFields2 = (prev, next) => {
1507
1742
  fields.push("receiveShadow");
1508
1743
  if (!bodyEquals2(prev.body, next.body))
1509
1744
  fields.push("body");
1745
+ if (!edgesEquals(prev.edges, next.edges))
1746
+ fields.push("edges");
1747
+ if (!trailEquals(prev.trail, next.trail))
1748
+ fields.push("trail");
1510
1749
  return fields;
1511
1750
  }
1512
1751
  case "Instances": {
@@ -1662,6 +1901,42 @@ var changedFields2 = (prev, next) => {
1662
1901
  fields.push("shadowBias");
1663
1902
  return fields;
1664
1903
  }
1904
+ case "Points": {
1905
+ if (prev._tag !== "Points")
1906
+ return;
1907
+ if (!behaviourEquals2(prev.behaviour, next.behaviour))
1908
+ fields.push("behaviour");
1909
+ if (!vec3Equals2(prev.position, next.position))
1910
+ fields.push("position");
1911
+ if (!rotationEquals2(prev.rotation, next.rotation))
1912
+ fields.push("rotation");
1913
+ if (!vec3Equals2(prev.scale, next.scale))
1914
+ fields.push("scale");
1915
+ if (!transitionEquals2(prev.transition, next.transition))
1916
+ fields.push("transition");
1917
+ if (!pointLookEquals(prev.look, next.look))
1918
+ fields.push("look");
1919
+ return fields;
1920
+ }
1921
+ case "Line": {
1922
+ if (prev._tag !== "Line")
1923
+ return;
1924
+ if (!behaviourEquals2(prev.behaviour, next.behaviour))
1925
+ fields.push("behaviour");
1926
+ if (!vec3Equals2(prev.position, next.position))
1927
+ fields.push("position");
1928
+ if (!rotationEquals2(prev.rotation, next.rotation))
1929
+ fields.push("rotation");
1930
+ if (!vec3Equals2(prev.scale, next.scale))
1931
+ fields.push("scale");
1932
+ if (!transitionEquals2(prev.transition, next.transition))
1933
+ fields.push("transition");
1934
+ if (prev.interactive !== next.interactive)
1935
+ fields.push("interactive");
1936
+ if (!lineLookEquals(prev, next))
1937
+ fields.push("line");
1938
+ return fields;
1939
+ }
1665
1940
  case "ShaderGrid": {
1666
1941
  if (prev._tag !== "ShaderGrid")
1667
1942
  return;
@@ -2352,7 +2627,7 @@ import {
2352
2627
  Plane,
2353
2628
  Ray,
2354
2629
  Spherical,
2355
- Vector3 as Vector33
2630
+ Vector3 as Vector34
2356
2631
  } from "three";
2357
2632
  var defaultOrbit2 = {
2358
2633
  enableRotate: true,
@@ -2379,14 +2654,14 @@ var orbitOptions2 = (options = {}) => ({
2379
2654
  ...options
2380
2655
  });
2381
2656
  var twoPi = 2 * Math.PI;
2382
- var offset = new Vector33;
2657
+ var offset = new Vector34;
2383
2658
  var spherical = new Spherical;
2384
- var target = new Vector33;
2385
- var position2 = new Vector33;
2386
- var axis = new Vector33;
2387
- var panOffset = new Vector33;
2659
+ var target = new Vector34;
2660
+ var position2 = new Vector34;
2661
+ var axis = new Vector34;
2662
+ var panOffset = new Vector34;
2388
2663
  var basis = new Matrix42;
2389
- var worldUp = new Vector33(0, 1, 0);
2664
+ var worldUp = new Vector34(0, 1, 0);
2390
2665
  var toVec3 = (vector) => [vector.x, vector.y, vector.z];
2391
2666
  var clampAzimuth = (theta, options) => {
2392
2667
  let min = options.minAzimuthAngle;
@@ -2464,9 +2739,9 @@ var panCamera2 = (camera, deltaX, deltaY, area, options = defaultOrbit2) => {
2464
2739
  return place(camera, options, zoomOf(camera, options));
2465
2740
  };
2466
2741
  var tiltLimit = Math.cos(70 * MathUtils.DEG2RAD);
2467
- var pointer = new Vector33;
2468
- var before = new Vector33;
2469
- var forward = new Vector33;
2742
+ var pointer = new Vector34;
2743
+ var before = new Vector34;
2744
+ var forward = new Vector34;
2470
2745
  var groundRay = new Ray;
2471
2746
  var ground = new Plane;
2472
2747
  var hostCamera = (camera, cursor, zoom) => {
@@ -2568,7 +2843,7 @@ var orbitKey2 = (camera, key, modifiers, area, options = defaultOrbit2) => {
2568
2843
  return rotating ? turned(-turn, 0) : panCamera2(camera, -step, 0, area, options);
2569
2844
  }
2570
2845
  };
2571
- var corner = new Vector33;
2846
+ var corner = new Vector34;
2572
2847
  var view = new Matrix42;
2573
2848
  var fitDistance = (camera, box, area, margin) => {
2574
2849
  const largest = Math.max(box.max[0] - box.min[0], box.max[1] - box.min[1], box.max[2] - box.min[2]);
@@ -2621,12 +2896,12 @@ import {
2621
2896
  PerspectiveCamera as PerspectiveCamera2,
2622
2897
  Raycaster,
2623
2898
  Vector2 as Vector22,
2624
- Vector3 as Vector34
2899
+ Vector3 as Vector35
2625
2900
  } from "three";
2626
- var point = new Vector34;
2627
- var cameraPosition = new Vector34;
2628
- var direction = new Vector34;
2629
- var toPoint = new Vector34;
2901
+ var point = new Vector35;
2902
+ var cameraPosition = new Vector35;
2903
+ var direction = new Vector35;
2904
+ var toPoint = new Vector35;
2630
2905
  var ndc = new Vector22;
2631
2906
  var raycaster = new Raycaster;
2632
2907
  var lensFor = (graph, overlay) => {
@@ -2701,7 +2976,7 @@ import {
2701
2976
  Euler as Euler3,
2702
2977
  Matrix4 as Matrix43,
2703
2978
  Quaternion as Quaternion3,
2704
- Vector3 as Vector35
2979
+ Vector3 as Vector36
2705
2980
  } from "three";
2706
2981
  var interactionBits2 = (groups) => {
2707
2982
  const mask = (list) => list.reduce((bits, group) => bits | 1 << group, 0);
@@ -2710,8 +2985,40 @@ var interactionBits2 = (groups) => {
2710
2985
  const filters = groups.filters === null ? 65535 : mask(groups.filters);
2711
2986
  return (mask(groups.memberships) << 16 | filters) >>> 0;
2712
2987
  };
2988
+ var run = (first, count) => Array.from({ length: count }, (_, i) => String.fromCharCode(first.charCodeAt(0) + i));
2989
+ var digits = [...run("A", 26), ...run("a", 26), ...run("0", 10), "+", "/"];
2990
+ var digitValues = new Map(digits.map((char, i) => [char, i]));
2991
+ var digit = (value) => digits[value] ?? "";
2992
+ var snapshotText2 = (bytes) => {
2993
+ const out = [];
2994
+ for (let at = 0;at < bytes.length; at += 3) {
2995
+ const a = bytes[at] ?? 0;
2996
+ const b = bytes[at + 1] ?? 0;
2997
+ const c = bytes[at + 2] ?? 0;
2998
+ const left = bytes.length - at;
2999
+ out.push(digit(a >> 2), digit((a & 3) << 4 | b >> 4), left > 1 ? digit((b & 15) << 2 | c >> 6) : "=", left > 2 ? digit(c & 63) : "=");
3000
+ }
3001
+ return out.join("");
3002
+ };
3003
+ var snapshotBytes2 = (text) => {
3004
+ const digits = text.replace(/=+$/, "");
3005
+ const bytes = new Uint8Array(Math.floor(digits.length * 3 / 4));
3006
+ let bits = 0;
3007
+ let held = 0;
3008
+ let at = 0;
3009
+ for (const char of digits) {
3010
+ bits = bits << 6 | (digitValues.get(char) ?? 0);
3011
+ held += 6;
3012
+ if (held >= 8) {
3013
+ held -= 8;
3014
+ bytes[at] = bits >> held & 255;
3015
+ at += 1;
3016
+ }
3017
+ }
3018
+ return bytes;
3019
+ };
2713
3020
  var toVec32 = (vector) => [vector.x, vector.y, vector.z];
2714
- var size = new Vector35;
3021
+ var size = new Vector36;
2715
3022
  var scaledPositions = (geometry, scale) => {
2716
3023
  const positions = geometry.getAttribute("position");
2717
3024
  const points = new Float32Array(positions.count * 3);
@@ -2730,7 +3037,7 @@ var triangles = (geometry) => {
2730
3037
  };
2731
3038
  var speck = {
2732
3039
  shape: { _tag: "Ball", radius: 0.001 },
2733
- offset: new Vector35
3040
+ offset: new Vector36
2734
3041
  };
2735
3042
  var shapeFor = (geometry, collider, scale) => {
2736
3043
  if (geometry.getAttribute("position") === undefined)
@@ -2744,7 +3051,7 @@ var shapeFor = (geometry, collider, scale) => {
2744
3051
  box.getSize(size).multiply(scale).multiplyScalar(0.5);
2745
3052
  return {
2746
3053
  shape: { _tag: "Cuboid", halfExtents: toVec32(size) },
2747
- offset: box.getCenter(new Vector35).multiply(scale)
3054
+ offset: box.getCenter(new Vector36).multiply(scale)
2748
3055
  };
2749
3056
  }
2750
3057
  case "ball": {
@@ -2761,7 +3068,7 @@ var shapeFor = (geometry, collider, scale) => {
2761
3068
  case "hull":
2762
3069
  return {
2763
3070
  shape: { _tag: "Hull", points: scaledPositions(geometry, scale) },
2764
- offset: new Vector35
3071
+ offset: new Vector36
2765
3072
  };
2766
3073
  case "trimesh":
2767
3074
  return {
@@ -2770,7 +3077,7 @@ var shapeFor = (geometry, collider, scale) => {
2770
3077
  vertices: scaledPositions(geometry, scale),
2771
3078
  indices: triangles(geometry)
2772
3079
  },
2773
- offset: new Vector35
3080
+ offset: new Vector36
2774
3081
  };
2775
3082
  }
2776
3083
  };
@@ -2815,11 +3122,11 @@ function describedCollider(collider, scale) {
2815
3122
  }
2816
3123
  var relative = new Matrix43;
2817
3124
  var bodyInverse = new Matrix43;
2818
- var where = new Vector35;
3125
+ var where = new Vector36;
2819
3126
  var turn = new Quaternion3;
2820
- var relativeScale = new Vector35;
2821
- var worldScale = new Vector35;
2822
- var worldPosition = new Vector35;
3127
+ var relativeScale = new Vector36;
3128
+ var worldScale = new Vector36;
3129
+ var worldPosition = new Vector36;
2823
3130
  var worldTurn = new Quaternion3;
2824
3131
  var isMesh = (object) => ("isMesh" in object) && object.isMesh === true;
2825
3132
  function placedCollider2(child, collider, body) {
@@ -3075,7 +3382,7 @@ var stageBounds2 = (box) => {
3075
3382
  import { Box3, EquirectangularReflectionMapping, UVMapping } from "three";
3076
3383
 
3077
3384
  // src/kernel/pointer.ts
3078
- import { Raycaster as Raycaster2, Vector2 as Vector23, Vector3 as Vector36 } from "three";
3385
+ import { Raycaster as Raycaster2, Vector2 as Vector23, Vector3 as Vector37 } from "three";
3079
3386
  var noModifiers2 = { shift: false, control: false, meta: false, alt: false };
3080
3387
  var pointOnPlane2 = (ray, plane) => {
3081
3388
  const [nx, ny, nz] = plane.normal;
@@ -3116,7 +3423,7 @@ var shown = (object) => {
3116
3423
  var createPicker2 = (graph) => {
3117
3424
  const raycaster = new Raycaster2;
3118
3425
  const ndc = new Vector23;
3119
- const normal = new Vector36;
3426
+ const normal = new Vector37;
3120
3427
  const aim = (x, y) => {
3121
3428
  graph.scene.updateMatrixWorld();
3122
3429
  const views = graph.views();
@@ -3144,26 +3451,26 @@ var createPicker2 = (graph) => {
3144
3451
  }
3145
3452
  return false;
3146
3453
  };
3147
- const rayOrigin = new Vector36;
3148
- const rayDirection = new Vector36;
3149
- const nearestHit = (view) => {
3150
- for (const intersection of raycaster.intersectObjects(graph.interactiveObjects(), true)) {
3454
+ const rayOrigin = new Vector37;
3455
+ const rayDirection = new Vector37;
3456
+ const nearestHit = (on, view) => {
3457
+ for (const intersection of raycaster.intersectObjects(on.interactiveObjects(), true)) {
3151
3458
  if (!shown(intersection.object))
3152
3459
  continue;
3153
- const owner = keyedOwner(graph, intersection.object);
3154
- const key = owner === undefined ? undefined : graph.keyOf(owner);
3460
+ const owner = keyedOwner(on, intersection.object);
3461
+ const key = owner === undefined ? undefined : on.keyOf(owner);
3155
3462
  if (owner !== undefined && key !== undefined) {
3156
3463
  normal.set(0, 0, 0);
3157
3464
  if (intersection.face) {
3158
3465
  normal.copy(intersection.face.normal).transformDirection(intersection.object.matrixWorld);
3159
3466
  }
3160
- const instance = intersection.instanceId === undefined ? undefined : graph.instanceKeyOf(intersection.object, intersection.instanceId);
3467
+ const instance = intersection.instanceId === undefined ? undefined : on.instanceKeyOf(intersection.object, intersection.instanceId);
3161
3468
  return {
3162
3469
  key,
3163
3470
  ...instance === undefined ? {} : { instance },
3164
3471
  ...partOf(owner, intersection.object),
3165
3472
  ...view === undefined ? {} : { view },
3166
- path: pathOf(graph, owner),
3473
+ path: pathOf(on, owner),
3167
3474
  point: toVec33(intersection.point),
3168
3475
  distance: intersection.distance,
3169
3476
  normal: toVec33(normal),
@@ -3174,6 +3481,16 @@ var createPicker2 = (graph) => {
3174
3481
  }
3175
3482
  return;
3176
3483
  };
3484
+ const hudHit = (x, y) => {
3485
+ const hud = graph.hud();
3486
+ if (hud === undefined || hud.graph.interactiveObjects().length === 0)
3487
+ return;
3488
+ hud.graph.scene.updateMatrixWorld();
3489
+ hud.graph.camera.updateMatrixWorld();
3490
+ ndc.set(x, y);
3491
+ raycaster.setFromCamera(ndc, hud.graph.camera);
3492
+ return nearestHit(hud.graph, undefined);
3493
+ };
3177
3494
  const currentRay = () => ({
3178
3495
  origin: toVec33(raycaster.ray.origin),
3179
3496
  direction: toVec33(raycaster.ray.direction)
@@ -3200,31 +3517,35 @@ var createPicker2 = (graph) => {
3200
3517
  holdCameras: graph.holdCameras,
3201
3518
  turned: graph.turned,
3202
3519
  pick: (x, y) => {
3520
+ const hud = hudHit(x, y);
3521
+ if (hud !== undefined)
3522
+ return hud;
3203
3523
  if (graph.interactiveObjects().length === 0)
3204
3524
  return;
3205
3525
  const view = aim(x, y);
3206
3526
  if (view === false)
3207
3527
  return;
3208
- return nearestHit(view);
3528
+ return nearestHit(graph, view);
3209
3529
  },
3210
3530
  pickRay: (ray) => {
3211
3531
  if (graph.interactiveObjects().length === 0)
3212
3532
  return;
3213
3533
  graph.scene.updateMatrixWorld();
3214
3534
  raycaster.set(rayOrigin.fromArray(ray.origin), rayDirection.fromArray(ray.direction).normalize());
3215
- return nearestHit(undefined);
3535
+ return nearestHit(graph, undefined);
3216
3536
  },
3217
3537
  viewport: graph.viewport
3218
3538
  };
3219
3539
  };
3220
3540
  var none2 = [];
3221
- var missTolerance = 2;
3541
+ var missTolerances = { mouse: 2, pen: 2, touch: 10 };
3222
3542
  var pointerOf = (input) => ("pointer" in input) && input.pointer !== undefined ? input.pointer : 0;
3223
3543
  var createPointerTracker2 = (picker) => {
3224
3544
  let position;
3225
3545
  const pressedPointers = new Map;
3226
3546
  let primary;
3227
3547
  let gesturePress;
3548
+ let gestureMoved = false;
3228
3549
  let hovered;
3229
3550
  let press;
3230
3551
  let pressing = false;
@@ -3254,7 +3575,7 @@ var createPointerTracker2 = (picker) => {
3254
3575
  }
3255
3576
  return;
3256
3577
  }
3257
- if (from?.button === event.button && travel(from, event.x, event.y) <= missTolerance) {
3578
+ if (from?.button === event.button && travel(from, event.x, event.y) <= missTolerances[from.device]) {
3258
3579
  events.push({ _tag: "PointerMissed", button: event.button });
3259
3580
  }
3260
3581
  };
@@ -3289,6 +3610,7 @@ var createPointerTracker2 = (picker) => {
3289
3610
  pressedPointers.clear();
3290
3611
  primary = id;
3291
3612
  pressedPointers.set(id, { x: event.x, y: event.y });
3613
+ gestureMoved = false;
3292
3614
  const region = picker.region(event.x, event.y);
3293
3615
  reportTurned(region, events);
3294
3616
  picker.holdCameras(true);
@@ -3304,6 +3626,7 @@ var createPointerTracker2 = (picker) => {
3304
3626
  if (from === undefined || gesturePress === undefined)
3305
3627
  return;
3306
3628
  pressedPointers.set(id, to);
3629
+ gestureMoved = true;
3307
3630
  const { view, width, height, x, y } = gesturePress;
3308
3631
  const region = { ...view === undefined ? {} : { view }, width, height, x, y };
3309
3632
  const moved = pixels(from, to);
@@ -3342,12 +3665,23 @@ var createPointerTracker2 = (picker) => {
3342
3665
  }
3343
3666
  });
3344
3667
  };
3345
- const endGestures = () => {
3668
+ const endGestures = (events) => {
3346
3669
  if (gesturePress !== undefined)
3347
3670
  picker.holdCameras(false);
3671
+ if (gesturePress !== undefined && gestureMoved) {
3672
+ const { button, modifiers, key, view } = gesturePress;
3673
+ events.push({
3674
+ _tag: "GestureEnd",
3675
+ button,
3676
+ modifiers,
3677
+ ...key === undefined ? {} : { key },
3678
+ ...view === undefined ? {} : { view }
3679
+ });
3680
+ }
3348
3681
  pressedPointers.clear();
3349
3682
  primary = undefined;
3350
3683
  gesturePress = undefined;
3684
+ gestureMoved = false;
3351
3685
  };
3352
3686
  const secondary = (event, id) => {
3353
3687
  const events = [];
@@ -3367,7 +3701,7 @@ var createPointerTracker2 = (picker) => {
3367
3701
  return secondary(event, id);
3368
3702
  const events = [];
3369
3703
  if (event._tag === "PointerLeave") {
3370
- endGestures();
3704
+ endGestures(events);
3371
3705
  if (position !== undefined)
3372
3706
  endDrag(captured(), position.x, position.y, events);
3373
3707
  dragged = false;
@@ -3392,7 +3726,13 @@ var createPointerTracker2 = (picker) => {
3392
3726
  moveGesture(id, event, events);
3393
3727
  break;
3394
3728
  case "PointerDown":
3395
- press = { key: hit?.key, button: event.button, x: event.x, y: event.y };
3729
+ press = {
3730
+ key: hit?.key,
3731
+ button: event.button,
3732
+ x: event.x,
3733
+ y: event.y,
3734
+ device: event.device ?? "mouse"
3735
+ };
3396
3736
  pressing = true;
3397
3737
  dragged = false;
3398
3738
  if (hit !== undefined)
@@ -3402,7 +3742,7 @@ var createPointerTracker2 = (picker) => {
3402
3742
  case "PointerUp":
3403
3743
  release(hit, event, events);
3404
3744
  endDrag(drag, event.x, event.y, events);
3405
- endGestures();
3745
+ endGestures(events);
3406
3746
  break;
3407
3747
  case "DoubleClick":
3408
3748
  if (hit !== undefined && pressed(hit)) {
@@ -3581,6 +3921,8 @@ var createSceneLoop2 = (options) => {
3581
3921
  emit({ _tag: "Collision", ...collision });
3582
3922
  for (const force of graph.contactForces())
3583
3923
  emit({ _tag: "ContactForce", ...force });
3924
+ for (const reached of graph.physicsReached())
3925
+ emit({ _tag: "PhysicsReached", ...reached });
3584
3926
  const settings = graph.renderSettings();
3585
3927
  if (settings !== configured) {
3586
3928
  renderer.configure(settings);
@@ -3594,6 +3936,11 @@ var createSceneLoop2 = (options) => {
3594
3936
  renderer.render(graph.scene, graph.camera);
3595
3937
  for (const view of views)
3596
3938
  renderer.render(graph.scene, view.camera, view.area);
3939
+ const hud = graph.hud();
3940
+ if (hud !== undefined && renderer.renderOver !== undefined) {
3941
+ renderer.renderOver(hud.graph.scene, hud.graph.camera);
3942
+ hud.graph.drawn();
3943
+ }
3597
3944
  graph.drawn();
3598
3945
  measure();
3599
3946
  const overlays = graph.overlays();
@@ -3678,21 +4025,21 @@ var recordingRendererFactory2 = () => {
3678
4025
  // src/kernel/sceneGraph.ts
3679
4026
  import {
3680
4027
  AmbientLight,
3681
- Color as Color2,
4028
+ Color as Color4,
3682
4029
  AnimationClip,
3683
4030
  AnimationMixer,
3684
4031
  DirectionalLight,
3685
4032
  Group,
3686
4033
  GridHelper,
3687
4034
  HemisphereLight,
3688
- Mesh,
4035
+ Mesh as Mesh2,
3689
4036
  OrthographicCamera as OrthographicCamera4,
3690
4037
  PerspectiveCamera as PerspectiveCamera3,
3691
4038
  PointLight,
3692
4039
  Quaternion as Quaternion6,
3693
4040
  Scene,
3694
4041
  SpotLight,
3695
- Vector3 as Vector39
4042
+ Vector3 as Vector311
3696
4043
  } from "three";
3697
4044
  import { clone as cloneSkinned } from "three/addons/utils/SkeletonUtils.js";
3698
4045
 
@@ -3700,16 +4047,16 @@ import { clone as cloneSkinned } from "three/addons/utils/SkeletonUtils.js";
3700
4047
  import { Euler as Euler5, Quaternion as Quaternion5 } from "three";
3701
4048
 
3702
4049
  // src/kernel/bodySources.ts
3703
- import { Euler as Euler4, Matrix4 as Matrix44, Quaternion as Quaternion4, Vector3 as Vector37 } from "three";
4050
+ import { Euler as Euler4, Matrix4 as Matrix44, Quaternion as Quaternion4, Vector3 as Vector38 } from "three";
3704
4051
  var bodyId = (key, instance) => instance === null ? key : `${key}\x00${instance}`;
3705
- var position3 = new Vector37;
4052
+ var position3 = new Vector38;
3706
4053
  var quaternion2 = new Quaternion4;
3707
- var scale2 = new Vector37;
4054
+ var scale2 = new Vector38;
3708
4055
  var matrix2 = new Matrix44;
3709
4056
  var local = new Matrix44;
3710
4057
  var inverse = new Matrix44;
3711
4058
  var euler2 = new Euler4;
3712
- var ownScale = new Vector37;
4059
+ var ownScale = new Vector38;
3713
4060
  var toPose = (from) => {
3714
4061
  from.decompose(position3, quaternion2, scale2);
3715
4062
  return {
@@ -3867,6 +4214,36 @@ var between = (a, b, alpha) => {
3867
4214
  quaternion: [from.x, from.y, from.z, from.w]
3868
4215
  };
3869
4216
  };
4217
+ var catchUpAfter = 0.25;
4218
+ var mostPerFrame = 1;
4219
+ var checksumBytes = new DataView(new ArrayBuffer(8));
4220
+ var mixByte = (hash, byte) => Math.imul(hash ^ byte, 16777619) >>> 0;
4221
+ var mixNumber = (hash, value) => {
4222
+ checksumBytes.setFloat64(0, value);
4223
+ let mixed = hash;
4224
+ for (let i = 0;i < 8; i++)
4225
+ mixed = mixByte(mixed, checksumBytes.getUint8(i));
4226
+ return mixed;
4227
+ };
4228
+ var mixText = (hash, text) => {
4229
+ let mixed = hash;
4230
+ for (let i = 0;i < text.length; i++) {
4231
+ const code = text.charCodeAt(i);
4232
+ mixed = mixByte(mixByte(mixed, code & 255), code >>> 8);
4233
+ }
4234
+ return mixed;
4235
+ };
4236
+ var checksumOf = (poses) => {
4237
+ let hash = 2166136261;
4238
+ for (const [id, { position, quaternion }] of poses.toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
4239
+ hash = mixText(hash, id);
4240
+ for (const value of position)
4241
+ hash = mixNumber(hash, value);
4242
+ for (const value of quaternion)
4243
+ hash = mixNumber(hash, value);
4244
+ }
4245
+ return hash.toString(16).padStart(8, "0");
4246
+ };
3870
4247
  var remade = (hosted, source, signature) => hosted.source.holder !== source.holder || hosted.source.body.collider !== source.body.collider || !collidersEqual(hosted.source.body.colliders, source.body.colliders) || hosted.signature !== signature;
3871
4248
  var resettled = (a, b) => !bodyEquals2({ ...a, impulse: null }, { ...b, impulse: null });
3872
4249
  var named = (a, b, ids) => {
@@ -3892,6 +4269,13 @@ var createBodyHost = (engine, scene) => {
3892
4269
  const debug = createPhysicsDebug(scene);
3893
4270
  let last;
3894
4271
  let accumulator = 0;
4272
+ let steps = 0;
4273
+ let reported;
4274
+ let snapped;
4275
+ let reached = [];
4276
+ const kept = new Map;
4277
+ let rushing = false;
4278
+ let restored;
3895
4279
  const drawDebug = () => {
3896
4280
  if (world !== undefined && settings?.debug === true)
3897
4281
  debug.show(world.debugLines());
@@ -3907,9 +4291,37 @@ var createBodyHost = (engine, scene) => {
3907
4291
  joined.clear();
3908
4292
  accumulator = 0;
3909
4293
  last = undefined;
4294
+ steps = 0;
4295
+ reported = undefined;
4296
+ snapped = undefined;
4297
+ reached = [];
4298
+ };
4299
+ const make = (active, physics) => {
4300
+ const { from } = physics;
4301
+ const bytes = from === null ? undefined : from.snapshot === null ? kept.get(from.step) : snapshotBytes2(from.snapshot);
4302
+ if (from === null || bytes === undefined || active.restoreWorld === undefined) {
4303
+ kept.clear();
4304
+ return active.createWorld(physics);
4305
+ }
4306
+ for (const step of kept.keys())
4307
+ if (step > from.step || from.snapshot !== null)
4308
+ kept.delete(step);
4309
+ const made = active.restoreWorld(physics, bytes);
4310
+ steps = from.step;
4311
+ rushing = true;
4312
+ restored = { bodies: new Set(made.bodies), joints: new Set(made.joints) };
4313
+ return made.world;
4314
+ };
4315
+ const adopt = (active, source, signature) => {
4316
+ const pose = active.pose(source.id) ?? source.described();
4317
+ source.place(pose);
4318
+ hosted.set(source.id, { source, signature, previous: pose, current: pose, shown: pose });
3910
4319
  };
4320
+ let restarting = false;
3911
4321
  const add = (active, source, signature) => {
3912
- const pose = source.pose();
4322
+ const pose = restarting ? source.described() : source.pose();
4323
+ if (restarting)
4324
+ source.place(pose);
3913
4325
  active.add(source.id, source.body, source.colliders(), pose);
3914
4326
  if (source.body.impulse !== null)
3915
4327
  active.applyImpulse(source.id, source.body.impulse.impulse);
@@ -3919,6 +4331,10 @@ var createBodyHost = (engine, scene) => {
3919
4331
  const syncBody = (active, source) => {
3920
4332
  const existing = hosted.get(source.id);
3921
4333
  const signature = source.signature();
4334
+ if (existing === undefined && restored?.bodies.has(source.id) === true) {
4335
+ adopt(active, source, signature);
4336
+ return;
4337
+ }
3922
4338
  if (existing === undefined || remade(existing, source, signature)) {
3923
4339
  if (existing !== undefined)
3924
4340
  active.remove(source.id);
@@ -3974,11 +4390,22 @@ var createBodyHost = (engine, scene) => {
3974
4390
  const sync = (physics, sources) => {
3975
4391
  if (physics === null || engine === undefined) {
3976
4392
  dispose();
4393
+ kept.clear();
3977
4394
  return;
3978
4395
  }
4396
+ restarting = settings !== null && settings.run !== physics.run;
4397
+ if (restarting)
4398
+ dispose();
3979
4399
  if (world === undefined)
3980
- world = engine.createWorld(physics);
3981
- else if (!physicsEquals2(settings && { ...settings, joints: physics.joints }, physics)) {
4400
+ world = make(engine, physics);
4401
+ else if (!physicsEquals2(settings && {
4402
+ ...settings,
4403
+ joints: physics.joints,
4404
+ until: physics.until,
4405
+ snapshotAt: physics.snapshotAt,
4406
+ from: physics.from,
4407
+ keep: physics.keep
4408
+ }, physics)) {
3982
4409
  world.configure(physics);
3983
4410
  }
3984
4411
  settings = physics;
@@ -3995,7 +4422,22 @@ var createBodyHost = (engine, scene) => {
3995
4422
  world.remove(id);
3996
4423
  hosted.delete(id);
3997
4424
  }
4425
+ if (restored !== undefined) {
4426
+ for (const id of restored.bodies)
4427
+ if (!seen.has(id))
4428
+ world.remove(id);
4429
+ for (const joint of physics.joints) {
4430
+ if (restored.joints.has(joint.key))
4431
+ joined.set(joint.key, joint);
4432
+ }
4433
+ for (const id of restored.joints) {
4434
+ if (!joined.has(id))
4435
+ world.removeJoint(id);
4436
+ }
4437
+ restored = undefined;
4438
+ }
3998
4439
  syncJoints(world, physics.joints);
4440
+ restarting = false;
3999
4441
  drawDebug();
4000
4442
  };
4001
4443
  const kinematicTargets = (active) => {
@@ -4004,11 +4446,8 @@ var createBodyHost = (engine, scene) => {
4004
4446
  active.moveKinematic(id, source.pose());
4005
4447
  }
4006
4448
  };
4007
- const step = (active, seconds) => {
4008
- for (const body of hosted.values())
4009
- body.previous = body.current;
4010
- kinematicTargets(active);
4011
- for (const collision of active.step(seconds)) {
4449
+ const heard = (arrived, active) => {
4450
+ for (const collision of arrived) {
4012
4451
  collisions.push({
4013
4452
  ...named(hosted.get(collision.a)?.source, hosted.get(collision.b)?.source, collision),
4014
4453
  started: collision.started
@@ -4021,22 +4460,99 @@ var createBodyHost = (engine, scene) => {
4021
4460
  ...payload
4022
4461
  });
4023
4462
  }
4463
+ };
4464
+ const step = (active, seconds) => {
4465
+ for (const body of hosted.values())
4466
+ body.previous = body.current;
4467
+ kinematicTargets(active);
4468
+ heard(active.step(seconds), active);
4469
+ for (const [id, body] of hosted)
4470
+ body.current = active.pose(id) ?? body.current;
4471
+ steps += 1;
4472
+ };
4473
+ const collect = (active) => {
4474
+ if (active.received === undefined)
4475
+ return false;
4476
+ const { collisions: arrived, pending } = active.received();
4477
+ heard(arrived, active);
4024
4478
  for (const [id, body] of hosted)
4025
4479
  body.current = active.pose(id) ?? body.current;
4480
+ return pending;
4481
+ };
4482
+ const free = (active, timeStep, delta) => {
4483
+ accumulator += delta;
4484
+ while (accumulator >= timeStep) {
4485
+ step(active, timeStep);
4486
+ accumulator -= timeStep;
4487
+ }
4488
+ collect(active);
4489
+ return accumulator / timeStep;
4490
+ };
4491
+ const snapshotOwed = (physics, until) => physics.snapshotAt !== null && snapped !== physics.snapshotAt && steps <= physics.snapshotAt && physics.snapshotAt <= until;
4492
+ const report = (active, physics, snapshot) => {
4493
+ const wanted = snapshot || physics.keep > 0;
4494
+ const bytes = wanted ? active.snapshot?.() : undefined;
4495
+ if (wanted && active.snapshot !== undefined && bytes === undefined)
4496
+ return false;
4497
+ if (physics.keep > 0 && bytes !== undefined) {
4498
+ kept.delete(steps);
4499
+ kept.set(steps, bytes);
4500
+ for (const step of kept.keys())
4501
+ if (kept.size > physics.keep)
4502
+ kept.delete(step);
4503
+ }
4504
+ const { run } = physics;
4505
+ reached.push({
4506
+ run,
4507
+ step: steps,
4508
+ checksum: checksumOf([...hosted.keys()].flatMap((id) => posed(active, id))),
4509
+ ...snapshot && bytes !== undefined ? { snapshot: snapshotText2(bytes) } : {}
4510
+ });
4511
+ return true;
4512
+ };
4513
+ const driven = (active, physics, until, delta) => {
4514
+ const { timeStep } = physics;
4515
+ const snapping = snapshotOwed(physics, until);
4516
+ const stop = snapping && physics.snapshotAt !== null ? physics.snapshotAt : until;
4517
+ accumulator += delta;
4518
+ const owed = Math.max(0, stop - steps);
4519
+ const paced = Math.floor(accumulator / timeStep);
4520
+ const behind = rushing || owed > Math.ceil(catchUpAfter / timeStep);
4521
+ rushing = false;
4522
+ const count = Math.min(owed, behind ? owed : paced, Math.ceil(mostPerFrame / timeStep));
4523
+ for (let i = 0;i < count; i++)
4524
+ step(active, timeStep);
4525
+ accumulator = Math.max(0, accumulator - count * timeStep);
4526
+ const pending = collect(active);
4527
+ if (steps < stop)
4528
+ return Math.min(1, accumulator / timeStep);
4529
+ if (stop < until) {
4530
+ if (!pending && report(active, physics, true))
4531
+ snapped = stop;
4532
+ return Math.min(1, accumulator / timeStep);
4533
+ }
4534
+ accumulator = 0;
4535
+ if (steps === until && (reported !== until || snapping) && !pending && report(active, physics, snapping)) {
4536
+ reported = until;
4537
+ if (snapping)
4538
+ snapped = until;
4539
+ }
4540
+ return 1;
4541
+ };
4542
+ const posed = (active, id) => {
4543
+ const pose = active.pose(id);
4544
+ return pose === undefined ? [] : [[id, pose]];
4026
4545
  };
4027
4546
  const tick = (elapsed, animating) => {
4028
4547
  const delta = last === undefined ? 0 : Math.min(0.5, Math.max(0, elapsed - last));
4029
4548
  last = elapsed;
4030
- if (world === undefined || settings === null || settings.paused || !animating)
4549
+ if (world === undefined || settings === null || settings.paused)
4550
+ return;
4551
+ if (settings.until === null && !animating)
4031
4552
  return;
4032
- const timeStep = settings.timeStep;
4033
- accumulator += delta;
4034
4553
  scene.updateMatrixWorld();
4035
- while (accumulator >= timeStep) {
4036
- step(world, timeStep);
4037
- accumulator -= timeStep;
4038
- }
4039
- const alpha = settings.interpolate ? accumulator / timeStep : 1;
4554
+ const progress = settings.until === null ? free(world, settings.timeStep, delta) : driven(world, settings, settings.until, delta);
4555
+ const alpha = settings.interpolate ? progress : 1;
4040
4556
  for (const body of hosted.values()) {
4041
4557
  if (body.source.body.type === "dynamic") {
4042
4558
  body.shown = between(body.previous, body.current, alpha);
@@ -4064,15 +4580,23 @@ var createBodyHost = (engine, scene) => {
4064
4580
  contactForces = [];
4065
4581
  return drained;
4066
4582
  },
4583
+ drainReached: () => {
4584
+ if (reached.length === 0)
4585
+ return reached;
4586
+ const drained = reached;
4587
+ reached = [];
4588
+ return drained;
4589
+ },
4590
+ owesSteps: () => world !== undefined && settings !== null && !settings.paused && settings.until !== null && (steps < settings.until || reported !== settings.until || snapshotOwed(settings, settings.until)),
4067
4591
  dispose
4068
4592
  };
4069
4593
  };
4070
4594
 
4071
4595
  // src/kernel/cameraMotion.ts
4072
- import { OrthographicCamera as OrthographicCamera3, Spherical as Spherical2, Vector3 as Vector38 } from "three";
4073
- var createPose = () => ({ target: new Vector38, spherical: new Spherical2, zoom: 1 });
4074
- var offset2 = new Vector38;
4075
- var position4 = new Vector38;
4596
+ import { OrthographicCamera as OrthographicCamera3, Spherical as Spherical2, Vector3 as Vector39 } from "three";
4597
+ var createPose = () => ({ target: new Vector39, spherical: new Spherical2, zoom: 1 });
4598
+ var offset2 = new Vector39;
4599
+ var position4 = new Vector39;
4076
4600
  var readPose = (pose, description) => {
4077
4601
  pose.target.fromArray(description.lookAt);
4078
4602
  offset2.fromArray(description.position).sub(pose.target);
@@ -4113,8 +4637,8 @@ var createCameraMotion = () => {
4113
4637
  const current = createPose();
4114
4638
  const described = createPose();
4115
4639
  const goal = createPose();
4116
- const goalPosition = new Vector38;
4117
- const lookAt = new Vector38;
4640
+ const goalPosition = new Vector39;
4641
+ const lookAt = new Vector39;
4118
4642
  let shown = false;
4119
4643
  let easing = false;
4120
4644
  let damping = 0;
@@ -4204,18 +4728,7 @@ var createCameraMotion = () => {
4204
4728
 
4205
4729
  // src/kernel/cameraShake.ts
4206
4730
  import { SimplexNoise } from "three/addons/math/SimplexNoise.js";
4207
- var seeded = (seed) => {
4208
- let state = seed >>> 0;
4209
- return {
4210
- random: () => {
4211
- state = state + 1831565813 >>> 0;
4212
- let t = state;
4213
- t = Math.imul(t ^ t >>> 15, t | 1);
4214
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
4215
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
4216
- }
4217
- };
4218
- };
4731
+ var seeded = (seed) => ({ random: seededRandom(seed) });
4219
4732
  var yawNoise = new SimplexNoise(seeded(1));
4220
4733
  var pitchNoise = new SimplexNoise(seeded(2));
4221
4734
  var rollNoise = new SimplexNoise(seeded(3));
@@ -4250,8 +4763,147 @@ var createCameraShaker = () => {
4250
4763
  };
4251
4764
  };
4252
4765
 
4766
+ // src/kernel/trail.ts
4767
+ import {
4768
+ BufferAttribute as BufferAttribute2,
4769
+ BufferGeometry as BufferGeometry3,
4770
+ Color as Color3,
4771
+ DoubleSide as DoubleSide2,
4772
+ Mesh,
4773
+ MeshBasicMaterial as MeshBasicMaterial2,
4774
+ Vector3 as Vector310
4775
+ } from "three";
4776
+ var stepsPerSecond = 60;
4777
+ var widthAt = (trail, along) => {
4778
+ const full = trail.width * 0.1;
4779
+ if (trail.taper === "linear")
4780
+ return full * along;
4781
+ if (trail.taper === "quadratic")
4782
+ return full * along * along;
4783
+ return full;
4784
+ };
4785
+ var createTrailRibbon = (first, start, now) => {
4786
+ let trail = first;
4787
+ let count = 0;
4788
+ let points = new Float32Array(0);
4789
+ const geometry = new BufferGeometry3;
4790
+ const material = new MeshBasicMaterial2({
4791
+ vertexColors: true,
4792
+ transparent: true,
4793
+ side: DoubleSide2,
4794
+ depthWrite: false
4795
+ });
4796
+ const object = new Mesh(geometry, material);
4797
+ object.frustumCulled = false;
4798
+ object.raycast = () => {
4799
+ return;
4800
+ };
4801
+ object.name = "effect-three:trail";
4802
+ const last = new Vector310;
4803
+ const here = new Vector310;
4804
+ const ahead = new Vector310;
4805
+ const behind = new Vector310;
4806
+ const side = new Vector310;
4807
+ const toEye = new Vector310;
4808
+ const tint = new Color3;
4809
+ let clock = now;
4810
+ let phase = 0;
4811
+ const reset = (at, time) => {
4812
+ count = Math.max(2, Math.round(trail.length * 10));
4813
+ points = new Float32Array(count * 3);
4814
+ for (let point = 0;point < count; point++)
4815
+ at.toArray(points, point * 3);
4816
+ const index = [];
4817
+ for (let point = 0;point < count - 1; point++) {
4818
+ const a = point * 2;
4819
+ index.push(a, a + 1, a + 2, a + 1, a + 3, a + 2);
4820
+ }
4821
+ geometry.setIndex(index);
4822
+ geometry.setAttribute("position", new BufferAttribute2(new Float32Array(count * 6), 3));
4823
+ geometry.setAttribute("color", new BufferAttribute2(new Float32Array(count * 8), 4));
4824
+ paint();
4825
+ last.copy(at);
4826
+ clock = time;
4827
+ phase = 0;
4828
+ };
4829
+ const paint = () => {
4830
+ const colors = geometry.getAttribute("color");
4831
+ tint.setHex(trail.color);
4832
+ for (let point = 0;point < count; point++) {
4833
+ const alpha = trail.fade ? point / (count - 1) : 1;
4834
+ colors.setXYZW(point * 2, tint.r, tint.g, tint.b, alpha);
4835
+ colors.setXYZW(point * 2 + 1, tint.r, tint.g, tint.b, alpha);
4836
+ }
4837
+ colors.needsUpdate = true;
4838
+ };
4839
+ const push = (at) => {
4840
+ points.copyWithin(0, 3);
4841
+ at.toArray(points, (count - 1) * 3);
4842
+ };
4843
+ reset(start, now);
4844
+ return {
4845
+ object,
4846
+ record: (position, time) => {
4847
+ const due = Math.min(count, Math.floor((time - clock) * stepsPerSecond));
4848
+ if (due <= 0)
4849
+ return;
4850
+ clock += due / stepsPerSecond;
4851
+ for (let step = 0;step < due; step++) {
4852
+ if (phase === 0) {
4853
+ for (let shift = 0;shift < trail.decay; shift++) {
4854
+ if (position.distanceTo(last) < trail.stride)
4855
+ continue;
4856
+ push(position);
4857
+ }
4858
+ last.copy(position);
4859
+ }
4860
+ phase = (phase + 1) % Math.max(1, Math.round(trail.interval));
4861
+ }
4862
+ },
4863
+ face: (eye) => {
4864
+ const position = geometry.getAttribute("position");
4865
+ for (let point = 0;point < count; point++) {
4866
+ here.fromArray(points, point * 3);
4867
+ ahead.fromArray(points, Math.min(point + 1, count - 1) * 3);
4868
+ behind.fromArray(points, Math.max(point - 1, 0) * 3);
4869
+ side.subVectors(ahead, behind).cross(toEye.subVectors(eye, here));
4870
+ const length = side.length();
4871
+ if (length > 0.000000001)
4872
+ side.multiplyScalar(widthAt(trail, point / (count - 1)) / 2 / length);
4873
+ else
4874
+ side.set(0, 0, 0);
4875
+ position.setXYZ(point * 2, here.x + side.x, here.y + side.y, here.z + side.z);
4876
+ position.setXYZ(point * 2 + 1, here.x - side.x, here.y - side.y, here.z - side.z);
4877
+ }
4878
+ position.needsUpdate = true;
4879
+ },
4880
+ settled: () => {
4881
+ const end = (count - 1) * 3;
4882
+ for (let point = 0;point < end; point += 3) {
4883
+ if (points[point] !== points[end] || points[point + 1] !== points[end + 1])
4884
+ return false;
4885
+ if (points[point + 2] !== points[end + 2])
4886
+ return false;
4887
+ }
4888
+ return true;
4889
+ },
4890
+ restyle: (next, position, time) => {
4891
+ const resized = Math.round(next.length * 10) !== Math.round(trail.length * 10);
4892
+ trail = next;
4893
+ if (resized)
4894
+ reset(position, time);
4895
+ else
4896
+ paint();
4897
+ },
4898
+ dispose: () => {
4899
+ geometry.dispose();
4900
+ material.dispose();
4901
+ }
4902
+ };
4903
+ };
4904
+
4253
4905
  // src/kernel/sceneGraph.ts
4254
- var isPlaced = (entry) => entry._tag === "Group" || entry._tag === "Mesh" || entry._tag === "Instances" || entry._tag === "Borrowed" || entry._tag === "Label";
4906
+ var isPlaced = (entry) => entry._tag === "Group" || entry._tag === "Mesh" || entry._tag === "Instances" || entry._tag === "Borrowed" || entry._tag === "Label" || entry._tag === "Line" || entry._tag === "Points";
4255
4907
  var isSurfaced = (entry) => entry._tag === "Mesh" || entry._tag === "Instances";
4256
4908
  var writeVec3 = (target, v) => {
4257
4909
  target.set(v[0], v[1], v[2]);
@@ -4345,6 +4997,7 @@ var createSceneGraph2 = (options = {}) => {
4345
4997
  let views = [];
4346
4998
  const textureGraphs = new Map;
4347
4999
  let textureList = [];
5000
+ let hudHosted;
4348
5001
  let viewDescriptions = [];
4349
5002
  let gestures = emptyScene.gestures;
4350
5003
  let overlayList = emptyScene.overlays;
@@ -4446,11 +5099,35 @@ var createSceneGraph2 = (options = {}) => {
4446
5099
  const keys = new WeakMap;
4447
5100
  const spinning = new Set;
4448
5101
  const facing = new Set;
5102
+ const facingOrder = [];
4449
5103
  const cameraTurn = new Quaternion6;
4450
5104
  const parentTurn = new Quaternion6;
5105
+ const sparkling = new Set;
5106
+ const trackSparkle = (entry, node) => {
5107
+ if (animatedLook(node.look))
5108
+ sparkling.add(entry);
5109
+ else
5110
+ sparkling.delete(entry);
5111
+ if (entry._tag === "Points")
5112
+ entry.hosted?.tick(animating ? elapsed : 0);
5113
+ };
5114
+ const sparkle = (entry) => {
5115
+ if (entry._tag === "Points")
5116
+ entry.hosted?.tick(elapsed);
5117
+ };
5118
+ const trailing = new Map;
5119
+ const trailPlace = new Vector311;
5120
+ const eye = new Vector311;
5121
+ const trailsMoving = () => {
5122
+ for (const ribbon of trailing.values())
5123
+ if (!ribbon.settled())
5124
+ return true;
5125
+ return false;
5126
+ };
5127
+ const edged = new Map;
4451
5128
  const detailing = new Set;
4452
- const cameraPlace = new Vector39;
4453
- const groupPlace = new Vector39;
5129
+ const cameraPlace = new Vector311;
5130
+ const groupPlace = new Vector311;
4454
5131
  const playing = new Set;
4455
5132
  const registry = {
4456
5133
  ...builtinBehaviours2,
@@ -4460,7 +5137,7 @@ var createSceneGraph2 = (options = {}) => {
4460
5137
  const behaving = new Map;
4461
5138
  const moving = new Map;
4462
5139
  const reparented = new Set;
4463
- const worldPosition = new Vector39;
5140
+ const worldPosition = new Vector311;
4464
5141
  const interactive = new Set;
4465
5142
  let interactiveList;
4466
5143
  let index = emptyIndex2;
@@ -4528,6 +5205,81 @@ var createSceneGraph2 = (options = {}) => {
4528
5205
  if (lockZ)
4529
5206
  object.rotation.z = 0;
4530
5207
  };
5208
+ const depthOf = (object) => {
5209
+ let depth = 0;
5210
+ for (let parent = object.parent;parent !== null; parent = parent.parent)
5211
+ depth += 1;
5212
+ return depth;
5213
+ };
5214
+ const faceCameras = () => {
5215
+ facingOrder.length = 0;
5216
+ facing.forEach((entry) => facingOrder.push(entry));
5217
+ facingOrder.sort((a, b) => depthOf(a.object) - depthOf(b.object));
5218
+ facingOrder.forEach(faceCamera);
5219
+ };
5220
+ const trailTarget = (object, local) => local ? trailPlace.copy(object.position) : object.getWorldPosition(trailPlace);
5221
+ const trackTrail = (entry, node) => {
5222
+ const current = trailing.get(entry);
5223
+ if (node.trail === null) {
5224
+ if (current === undefined)
5225
+ return;
5226
+ current.object.removeFromParent();
5227
+ current.dispose();
5228
+ trailing.delete(entry);
5229
+ return;
5230
+ }
5231
+ const start = trailTarget(entry.object, node.trail.local);
5232
+ if (current !== undefined) {
5233
+ current.restyle(node.trail, start, elapsed);
5234
+ return;
5235
+ }
5236
+ const ribbon = createTrailRibbon(node.trail, start, elapsed);
5237
+ scene.add(ribbon.object);
5238
+ trailing.set(entry, ribbon);
5239
+ };
5240
+ const followTrail = (ribbon, entry) => {
5241
+ if (entry._tag !== "Group" && entry._tag !== "Mesh")
5242
+ return;
5243
+ if (entry.node.trail === null)
5244
+ return;
5245
+ ribbon.record(trailTarget(entry.object, entry.node.trail.local), elapsed);
5246
+ ribbon.face(eye);
5247
+ };
5248
+ const unpickable = (part) => {
5249
+ part.raycast = () => {
5250
+ return;
5251
+ };
5252
+ };
5253
+ const trackEdges = (entry, node) => {
5254
+ const current = edged.get(entry);
5255
+ const draw = options.kit?.line;
5256
+ if (node.edges === null || draw === undefined) {
5257
+ if (current === undefined)
5258
+ return;
5259
+ current.hosted.object.removeFromParent();
5260
+ current.hosted.dispose();
5261
+ edged.delete(entry);
5262
+ return;
5263
+ }
5264
+ if (current !== undefined)
5265
+ return;
5266
+ const hosted = draw(edgeLine(node.key, node.edges, []));
5267
+ entry.object.add(hosted.object);
5268
+ edged.set(entry, { hosted, geometry: undefined, edges: undefined });
5269
+ };
5270
+ const refreshEdges = (lines, entry) => {
5271
+ if (entry._tag !== "Mesh" || entry.node.edges === null)
5272
+ return;
5273
+ const { geometry } = entry.object;
5274
+ const look = entry.node.edges;
5275
+ if (geometry === lines.geometry && lines.edges !== undefined && edgesEquals(look, lines.edges)) {
5276
+ return;
5277
+ }
5278
+ lines.hosted.update(edgeLine(entry.node.key, look, edgePoints(geometry, look.threshold)));
5279
+ lines.hosted.object.traverse(unpickable);
5280
+ lines.geometry = geometry;
5281
+ lines.edges = look;
5282
+ };
4531
5283
  const childObject = (node, index) => {
4532
5284
  const child = node.children[index];
4533
5285
  return child === undefined ? undefined : entries.get(child.key)?.object;
@@ -4592,10 +5344,11 @@ var createSceneGraph2 = (options = {}) => {
4592
5344
  if (node.follow !== null)
4593
5345
  entry.object.visible = false;
4594
5346
  trackDetail(entry, node);
5347
+ trackTrail(entry, node);
4595
5348
  return entry;
4596
5349
  }
4597
5350
  case "Mesh": {
4598
- const object = new Mesh(store.acquireGeometry(node.geometry), store.acquireMaterial(node.material));
5351
+ const object = new Mesh2(store.acquireGeometry(node.geometry), store.acquireMaterial(node.material));
4599
5352
  const entry = { _tag: "Mesh", node, object };
4600
5353
  writeVec3(object.position, node.position);
4601
5354
  writeVec3(object.scale, node.scale);
@@ -4604,6 +5357,8 @@ var createSceneGraph2 = (options = {}) => {
4604
5357
  trackInteractive(object, node.interactive);
4605
5358
  object.castShadow = node.castShadow;
4606
5359
  object.receiveShadow = node.receiveShadow;
5360
+ trackEdges(entry, node);
5361
+ trackTrail(entry, node);
4607
5362
  return entry;
4608
5363
  }
4609
5364
  case "Instances": {
@@ -4677,6 +5432,30 @@ var createSceneGraph2 = (options = {}) => {
4677
5432
  const hosted = options.kit?.shaderGrid(node);
4678
5433
  return { _tag: "ShaderGrid", node, object: hosted?.object ?? new Group, hosted };
4679
5434
  }
5435
+ case "Points": {
5436
+ const hosted = options.kit?.points?.(node);
5437
+ const entry = { _tag: "Points", node, object: new Group, hosted };
5438
+ if (hosted !== undefined)
5439
+ entry.object.add(hosted.object);
5440
+ writeVec3(entry.object.position, node.position);
5441
+ writeVec3(entry.object.scale, node.scale);
5442
+ trackRotation(entry, node);
5443
+ trackBehaviour(entry, node);
5444
+ trackSparkle(entry, node);
5445
+ return entry;
5446
+ }
5447
+ case "Line": {
5448
+ const hosted = options.kit?.line?.(node);
5449
+ const entry = { _tag: "Line", node, object: new Group, hosted };
5450
+ if (hosted !== undefined)
5451
+ entry.object.add(hosted.object);
5452
+ writeVec3(entry.object.position, node.position);
5453
+ writeVec3(entry.object.scale, node.scale);
5454
+ trackRotation(entry, node);
5455
+ trackBehaviour(entry, node);
5456
+ trackInteractive(entry.object, node.interactive);
5457
+ return entry;
5458
+ }
4680
5459
  case "HemisphereLight": {
4681
5460
  const object = new HemisphereLight;
4682
5461
  writeHemisphereLight(object, node);
@@ -4701,9 +5480,9 @@ var createSceneGraph2 = (options = {}) => {
4701
5480
  if (!node.castShadow && !node.receiveShadow)
4702
5481
  return;
4703
5482
  shown.traverse((part) => {
4704
- if (part instanceof Mesh && node.castShadow)
5483
+ if (part instanceof Mesh2 && node.castShadow)
4705
5484
  part.castShadow = true;
4706
- if (part instanceof Mesh && node.receiveShadow)
5485
+ if (part instanceof Mesh2 && node.receiveShadow)
4707
5486
  part.receiveShadow = true;
4708
5487
  });
4709
5488
  };
@@ -4842,6 +5621,17 @@ var createSceneGraph2 = (options = {}) => {
4842
5621
  spinning.delete(entry);
4843
5622
  facing.delete(entry);
4844
5623
  detailing.delete(entry);
5624
+ const lines = edged.get(entry);
5625
+ if (lines !== undefined) {
5626
+ lines.hosted.dispose();
5627
+ edged.delete(entry);
5628
+ }
5629
+ const ribbon = trailing.get(entry);
5630
+ if (ribbon !== undefined) {
5631
+ ribbon.object.removeFromParent();
5632
+ ribbon.dispose();
5633
+ trailing.delete(entry);
5634
+ }
4845
5635
  moving.delete(entry);
4846
5636
  reparented.delete(entry);
4847
5637
  trackInteractive(entry.object, false);
@@ -4853,8 +5643,10 @@ var createSceneGraph2 = (options = {}) => {
4853
5643
  entry.object.dispose();
4854
5644
  if (entry._tag === "Grid")
4855
5645
  disposeGridLines(entry.helper);
4856
- if (entry._tag === "ShaderGrid")
5646
+ if (entry._tag === "ShaderGrid" || entry._tag === "Line" || entry._tag === "Points") {
4857
5647
  entry.hosted?.dispose();
5648
+ }
5649
+ sparkling.delete(entry);
4858
5650
  if (entry._tag === "Label") {
4859
5651
  entry.hosted?.dispose();
4860
5652
  if (entry.node.material !== null && entry.material !== undefined) {
@@ -4949,6 +5741,12 @@ var createSceneGraph2 = (options = {}) => {
4949
5741
  retarget(entry, next);
4950
5742
  if (fields.includes("rotation"))
4951
5743
  trackRotation(entry, next);
5744
+ if (entry._tag === "Mesh" && next._tag === "Mesh" && fields.includes("edges")) {
5745
+ trackEdges(entry, next);
5746
+ }
5747
+ if (entry._tag === "Mesh" && next._tag === "Mesh" && fields.includes("trail")) {
5748
+ trackTrail(entry, next);
5749
+ }
4952
5750
  if (fields.includes("behaviour"))
4953
5751
  trackBehaviour(entry, next);
4954
5752
  if (fields.includes("interactive"))
@@ -5005,6 +5803,8 @@ var createSceneGraph2 = (options = {}) => {
5005
5803
  trackInteractive(entry.object, next.interactive);
5006
5804
  if (fields.includes("detail"))
5007
5805
  trackDetail(entry, next);
5806
+ if (fields.includes("trail"))
5807
+ trackTrail(entry, next);
5008
5808
  if (fields.includes("follow")) {
5009
5809
  entry.object.visible = next.follow === null;
5010
5810
  if (next.follow === null) {
@@ -5040,6 +5840,34 @@ var createSceneGraph2 = (options = {}) => {
5040
5840
  entry.node = next;
5041
5841
  return;
5042
5842
  }
5843
+ if (entry._tag === "Points" && next._tag === "Points") {
5844
+ entry.node = next;
5845
+ if (movesTransform(entry, fields))
5846
+ retarget(entry, next);
5847
+ if (fields.includes("rotation"))
5848
+ trackRotation(entry, next);
5849
+ if (fields.includes("behaviour"))
5850
+ trackBehaviour(entry, next);
5851
+ if (fields.includes("look")) {
5852
+ entry.hosted?.update(next);
5853
+ trackSparkle(entry, next);
5854
+ }
5855
+ return;
5856
+ }
5857
+ if (entry._tag === "Line" && next._tag === "Line") {
5858
+ entry.node = next;
5859
+ if (movesTransform(entry, fields))
5860
+ retarget(entry, next);
5861
+ if (fields.includes("rotation"))
5862
+ trackRotation(entry, next);
5863
+ if (fields.includes("behaviour"))
5864
+ trackBehaviour(entry, next);
5865
+ if (fields.includes("interactive"))
5866
+ trackInteractive(entry.object, next.interactive);
5867
+ if (fields.includes("line"))
5868
+ entry.hosted?.update(next);
5869
+ return;
5870
+ }
5043
5871
  if (entry._tag === "ShaderGrid" && next._tag === "ShaderGrid") {
5044
5872
  entry.hosted?.update(entry.node, next);
5045
5873
  entry.node = next;
@@ -5169,6 +5997,7 @@ var createSceneGraph2 = (options = {}) => {
5169
5997
  gestures: "page",
5170
5998
  views: [],
5171
5999
  renderTextures: [],
6000
+ hud: null,
5172
6001
  overlays: [],
5173
6002
  environment: null,
5174
6003
  sky: null,
@@ -5176,10 +6005,67 @@ var createSceneGraph2 = (options = {}) => {
5176
6005
  physics: null,
5177
6006
  xr: null,
5178
6007
  measure: [],
6008
+ assets: [],
5179
6009
  render: parent.render,
5180
6010
  resources: parent.resources,
5181
6011
  children: texture.children
5182
6012
  });
6013
+ const hudCamera = (hud) => {
6014
+ const [marginX, marginY] = hud.margin;
6015
+ const [vertical, horizontal] = hud.alignment.split("-");
6016
+ const across = { left: -viewport.width / 2 + marginX, right: viewport.width / 2 - marginX };
6017
+ const up = { top: viewport.height / 2 - marginY, bottom: -viewport.height / 2 + marginY };
6018
+ const x = across[horizontal] ?? 0;
6019
+ const y = up[vertical] ?? 0;
6020
+ return orthographicCamera2({
6021
+ zoom: 1,
6022
+ near: 0.1,
6023
+ far: 2000,
6024
+ position: [-x, -y, 200],
6025
+ lookAt: [-x, -y, 0]
6026
+ });
6027
+ };
6028
+ const hudScene = (hud, parent) => textureScene({
6029
+ id: "hud",
6030
+ width: viewport.width,
6031
+ height: viewport.height,
6032
+ camera: hudCamera(hud),
6033
+ background: null,
6034
+ children: hud.children
6035
+ }, parent);
6036
+ const syncHud = (next) => {
6037
+ if (next.hud === null) {
6038
+ if (hudHosted === undefined)
6039
+ return [];
6040
+ hudHosted.graph.dispose();
6041
+ hudHosted = undefined;
6042
+ return [{ _tag: "RemoveHud" }];
6043
+ }
6044
+ const created = hudHosted === undefined;
6045
+ const hosted = hudHosted ?? {
6046
+ graph: createSceneGraph2({
6047
+ store,
6048
+ ...options.behaviours === undefined ? {} : { behaviours: options.behaviours },
6049
+ ...options.kit === undefined ? {} : { kit: options.kit }
6050
+ }),
6051
+ description: next.hud,
6052
+ parent: next
6053
+ };
6054
+ if (created)
6055
+ hosted.graph.resize(viewport.width, viewport.height);
6056
+ const result = hosted.graph.reconcile(hudScene(next.hud, next));
6057
+ hosted.description = next.hud;
6058
+ hosted.parent = next;
6059
+ hudHosted = hosted;
6060
+ const ops = result._tag === "Applied" ? result.ops : [];
6061
+ return created || ops.length > 0 ? [{ _tag: "UpdateHud", ops }] : [];
6062
+ };
6063
+ const refitHud = () => {
6064
+ if (hudHosted === undefined)
6065
+ return;
6066
+ hudHosted.graph.resize(viewport.width, viewport.height);
6067
+ hudHosted.graph.reconcile(hudScene(hudHosted.description, hudHosted.parent));
6068
+ };
5183
6069
  function hostedTexture(texture) {
5184
6070
  const hosted = textureGraphs.get(texture.id);
5185
6071
  if (hosted === undefined)
@@ -5203,7 +6089,7 @@ var createSceneGraph2 = (options = {}) => {
5203
6089
  const reframed = existing === undefined || existing.description.width !== texture.width || existing.description.height !== texture.height || existing.description.background !== texture.background;
5204
6090
  if (reframed) {
5205
6091
  hosted.graph.resize(texture.width, texture.height);
5206
- hosted.graph.scene.background = texture.background === null ? null : new Color2(texture.background);
6092
+ hosted.graph.scene.background = texture.background === null ? null : new Color4(texture.background);
5207
6093
  }
5208
6094
  hosted.description = texture;
5209
6095
  textureGraphs.set(texture.id, hosted);
@@ -5228,6 +6114,11 @@ var createSceneGraph2 = (options = {}) => {
5228
6114
  if (checked._tag === "Invalid")
5229
6115
  return checked;
5230
6116
  }
6117
+ if (next.hud !== null) {
6118
+ const checked = indexScene2(hudScene(next.hud, next), knownBehaviours);
6119
+ if (checked._tag === "Invalid")
6120
+ return checked;
6121
+ }
5231
6122
  const result = diff2(index, next, knownBehaviours);
5232
6123
  if (result._tag === "Invalid")
5233
6124
  return result;
@@ -5258,11 +6149,13 @@ var createSceneGraph2 = (options = {}) => {
5258
6149
  invalidate();
5259
6150
  physicsDescription = next.physics;
5260
6151
  bodyHost.sync(next.physics, bodySources());
6152
+ if (bodyHost.owesSteps())
6153
+ invalidate();
5261
6154
  xrDescription = next.xr;
5262
6155
  if (next.measure.join("\x00") !== measured.join("\x00"))
5263
6156
  invalidate();
5264
6157
  measured = next.measure;
5265
- const ops = [...result.ops, ...syncTextures(next)];
6158
+ const ops = [...result.ops, ...syncTextures(next), ...syncHud(next)];
5266
6159
  if (ops.length > 0)
5267
6160
  invalidate();
5268
6161
  return { _tag: "Applied", ops };
@@ -5275,6 +6168,7 @@ var createSceneGraph2 = (options = {}) => {
5275
6168
  elapsed = elapsedSeconds;
5276
6169
  for (const texture of textureList)
5277
6170
  texture.graph.tick(elapsedSeconds);
6171
+ hudHosted?.graph.tick(elapsedSeconds);
5278
6172
  for (const entry of spinning) {
5279
6173
  if (isPlaced(entry)) {
5280
6174
  writeRotation2(entry.object.rotation, entry.node.rotation, elapsed);
@@ -5292,7 +6186,14 @@ var createSceneGraph2 = (options = {}) => {
5292
6186
  bodyHost.tick(elapsed, animating);
5293
6187
  if (facing.size > 0) {
5294
6188
  camera.getWorldQuaternion(cameraTurn);
5295
- facing.forEach(faceCamera);
6189
+ faceCameras();
6190
+ }
6191
+ edged.forEach(refreshEdges);
6192
+ if (animating)
6193
+ sparkling.forEach(sparkle);
6194
+ if (trailing.size > 0) {
6195
+ camera.getWorldPosition(eye);
6196
+ trailing.forEach(followTrail);
5296
6197
  }
5297
6198
  if (detailing.size > 0) {
5298
6199
  camera.getWorldPosition(cameraPlace);
@@ -5307,6 +6208,7 @@ var createSceneGraph2 = (options = {}) => {
5307
6208
  invalidate();
5308
6209
  for (const texture of textureList)
5309
6210
  texture.graph.setAnimating(next);
6211
+ hudHosted?.graph.setAnimating(next);
5310
6212
  animating = next;
5311
6213
  if (next)
5312
6214
  return;
@@ -5323,6 +6225,8 @@ var createSceneGraph2 = (options = {}) => {
5323
6225
  texture.graph.dispose();
5324
6226
  textureGraphs.clear();
5325
6227
  textureList = [];
6228
+ hudHosted?.graph.dispose();
6229
+ hudHosted = undefined;
5326
6230
  for (const entry of entries.values()) {
5327
6231
  entry.object.removeFromParent();
5328
6232
  release(entry);
@@ -5355,12 +6259,14 @@ var createSceneGraph2 = (options = {}) => {
5355
6259
  return viewMotions.get(view)?.turned();
5356
6260
  },
5357
6261
  renderTextures: () => textureList,
6262
+ hud: () => hudHosted,
5358
6263
  gestures: () => gestures,
5359
6264
  overlays: () => overlayList,
5360
6265
  environment: () => environmentDescription,
5361
6266
  contactShadows: () => contactDescription,
5362
6267
  collisions: bodyHost.drainCollisions,
5363
6268
  contactForces: bodyHost.drainContactForces,
6269
+ physicsReached: bodyHost.drainReached,
5364
6270
  xr: () => xrDescription,
5365
6271
  measured: () => measured,
5366
6272
  labelLayout: (key) => {
@@ -5383,7 +6289,7 @@ var createSceneGraph2 = (options = {}) => {
5383
6289
  tick,
5384
6290
  elapsedSeconds: () => elapsed,
5385
6291
  renderSettings: () => settings,
5386
- needsFrame: () => dirty || textureList.some((texture) => texture.graph.needsFrame()) || animating && (spinning.size > 0 || moving.size > 0 || behaving.size > 0 || clipsRunning() || cameraMotion.moving() || viewsMoving() || shaking() || bodyHost.needsFrame()),
6292
+ needsFrame: () => dirty || trailsMoving() || textureList.some((texture) => texture.graph.needsFrame()) || (hudHosted?.graph.needsFrame() ?? false) || animating && (spinning.size > 0 || moving.size > 0 || behaving.size > 0 || clipsRunning() || cameraMotion.moving() || viewsMoving() || shaking() || sparkling.size > 0 || bodyHost.needsFrame()) || bodyHost.owesSteps(),
5387
6293
  drawn: () => {
5388
6294
  dirty = false;
5389
6295
  },
@@ -5410,12 +6316,1937 @@ var createSceneGraph2 = (options = {}) => {
5410
6316
  viewport = { width, height };
5411
6317
  fitCamera(camera, viewport);
5412
6318
  hostViews(viewDescriptions);
6319
+ refitHud();
5413
6320
  invalidate();
5414
6321
  },
5415
6322
  dispose
5416
6323
  };
5417
6324
  };
5418
- export { handJoints2, clipTime2, clipRunning2, createPose2, resetPose2, float2, builtinBehaviours2, spinAngle2, writeRotation2, buildMaterial2, updateMaterial2, vec3Equals2, rotationEquals2, shapeEquals2, geometryRefEquals2, instanceEquals2, instancesEqual2, behaviourEquals2, animationEquals2, transitionEquals2, materialRefEquals2, cameraEquals2, renderSettingsEquals2, effectsEqual2, viewsEqual2, resourcesEqual2, overlaysEqual2, environmentEquals2, skyEquals2, contactShadowsEquals2, bodyEquals2, physicsEquals2, xrEquals2, capacityFor2, createPerformanceMonitor2, easeProgress2, startTween2, applyTween2, fixed2, spin2, billboard2, box2, sphere2, plane2, cylinder2, torus2, cone2, capsule2, roundedBox2, backdrop2, ring2, circle2, text2, instant2, ease2, basic2, lambert2, phong2, standard2, physical2, normalMaterial2, shadowMaterial2, grid2, shaderGrid2, sharedGeometry2, lentGeometry2, sharedMaterial2, group2, detailed2, headSpace2, jointSpace2, controllerSpace2, anchorSpace2, planeSpace2, meshSpace2, xrMeshGeometryId2, xrMesh2, xrAnchor2, mesh2, body2, interactionGroups2, cuboidCollider2, ballCollider2, capsuleCollider2, cylinderCollider2, coneCollider2, velocityMotor2, positionMotor2, physics2, bodyRef2, joint2, fixedJoint2, sphericalJoint2, revoluteJoint2, prismaticJoint2, ropeJoint2, springJoint2, glide2, xr2, instance2, instances2, behave2, play2, borrowed2, label2, colorRange2, ambientLight2, directionalLight2, pointLight2, spotLight2, hemisphereLight2, perspectiveCamera2, orthographicCamera2, cameraShake2, bloom2, vignette2, film2, renderSettings2, view2, renderTexture2, environment2, sunPositionFromAngles2, sky2, contactShadows2, overlay2, scene2, emptyIndex2, indexScene2, changedFields2, diff2, createResourceStore2, createSceneClock2, createManualFrames2, createGatedFrames2, createFramePacer2, defaultOrbit2, orbitOptions2, rotateCamera2, dollyCamera2, panCamera2, dollyCameraToward2, orbit2, orbitKey2, autoRotated2, fitCamera2, clipCamera2, placeOverlays2, interactionBits2, colliderFor2, describedColliders2, collidersFor2, colliderSignature2, locomote2, teleport2, glided2, createXrTracker2, environmentPresets2, environmentPresetUrl2, stagePresets2, stage2, centerOffset2, stageBounds2, noModifiers2, pointOnPlane2, createPicker2, createPointerTracker2, createSceneLoop2, recordingRendererFactory2, createSceneGraph2 };
6325
+ // src/kernel/preview.ts
6326
+ import {
6327
+ Box2,
6328
+ Color as Color5,
6329
+ DirectionalLight as DirectionalLight2,
6330
+ HemisphereLight as HemisphereLight2,
6331
+ InstancedMesh as InstancedMesh2,
6332
+ LinearSRGBColorSpace,
6333
+ LineBasicMaterial as LineBasicMaterial2,
6334
+ LineDashedMaterial,
6335
+ Matrix3,
6336
+ Matrix4 as Matrix45,
6337
+ Mesh as Mesh3,
6338
+ MeshBasicMaterial as MeshBasicMaterial3,
6339
+ MeshLambertMaterial as MeshLambertMaterial2,
6340
+ MeshNormalMaterial as MeshNormalMaterial2,
6341
+ MeshPhongMaterial as MeshPhongMaterial2,
6342
+ MeshStandardMaterial as MeshStandardMaterial2,
6343
+ MeshToonMaterial,
6344
+ Light,
6345
+ Line,
6346
+ PointLight as PointLight2,
6347
+ Points,
6348
+ Scene as Scene2,
6349
+ Sprite,
6350
+ PointsMaterial,
6351
+ SpotLight as SpotLight2,
6352
+ SpriteMaterial,
6353
+ SRGBColorSpace,
6354
+ Vector2 as Vector24,
6355
+ Vector3 as Vector313,
6356
+ AmbientLight as AmbientLight2
6357
+ } from "three";
6358
+ import {
6359
+ Projector,
6360
+ RenderableFace,
6361
+ RenderableLine,
6362
+ RenderableSprite
6363
+ } from "three/addons/renderers/Projector.js";
6364
+
6365
+ // src/kernel/paintOrder.ts
6366
+ import { Sphere, Vector3 as Vector312 } from "three";
6367
+ var onPlane = 0.000001;
6368
+ var candidates = 8;
6369
+ var splitCost = 8;
6370
+ var planeOf = (points) => {
6371
+ const [a, b, c] = points;
6372
+ if (a === undefined || b === undefined || c === undefined)
6373
+ return;
6374
+ const normal = new Vector312().subVectors(b, a).cross(new Vector312().subVectors(c, a));
6375
+ if (normal.lengthSq() === 0)
6376
+ return;
6377
+ normal.normalize();
6378
+ return { normal, constant: normal.dot(a) };
6379
+ };
6380
+ var side = (plane, point) => plane.normal.dot(point) - plane.constant;
6381
+ var toleranceAt = (points) => onPlane * Math.max(1, ...points.map((point) => point.length()));
6382
+ var split = (plane, facet) => {
6383
+ const sides = facet.points.map((point) => side(plane, point));
6384
+ const tolerance = toleranceAt(facet.points);
6385
+ if (sides.every((s) => Math.abs(s) <= tolerance))
6386
+ return { on: facet };
6387
+ if (sides.every((s) => s >= -tolerance))
6388
+ return { front: facet };
6389
+ if (sides.every((s) => s <= tolerance))
6390
+ return { back: facet };
6391
+ const front = [];
6392
+ const back = [];
6393
+ facet.points.forEach((point, i) => {
6394
+ const next = facet.points[(i + 1) % facet.points.length] ?? point;
6395
+ const here = sides[i] ?? 0;
6396
+ const there = sides[(i + 1) % sides.length] ?? 0;
6397
+ if (here >= -tolerance)
6398
+ front.push(point);
6399
+ if (here <= tolerance)
6400
+ back.push(point);
6401
+ if (here > tolerance && there < -tolerance || here < -tolerance && there > tolerance) {
6402
+ const crossing = point.clone().lerp(next, here / (here - there));
6403
+ front.push(crossing);
6404
+ back.push(crossing.clone());
6405
+ }
6406
+ });
6407
+ return {
6408
+ ...front.length >= 3 ? { front: { ...facet, points: front } } : {},
6409
+ ...back.length >= 3 ? { back: { ...facet, points: back } } : {}
6410
+ };
6411
+ };
6412
+ function hidesNone(facet, facets, eye) {
6413
+ const plane = planeOf(facet.points);
6414
+ if (plane === undefined)
6415
+ return true;
6416
+ const tolerance = toleranceAt(facet.points);
6417
+ return side(plane, eye) > tolerance && facets.every((other) => other === facet || other.points.every((point) => side(plane, point) <= tolerance));
6418
+ }
6419
+ var unitOf = (facets, marks, eye) => {
6420
+ const points = [...facets.flatMap((facet) => facet.points), ...marks.map((mark) => mark.at)];
6421
+ const bounds = new Sphere().setFromPoints(points);
6422
+ const selfOrdered = facets.every((facet) => hidesNone(facet, facets, eye));
6423
+ return { facets, marks, bounds, selfOrdered };
6424
+ };
6425
+ var sizeOf = (unit) => unit.facets.length + unit.marks.length;
6426
+ var sideOf = (plane, unit) => {
6427
+ const distance = side(plane, unit.bounds.center);
6428
+ const reach = unit.bounds.radius * (1 + onPlane) + onPlane;
6429
+ return distance > reach ? 1 : distance < -reach ? -1 : 0;
6430
+ };
6431
+ var weigh = (plane, units) => {
6432
+ let cut = 0;
6433
+ let front = 0;
6434
+ let back = 0;
6435
+ for (const unit of units) {
6436
+ const whole = sideOf(plane, unit);
6437
+ if (whole > 0)
6438
+ front += sizeOf(unit);
6439
+ else if (whole < 0)
6440
+ back += sizeOf(unit);
6441
+ else {
6442
+ const parts = unit.facets.map((facet) => split(plane, facet));
6443
+ const ahead = parts.filter((part) => part.front !== undefined).length;
6444
+ const behind = parts.filter((part) => part.back !== undefined).length;
6445
+ if (ahead > 0 && behind > 0)
6446
+ cut++;
6447
+ front += ahead;
6448
+ back += behind;
6449
+ }
6450
+ }
6451
+ return splitCost * cut + Math.abs(front - back);
6452
+ };
6453
+ var partition = (plane, units) => {
6454
+ const front = [];
6455
+ const back = [];
6456
+ const on = [];
6457
+ for (const unit of units) {
6458
+ const whole = sideOf(plane, unit);
6459
+ if (whole > 0)
6460
+ front.push(unit);
6461
+ else if (whole < 0)
6462
+ back.push(unit);
6463
+ else {
6464
+ const ahead = [];
6465
+ const behind = [];
6466
+ for (const facet of unit.facets) {
6467
+ const parts = split(plane, facet);
6468
+ if (parts.front !== undefined)
6469
+ ahead.push(parts.front);
6470
+ if (parts.back !== undefined)
6471
+ behind.push(parts.back);
6472
+ if (parts.on !== undefined)
6473
+ on.push(parts.on);
6474
+ }
6475
+ const marksAhead = unit.marks.filter((mark) => side(plane, mark.at) >= 0);
6476
+ const marksBehind = unit.marks.filter((mark) => side(plane, mark.at) < 0);
6477
+ if (ahead.length + marksAhead.length > 0)
6478
+ front.push({ ...unit, facets: ahead, marks: marksAhead });
6479
+ if (behind.length + marksBehind.length > 0)
6480
+ back.push({ ...unit, facets: behind, marks: marksBehind });
6481
+ }
6482
+ }
6483
+ return { front, back, on };
6484
+ };
6485
+ var planeFor = (units) => {
6486
+ const withFacets = units.filter((unit) => unit.facets.length > 0);
6487
+ const count = Math.min(candidates, withFacets.length);
6488
+ let best;
6489
+ let lightest = Number.POSITIVE_INFINITY;
6490
+ for (let i = 0;i < count; i++) {
6491
+ const unit = withFacets[Math.floor(i * withFacets.length / count)];
6492
+ const facet = unit?.facets[Math.floor(i * (unit?.facets.length ?? 0) / count)];
6493
+ const plane = facet === undefined ? undefined : planeOf(facet.points);
6494
+ if (plane === undefined)
6495
+ continue;
6496
+ const weight = weigh(plane, units);
6497
+ if (weight < lightest) {
6498
+ lightest = weight;
6499
+ best = plane;
6500
+ }
6501
+ }
6502
+ if (best !== undefined)
6503
+ return best;
6504
+ for (const unit of withFacets) {
6505
+ for (const facet of unit.facets) {
6506
+ const plane = planeOf(facet.points);
6507
+ if (plane !== undefined)
6508
+ return plane;
6509
+ }
6510
+ }
6511
+ return;
6512
+ };
6513
+ var distanceFrom = (eye) => (mark) => mark.at.distanceToSquared(eye);
6514
+ var apart = (unit) => [
6515
+ ...unit.facets.map((facet) => ({
6516
+ facets: [facet],
6517
+ marks: [],
6518
+ bounds: new Sphere().setFromPoints([...facet.points]),
6519
+ selfOrdered: true
6520
+ })),
6521
+ ...unit.marks.map((mark) => ({
6522
+ facets: [],
6523
+ marks: [mark],
6524
+ bounds: new Sphere(mark.at.clone(), 0),
6525
+ selfOrdered: true
6526
+ }))
6527
+ ];
6528
+ var paint = (units, eye, into) => {
6529
+ const [only] = units;
6530
+ if (only !== undefined && units.length === 1) {
6531
+ if (only.selfOrdered && only.marks.length === 0) {
6532
+ into.push(...only.facets);
6533
+ return;
6534
+ }
6535
+ if (only.facets.length === 0) {
6536
+ into.push(...only.marks.toSorted((a, b) => distanceFrom(eye)(b) - distanceFrom(eye)(a)));
6537
+ return;
6538
+ }
6539
+ paint(apart(only), eye, into);
6540
+ return;
6541
+ }
6542
+ const plane = planeFor(units);
6543
+ if (plane === undefined) {
6544
+ const marks = units.flatMap((unit) => unit.marks);
6545
+ into.push(...marks.toSorted((a, b) => distanceFrom(eye)(b) - distanceFrom(eye)(a)));
6546
+ return;
6547
+ }
6548
+ const { front, back, on } = partition(plane, units);
6549
+ const facing = side(plane, eye) >= 0;
6550
+ paint(facing ? back : front, eye, into);
6551
+ into.push(...on);
6552
+ paint(facing ? front : back, eye, into);
6553
+ };
6554
+ var paintOrder = (units, eye) => {
6555
+ const into = [];
6556
+ paint(units, eye, into);
6557
+ return into;
6558
+ };
6559
+
6560
+ // src/kernel/preview.ts
6561
+ var overdraw = 0.5;
6562
+ var black = new Color5(0, 0, 0);
6563
+ var irradianceAt = (lights, position, normal, into) => {
6564
+ into.copy(black);
6565
+ const lightPosition = new Vector313;
6566
+ const toLight = new Vector313;
6567
+ const direction = new Vector313;
6568
+ for (const light of lights) {
6569
+ const color = light.color.clone().multiplyScalar(light.intensity);
6570
+ if (light instanceof AmbientLight2) {
6571
+ into.add(color);
6572
+ } else if (light instanceof HemisphereLight2) {
6573
+ lightPosition.setFromMatrixPosition(light.matrixWorld).normalize();
6574
+ const weight = 0.5 * normal.dot(lightPosition) + 0.5;
6575
+ into.add(light.groundColor.clone().lerp(light.color, weight).multiplyScalar(light.intensity));
6576
+ } else if (light instanceof DirectionalLight2) {
6577
+ lightPosition.setFromMatrixPosition(light.matrixWorld);
6578
+ direction.setFromMatrixPosition(light.target.matrixWorld);
6579
+ toLight.subVectors(lightPosition, direction).normalize();
6580
+ into.add(color.multiplyScalar(Math.max(normal.dot(toLight), 0)));
6581
+ } else if (light instanceof PointLight2 || light instanceof SpotLight2) {
6582
+ lightPosition.setFromMatrixPosition(light.matrixWorld);
6583
+ toLight.subVectors(lightPosition, position);
6584
+ const distance = toLight.length();
6585
+ toLight.normalize();
6586
+ const cone = light instanceof SpotLight2 ? spotAttenuation(light, toLight) : 1;
6587
+ const falloff = distanceAttenuation(distance, light.distance, light.decay);
6588
+ into.add(color.multiplyScalar(Math.max(normal.dot(toLight), 0) * cone * falloff));
6589
+ }
6590
+ }
6591
+ return into;
6592
+ };
6593
+ var distanceAttenuation = (distance, cutoff, decay) => {
6594
+ const falloff = 1 / Math.max(distance ** decay, 0.01);
6595
+ return cutoff > 0 ? falloff * Math.min(Math.max(1 - (distance / cutoff) ** 4, 0), 1) ** 2 : falloff;
6596
+ };
6597
+ var smoothstep = (low, high, x) => {
6598
+ const t = Math.min(Math.max((x - low) / (high - low), 0), 1);
6599
+ return t * t * (3 - 2 * t);
6600
+ };
6601
+ var spotAttenuation = (light, toLight) => {
6602
+ const direction = new Vector313().setFromMatrixPosition(light.matrixWorld).sub(new Vector313().setFromMatrixPosition(light.target.matrixWorld)).normalize();
6603
+ return smoothstep(Math.cos(light.angle), Math.cos(light.angle * (1 - light.penumbra)), direction.dot(toLight));
6604
+ };
6605
+ var faceColor = (painter, face, material, centre) => {
6606
+ if (material instanceof MeshBasicMaterial3) {
6607
+ const color = material.color.clone();
6608
+ return material.vertexColors ? color.multiply(face.color) : color;
6609
+ }
6610
+ if (material instanceof MeshNormalMaterial2) {
6611
+ const normal = face.normalModel.clone().applyMatrix3(painter.normalView).normalize();
6612
+ return new Color5(normal.x, normal.y, normal.z).multiplyScalar(0.5).addScalar(0.5);
6613
+ }
6614
+ if (material instanceof MeshLambertMaterial2 || material instanceof MeshPhongMaterial2 || material instanceof MeshStandardMaterial2 || material instanceof MeshToonMaterial) {
6615
+ const diffuse = material.color.clone();
6616
+ if (material.vertexColors)
6617
+ diffuse.multiply(face.color);
6618
+ return irradianceAt(painter.lights, centre, face.normalModel, new Color5).multiply(diffuse).multiplyScalar(1 / Math.PI).add(material.emissive.clone().multiplyScalar(material.emissiveIntensity));
6619
+ }
6620
+ return;
6621
+ };
6622
+ var styleOf = (color, colorSpace) => {
6623
+ const shown = new Color5(Math.min(Math.max(color.r, 0), 1), Math.min(Math.max(color.g, 0), 1), Math.min(Math.max(color.b, 0), 1));
6624
+ return `#${shown.getHexString(colorSpace)}`;
6625
+ };
6626
+ var opacityOf = (material) => material.transparent ? material.opacity : 1;
6627
+ var standInsFor = (object) => {
6628
+ if (!(object instanceof InstancedMesh2)) {
6629
+ const standIn = object.clone(false);
6630
+ standIn.matrix.copy(object.matrixWorld);
6631
+ return [{ standIn }];
6632
+ }
6633
+ const material = Array.isArray(object.material) ? object.material[0] : object.material;
6634
+ if (material === undefined)
6635
+ return [];
6636
+ const matrix = new Matrix45;
6637
+ const standIns = [];
6638
+ for (let i = 0;i < object.count; i++) {
6639
+ object.getMatrixAt(i, matrix);
6640
+ const tinted = object.instanceColor === null ? undefined : material.clone();
6641
+ if (tinted !== undefined && "color" in tinted && tinted.color instanceof Color5) {
6642
+ tinted.color.multiply(object.getColorAt(i, new Color5));
6643
+ }
6644
+ const standIn = new Mesh3(object.geometry, tinted ?? material);
6645
+ standIn.matrix.multiplyMatrices(object.matrixWorld, matrix);
6646
+ standIn.renderOrder = object.renderOrder;
6647
+ standIns.push(tinted === undefined ? { standIn } : { standIn, tinted });
6648
+ }
6649
+ return standIns;
6650
+ };
6651
+ var number = (precision) => (value) => String(Number(value.toFixed(precision)));
6652
+ var widened = (points) => {
6653
+ const out = points.map((point) => point.clone());
6654
+ for (const [i, from] of out.entries()) {
6655
+ const to = out[(i + 1) % out.length];
6656
+ const length = to === undefined ? 0 : from.distanceTo(to);
6657
+ if (to === undefined || length === 0)
6658
+ continue;
6659
+ const step = new Vector24().subVectors(to, from).multiplyScalar(overdraw / length);
6660
+ to.add(step);
6661
+ from.sub(step);
6662
+ }
6663
+ return out;
6664
+ };
6665
+ var facetPaint = (fill, opacity) => opacity < 1 ? `fill="${fill}" fill-opacity="${String(Number(opacity.toFixed(2)))}"` : `fill="${fill}" stroke="${fill}"`;
6666
+ var centreOf2 = (object) => {
6667
+ if (!(object instanceof Mesh3))
6668
+ return new Vector313().setFromMatrixPosition(object.matrixWorld);
6669
+ const geometry = object.geometry;
6670
+ if (geometry.boundingSphere === null)
6671
+ geometry.computeBoundingSphere();
6672
+ return (geometry.boundingSphere?.center.clone() ?? new Vector313).applyMatrix4(object.matrixWorld);
6673
+ };
6674
+ var isDrawn = (object) => object instanceof Mesh3 || object instanceof Line || object instanceof Points || object instanceof Sprite;
6675
+ var previewSvg2 = (description, options) => {
6676
+ const { width, height } = options;
6677
+ const graph = createSceneGraph2({
6678
+ ...options.store === undefined ? {} : { store: options.store },
6679
+ ...options.behaviours === undefined ? {} : { behaviours: options.behaviours }
6680
+ });
6681
+ const applied = graph.reconcile(description);
6682
+ if (applied._tag === "Invalid") {
6683
+ graph.dispose();
6684
+ return applied;
6685
+ }
6686
+ graph.resize(width, height);
6687
+ graph.tick(options.at ?? 0);
6688
+ const { scene, camera } = graph;
6689
+ scene.updateMatrixWorld();
6690
+ camera.updateMatrixWorld();
6691
+ const lights = [];
6692
+ const drawn = [];
6693
+ scene.traverseVisible((object) => {
6694
+ if (object instanceof Light)
6695
+ lights.push(object);
6696
+ else if (isDrawn(object))
6697
+ drawn.push(object);
6698
+ });
6699
+ const colorSpace = graph.renderSettings().colorSpace === "linear" ? LinearSRGBColorSpace : SRGBColorSpace;
6700
+ const painter = {
6701
+ lights,
6702
+ normalView: new Matrix3().getNormalMatrix(camera.matrixWorldInverse),
6703
+ colorSpace
6704
+ };
6705
+ const halfWidth = width / 2;
6706
+ const halfHeight = height / 2;
6707
+ const at = number(options.precision ?? 1);
6708
+ const eye = new Vector313().setFromMatrixPosition(camera.matrixWorld);
6709
+ const ahead = camera.getWorldDirection(new Vector313);
6710
+ const near = { normal: ahead, constant: ahead.dot(eye) + camera.near };
6711
+ const xy = (x, y) => `${at(x * halfWidth)},${at(-y * halfHeight)}`;
6712
+ function facetOf(face, material, centre, opacity) {
6713
+ const color = faceColor(painter, face, material, centre);
6714
+ if (color === undefined)
6715
+ return;
6716
+ const points = [face.v1, face.v2, face.v3].map((vertex) => vertex.positionWorld.clone());
6717
+ const paint = facetPaint(styleOf(color, colorSpace), opacity);
6718
+ return split(near, { _tag: "Facet", points, item: { paint, d: "" } }).front;
6719
+ }
6720
+ const projector = new Projector;
6721
+ const holder = new Scene2;
6722
+ const units = [];
6723
+ const over = [];
6724
+ const tinted = [];
6725
+ for (const object of drawn) {
6726
+ for (const { standIn, tinted: copy } of standInsFor(object)) {
6727
+ if (copy !== undefined)
6728
+ tinted.push(copy);
6729
+ standIn.matrixAutoUpdate = false;
6730
+ holder.add(standIn);
6731
+ const projected = projector.projectScene(holder, camera, false, true);
6732
+ holder.remove(standIn);
6733
+ const centre = centreOf2(standIn);
6734
+ const facets = [];
6735
+ const marks = [];
6736
+ const keep = (material, item) => {
6737
+ if (!material.depthTest)
6738
+ over.push({ renderOrder: standIn.renderOrder, item });
6739
+ else if (item._tag === "Facet")
6740
+ facets.push(item);
6741
+ else
6742
+ marks.push(item);
6743
+ };
6744
+ for (const element of projected.elements) {
6745
+ const material = element.material;
6746
+ if (material === null || material.opacity === 0)
6747
+ continue;
6748
+ const opacity = opacityOf(material);
6749
+ const faded = opacity < 1 ? ` fill-opacity="${at(opacity)}"` : "";
6750
+ if (element instanceof RenderableFace) {
6751
+ const seen = facetOf(element, material, centre, opacity);
6752
+ if (seen === undefined)
6753
+ continue;
6754
+ keep(material, seen);
6755
+ } else if (element instanceof RenderableLine && material instanceof LineBasicMaterial2) {
6756
+ const a = element.v1.positionScreen;
6757
+ const b = element.v2.positionScreen;
6758
+ const dashes = material instanceof LineDashedMaterial ? ` stroke-dasharray="${at(material.dashSize)},${at(material.gapSize)}"` : "";
6759
+ const stroked = opacity < 1 ? ` stroke-opacity="${at(opacity)}"` : "";
6760
+ keep(material, {
6761
+ _tag: "Mark",
6762
+ at: element.v1.positionWorld.clone().lerp(element.v2.positionWorld, 0.5),
6763
+ item: {
6764
+ paint: `fill="none" stroke="${styleOf(material.color, colorSpace)}"${stroked} stroke-width="${at(material.linewidth)}" stroke-linecap="${material.linecap}"${dashes}`,
6765
+ d: `M${xy(a.x, a.y)}L${xy(b.x, b.y)}`
6766
+ }
6767
+ });
6768
+ } else if (element instanceof RenderableSprite && (material instanceof SpriteMaterial || material instanceof PointsMaterial)) {
6769
+ const size = material instanceof PointsMaterial ? material.size : 1;
6770
+ const w = element.scale.x * halfWidth * size;
6771
+ const h = element.scale.y * halfHeight * size;
6772
+ const x = element.x * halfWidth;
6773
+ const y = -element.y * halfHeight;
6774
+ keep(material, {
6775
+ _tag: "Mark",
6776
+ at: new Vector313(element.x, element.y, element.z).unproject(camera),
6777
+ item: {
6778
+ paint: `fill="${styleOf(material.color, colorSpace)}"${faded}`,
6779
+ d: `M${at(x - w / 2)},${at(y - h / 2)}h${at(w)}v${at(h)}h${at(-w)}z`
6780
+ }
6781
+ });
6782
+ }
6783
+ }
6784
+ if (facets.length + marks.length > 0)
6785
+ units.push(unitOf(facets, marks, eye));
6786
+ }
6787
+ }
6788
+ for (const material of tinted)
6789
+ material.dispose();
6790
+ graph.dispose();
6791
+ const ordered = [
6792
+ ...paintOrder(units, eye),
6793
+ ...over.toSorted((a, b) => a.renderOrder - b.renderOrder).map(({ item }) => item)
6794
+ ];
6795
+ const clip = new Box2(new Vector24(-halfWidth, -halfHeight), new Vector24(halfWidth, halfHeight));
6796
+ const toPicture = (point) => {
6797
+ const projectedPoint = point.clone().project(camera);
6798
+ return new Vector24(projectedPoint.x * halfWidth, -projectedPoint.y * halfHeight);
6799
+ };
6800
+ function outlineOf(shape) {
6801
+ if (shape._tag === "Mark")
6802
+ return shape.item.d;
6803
+ const corners = widened(shape.points.map(toPicture));
6804
+ return new Box2().setFromPoints([...corners]).intersectsBox(clip) ? `M${corners.map((corner) => `${at(corner.x)},${at(corner.y)}`).join("L")}z` : undefined;
6805
+ }
6806
+ const groups = [];
6807
+ for (const shape of ordered) {
6808
+ const outline = outlineOf(shape);
6809
+ const last = groups.at(-1);
6810
+ if (outline === undefined)
6811
+ continue;
6812
+ if (last?.paint === shape.item.paint)
6813
+ last.outlines.push(outline);
6814
+ else
6815
+ groups.push({ paint: shape.item.paint, outlines: [outline] });
6816
+ }
6817
+ const paths = groups.map(({ paint, outlines }) => `<path ${paint} d="${outlines.join("")}"/>`);
6818
+ const background = options.background === undefined || options.background === null ? "" : `<rect x="${at(-halfWidth)}" y="${at(-halfHeight)}" width="${at(width)}" height="${at(height)}" fill="${styleOf(new Color5(options.background), colorSpace)}"/>`;
6819
+ return {
6820
+ _tag: "Preview",
6821
+ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="${at(width)}" height="${at(height)}" viewBox="${at(-halfWidth)} ${at(-halfHeight)} ${at(width)} ${at(height)}">${background}<g stroke-width="0.6" stroke-linejoin="round">${paths.join("")}</g></svg>`
6822
+ };
6823
+ };
6824
+ // src/kernel/gizmo.ts
6825
+ import { Euler as Euler6, Quaternion as Quaternion7, Vector3 as Vector314 } from "three";
6826
+ var gizmo2 = (options = {}) => ({
6827
+ mode: options.mode ?? "translate",
6828
+ space: options.space ?? "world",
6829
+ size: options.size ?? 1,
6830
+ translationSnap: options.translationSnap ?? null,
6831
+ rotationSnap: options.rotationSnap ?? null,
6832
+ scaleSnap: options.scaleSnap ?? null,
6833
+ showX: options.showX ?? true,
6834
+ showY: options.showY ?? true,
6835
+ showZ: options.showZ ?? true,
6836
+ hovered: null,
6837
+ grab: null
6838
+ });
6839
+ var axes = {
6840
+ x: new Vector314(1, 0, 0),
6841
+ y: new Vector314(0, 1, 0),
6842
+ z: new Vector314(0, 0, 1)
6843
+ };
6844
+ var turnOf = (rotation) => new Quaternion7().setFromEuler(new Euler6(rotation[0], rotation[1], rotation[2]));
6845
+ var spaceOf = (state, handle) => {
6846
+ if (state.mode === "scale")
6847
+ return "local";
6848
+ if (handle === "xyz")
6849
+ return "world";
6850
+ return state.space;
6851
+ };
6852
+ var axisIn = (name, space, rotation) => space === "local" ? axes[name].clone().applyQuaternion(turnOf(rotation)) : axes[name].clone();
6853
+ var toVec34 = (vector) => [vector.x, vector.y, vector.z];
6854
+ var dragPlane = (state, handle, start, eye, facingCamera) => {
6855
+ const space = spaceOf(state, handle);
6856
+ const along = (name) => axisIn(name, space, start.rotation);
6857
+ const facing = (axis) => axis.clone().cross(eye.clone().cross(axis));
6858
+ const normals = {
6859
+ x: () => facing(along("x")),
6860
+ y: () => facing(along("y")),
6861
+ z: () => facing(along("z")),
6862
+ xy: () => along("z"),
6863
+ yz: () => along("x"),
6864
+ xz: () => along("y"),
6865
+ xyz: () => facingCamera.clone()
6866
+ };
6867
+ const normal = state.mode === "rotate" ? facingCamera.clone() : normals[handle]();
6868
+ if (normal.lengthSq() < 0.000000000001)
6869
+ normal.copy(eye);
6870
+ normal.normalize();
6871
+ const center = new Vector314(...start.position);
6872
+ return { normal: toVec34(normal), constant: -normal.dot(center) };
6873
+ };
6874
+ var onPlane2 = (grab, ray) => {
6875
+ const point = pointOnPlane2(ray, grab.plane);
6876
+ if (point === undefined)
6877
+ return;
6878
+ return new Vector314(...point).sub(new Vector314(...grab.start.position));
6879
+ };
6880
+ var backwards = (camera) => {
6881
+ const back = new Vector314(...camera.position).sub(new Vector314(...camera.lookAt));
6882
+ return back.lengthSq() > 0.000000000001 ? back.normalize() : new Vector314(0, 0, 1);
6883
+ };
6884
+ var pressGizmo2 = (state, placement, handle, ray, camera) => {
6885
+ const center = new Vector314(...placement.position);
6886
+ const facingCamera = backwards(camera);
6887
+ const towardsEye = new Vector314(...camera.position).sub(center);
6888
+ const distance = towardsEye.length();
6889
+ const eye = camera._tag === "Orthographic" || distance < 0.000000001 ? facingCamera.clone() : towardsEye.normalize();
6890
+ const plane = dragPlane(state, handle, placement, eye, facingCamera);
6891
+ const point = pointOnPlane2(ray, plane);
6892
+ if (point === undefined)
6893
+ return state;
6894
+ const from = toVec34(new Vector314(...point).sub(center));
6895
+ return { ...state, grab: { handle, start: placement, from, plane, eye: toVec34(eye), distance } };
6896
+ };
6897
+ var releaseGizmo2 = (state) => ({ ...state, grab: null });
6898
+ var hoverGizmo2 = (state, handle) => state.hovered === handle ? state : { ...state, hovered: handle };
6899
+ var snapped = (value, step) => step === null ? value : Math.round(value / step) * step;
6900
+ var includes = (handle, axis) => handle.includes(axis);
6901
+ var translated = (state, grab, end) => {
6902
+ const space = spaceOf(state, grab.handle);
6903
+ const offset = end.clone().sub(new Vector314(...grab.from));
6904
+ const turn = turnOf(grab.start.rotation);
6905
+ if (space === "local")
6906
+ offset.applyQuaternion(turn.clone().invert());
6907
+ if (!includes(grab.handle, "x"))
6908
+ offset.x = 0;
6909
+ if (!includes(grab.handle, "y"))
6910
+ offset.y = 0;
6911
+ if (!includes(grab.handle, "z"))
6912
+ offset.z = 0;
6913
+ if (space === "local") {
6914
+ const local = offset.add(new Vector314(...grab.start.position).applyQuaternion(turn.clone().invert()));
6915
+ if (includes(grab.handle, "x"))
6916
+ local.x = snapped(local.x, state.translationSnap);
6917
+ if (includes(grab.handle, "y"))
6918
+ local.y = snapped(local.y, state.translationSnap);
6919
+ if (includes(grab.handle, "z"))
6920
+ local.z = snapped(local.z, state.translationSnap);
6921
+ return { ...grab.start, position: toVec34(local.applyQuaternion(turn)) };
6922
+ }
6923
+ const position = offset.add(new Vector314(...grab.start.position));
6924
+ if (includes(grab.handle, "x"))
6925
+ position.x = snapped(position.x, state.translationSnap);
6926
+ if (includes(grab.handle, "y"))
6927
+ position.y = snapped(position.y, state.translationSnap);
6928
+ if (includes(grab.handle, "z"))
6929
+ position.z = snapped(position.z, state.translationSnap);
6930
+ return { ...grab.start, position: toVec34(position) };
6931
+ };
6932
+ var scaled2 = (state, grab, end) => {
6933
+ const from = new Vector314(...grab.from);
6934
+ const factor = new Vector314(1, 1, 1);
6935
+ if (grab.handle === "xyz") {
6936
+ const ratio = end.length() / Math.max(from.length(), 0.000000001) * (end.dot(from) < 0 ? -1 : 1);
6937
+ factor.setScalar(ratio);
6938
+ } else {
6939
+ const inverse = turnOf(grab.start.rotation).invert();
6940
+ const a = from.clone().applyQuaternion(inverse);
6941
+ const b = end.clone().applyQuaternion(inverse);
6942
+ factor.set(includes(grab.handle, "x") ? b.x / a.x : 1, includes(grab.handle, "y") ? b.y / a.y : 1, includes(grab.handle, "z") ? b.z / a.z : 1);
6943
+ }
6944
+ const step = state.scaleSnap;
6945
+ const each = (index, axis) => {
6946
+ const value = grab.start.scale[index] * factor.getComponent(index);
6947
+ if (step === null || !includes(grab.handle, axis))
6948
+ return value;
6949
+ return snapped(value, step) || step;
6950
+ };
6951
+ return { ...grab.start, scale: [each(0, "x"), each(1, "y"), each(2, "z")] };
6952
+ };
6953
+ var rotated = (state, grab, end) => {
6954
+ if (grab.handle !== "x" && grab.handle !== "y" && grab.handle !== "z")
6955
+ return grab.start;
6956
+ const space = spaceOf(state, grab.handle);
6957
+ const offset = end.clone().sub(new Vector314(...grab.from));
6958
+ const speed = 20 / Math.max(grab.distance, 0.000000001);
6959
+ const axis = axisIn(grab.handle, space, grab.start.rotation);
6960
+ const across = axis.clone().cross(new Vector314(...grab.eye));
6961
+ const angle = snapped(across.lengthSq() < 0.000000000001 ? 0 : offset.dot(across.normalize()) * speed, state.rotationSnap);
6962
+ const start = turnOf(grab.start.rotation);
6963
+ const turn = space === "local" ? start.clone().multiply(new Quaternion7().setFromAxisAngle(axes[grab.handle], angle)) : new Quaternion7().setFromAxisAngle(axes[grab.handle], angle).multiply(start);
6964
+ const euler = new Euler6().setFromQuaternion(turn.normalize());
6965
+ return { ...grab.start, rotation: [euler.x, euler.y, euler.z] };
6966
+ };
6967
+ var dragGizmo2 = (state, ray) => {
6968
+ const grab = state.grab;
6969
+ if (grab === null)
6970
+ return;
6971
+ const end = onPlane2(grab, ray);
6972
+ if (end === undefined)
6973
+ return grab.start;
6974
+ if (state.mode === "translate")
6975
+ return translated(state, grab, end);
6976
+ if (state.mode === "scale")
6977
+ return scaled2(state, grab, end);
6978
+ return rotated(state, grab, end);
6979
+ };
6980
+ var movedAlike2 = (from, to, other) => {
6981
+ const turn = turnOf(to.rotation).multiply(turnOf(from.rotation).invert());
6982
+ const turned = new Euler6().setFromQuaternion(turn.multiply(turnOf(other.rotation)).normalize());
6983
+ const ratio = (axis) => from.scale[axis] === 0 ? 1 : to.scale[axis] / from.scale[axis];
6984
+ const shifted = (axis) => other.position[axis] + to.position[axis] - from.position[axis];
6985
+ return {
6986
+ position: [shifted(0), shifted(1), shifted(2)],
6987
+ rotation: [turned.x, turned.y, turned.z],
6988
+ scale: [other.scale[0] * ratio(0), other.scale[1] * ratio(1), other.scale[2] * ratio(2)]
6989
+ };
6990
+ };
6991
+ var colours = {
6992
+ x: 16725587,
6993
+ y: 9100032,
6994
+ z: 2920447,
6995
+ xy: 2920447,
6996
+ yz: 16725587,
6997
+ xz: 9100032
6998
+ };
6999
+ var highlight = 16776960;
7000
+ var hideThreshold = 0.99;
7001
+ var screenScale = (state, at, camera) => {
7002
+ if (camera._tag === "Orthographic")
7003
+ return 480 / camera.zoom * state.size / 4;
7004
+ const distance = new Vector314(...camera.position).distanceTo(new Vector314(...at));
7005
+ const spread = Math.min(1.9 * Math.tan(Math.PI * camera.fov / 360), 7);
7006
+ return distance * spread * state.size / 4;
7007
+ };
7008
+ var gizmoHandleOf2 = (key, hit) => {
7009
+ const prefix = `${key}:`;
7010
+ if (!hit.startsWith(prefix))
7011
+ return;
7012
+ const name = hit.slice(prefix.length).split(":")[0];
7013
+ const handles = ["x", "y", "z", "xy", "yz", "xz", "xyz"];
7014
+ return handles.find((handle) => handle === name);
7015
+ };
7016
+ var look = (state, handle, colour) => basic2({
7017
+ color: state.grab?.handle === handle || state.hovered === handle ? highlight : colour,
7018
+ transparent: true,
7019
+ opacity: state.grab !== null && state.grab.handle !== handle ? 0.25 : 0.9,
7020
+ depthTest: false
7021
+ });
7022
+ var pointAlong = { x: [0, 0, -Math.PI / 2], y: [0, 0, 0], z: [Math.PI / 2, 0, 0] };
7023
+ var tips = { x: [0.5, 0, 0], y: [0, 0.5, 0], z: [0, 0, 0.5] };
7024
+ var ringRound = { x: [0, Math.PI / 2, 0], y: [Math.PI / 2, 0, 0], z: [0, 0, 0] };
7025
+ var ring = (key, state, axis) => [
7026
+ mesh2({
7027
+ key: `${key}:${axis}`,
7028
+ rotation: fixed2([...ringRound[axis]]),
7029
+ geometry: torus2({ radius: 0.5, tube: 0.02, radialSegments: 6, tubularSegments: 48 }),
7030
+ material: look(state, axis, colours[axis]),
7031
+ interactive: true
7032
+ })
7033
+ ];
7034
+ var arrow = (key, state, axis) => {
7035
+ const along = pointAlong[axis];
7036
+ const tipAt = tips[axis];
7037
+ const material = look(state, axis, colours[axis]);
7038
+ return [
7039
+ mesh2({
7040
+ key: `${key}:${axis}:stem`,
7041
+ position: [tipAt[0] / 2, tipAt[1] / 2, tipAt[2] / 2],
7042
+ rotation: fixed2([...along]),
7043
+ geometry: cylinder2({ radiusTop: 0.012, radiusBottom: 0.012, height: 0.5 }),
7044
+ material,
7045
+ interactive: true
7046
+ }),
7047
+ mesh2({
7048
+ key: `${key}:${axis}:tip`,
7049
+ position: tipAt,
7050
+ rotation: fixed2([...along]),
7051
+ geometry: state.mode === "scale" ? box2({ width: 0.08, height: 0.08, depth: 0.08 }) : cone2({ radius: 0.04, height: 0.12, radialSegments: 12 }),
7052
+ material,
7053
+ interactive: true
7054
+ })
7055
+ ];
7056
+ };
7057
+ var axisHandles = (key, state, visible) => ["x", "y", "z"].filter(visible).flatMap((axis) => state.mode === "rotate" ? ring(key, state, axis) : arrow(key, state, axis));
7058
+ var planeHandles = (key, state) => {
7059
+ if (state.mode === "rotate")
7060
+ return [];
7061
+ const squares = [
7062
+ {
7063
+ handle: "xy",
7064
+ at: [0.15, 0.15, 0],
7065
+ turn: [0, 0, 0],
7066
+ shown: state.showX && state.showY
7067
+ },
7068
+ {
7069
+ handle: "yz",
7070
+ at: [0, 0.15, 0.15],
7071
+ turn: [0, Math.PI / 2, 0],
7072
+ shown: state.showY && state.showZ
7073
+ },
7074
+ {
7075
+ handle: "xz",
7076
+ at: [0.15, 0, 0.15],
7077
+ turn: [-Math.PI / 2, 0, 0],
7078
+ shown: state.showX && state.showZ
7079
+ }
7080
+ ];
7081
+ return squares.filter((square) => square.shown).map((square) => mesh2({
7082
+ key: `${key}:${square.handle}`,
7083
+ position: square.at,
7084
+ rotation: fixed2([...square.turn]),
7085
+ geometry: plane2({ width: 0.12, height: 0.12 }),
7086
+ material: { ...look(state, square.handle, colours[square.handle]), side: "double" },
7087
+ interactive: true
7088
+ }));
7089
+ };
7090
+ var gizmoNodes2 = (key, state, placement, camera) => {
7091
+ const space = spaceOf(state, state.grab?.handle ?? null);
7092
+ const scale = screenScale(state, placement.position, camera);
7093
+ const eye = new Vector314(...camera.position).sub(new Vector314(...placement.position)).normalize();
7094
+ const shown = { x: state.showX, y: state.showY, z: state.showZ };
7095
+ const visible = (axis) => shown[axis] && (state.mode === "rotate" || state.grab?.handle === axis || Math.abs(axisIn(axis, space, placement.rotation).dot(eye)) <= hideThreshold);
7096
+ return group2({
7097
+ key,
7098
+ position: placement.position,
7099
+ rotation: fixed2(space === "local" ? placement.rotation : [0, 0, 0]),
7100
+ scale: [scale, scale, scale],
7101
+ children: [
7102
+ ...axisHandles(key, state, visible),
7103
+ ...planeHandles(key, state),
7104
+ ...state.mode === "rotate" ? [] : [
7105
+ mesh2({
7106
+ key: `${key}:xyz`,
7107
+ geometry: state.mode === "scale" ? box2({ width: 0.1, height: 0.1, depth: 0.1 }) : sphere2({ radius: 0.06, widthSegments: 12, heightSegments: 8 }),
7108
+ material: look(state, "xyz", 16777215),
7109
+ interactive: true
7110
+ })
7111
+ ]
7112
+ ]
7113
+ });
7114
+ };
7115
+ // src/kernel/boxSelect.ts
7116
+ import { OrthographicCamera as OrthographicCamera5, PerspectiveCamera as PerspectiveCamera4, Vector3 as Vector315 } from "three";
7117
+ var hostCamera2 = (camera, size) => {
7118
+ const host = camera._tag === "Perspective" ? new PerspectiveCamera4(camera.fov, size.width / size.height, camera.near, camera.far) : new OrthographicCamera5(size.width / -2, size.width / 2, size.height / 2, size.height / -2, camera.near, camera.far);
7119
+ if (host instanceof OrthographicCamera5 && camera._tag === "Orthographic") {
7120
+ host.zoom = camera.zoom;
7121
+ }
7122
+ host.position.set(...camera.position);
7123
+ host.lookAt(...camera.lookAt);
7124
+ host.updateMatrixWorld();
7125
+ host.updateProjectionMatrix();
7126
+ return host;
7127
+ };
7128
+ var between2 = (value, a, b) => value >= Math.min(a, b) && value <= Math.max(a, b);
7129
+ var boxSelect2 = (camera, size, from, to, candidates) => {
7130
+ const host = hostCamera2(camera, size);
7131
+ const inside = (candidate) => {
7132
+ const seen = new Vector315(...candidate.at).project(host);
7133
+ return between2(seen.x, from.x, to.x) && between2(seen.y, from.y, to.y) && between2(seen.z, -1, 1);
7134
+ };
7135
+ return candidates.filter(inside).map((candidate) => candidate.key);
7136
+ };
7137
+ // src/kernel/history.ts
7138
+ var make = (value, label = "Start") => ({
7139
+ entries: [{ id: 0, parent: null, label, value, next: null }],
7140
+ current: 0
7141
+ });
7142
+ var entryOf = (state, id) => {
7143
+ const entry = state.entries[id];
7144
+ if (entry === undefined)
7145
+ throw new RangeError(`No history entry ${id}`);
7146
+ return entry;
7147
+ };
7148
+ var presentEntry = (state) => entryOf(state, state.current);
7149
+ var present = (state) => presentEntry(state).value;
7150
+ var withNext = (state, id, next) => ({
7151
+ ...state,
7152
+ entries: state.entries.map((entry) => entry.id === id ? { ...entry, next } : entry)
7153
+ });
7154
+ var record = (state, value, label) => {
7155
+ const id = state.entries.length;
7156
+ const grown = {
7157
+ entries: [...state.entries, { id, parent: state.current, label, value, next: null }],
7158
+ current: id
7159
+ };
7160
+ return withNext(grown, state.current, id);
7161
+ };
7162
+ var canUndo = (state) => presentEntry(state).parent !== null;
7163
+ var canRedo = (state) => presentEntry(state).next !== null;
7164
+ var undo = (state) => {
7165
+ const parent = presentEntry(state).parent;
7166
+ return parent === null ? state : { ...state, current: parent };
7167
+ };
7168
+ var redo = (state) => {
7169
+ const next = presentEntry(state).next;
7170
+ return next === null ? state : { ...state, current: next };
7171
+ };
7172
+ var branches = (state) => state.entries.filter((entry) => entry.parent === state.current);
7173
+ var chooseBranch = (state, id) => branches(state).some((entry) => entry.id === id) ? withNext(state, state.current, id) : state;
7174
+ var pathTo = (state, id) => {
7175
+ const path = [];
7176
+ for (let at = id;at !== null; at = entryOf(state, at).parent) {
7177
+ path.unshift(entryOf(state, at));
7178
+ }
7179
+ return path;
7180
+ };
7181
+ var jumpTo = (state, id) => {
7182
+ const path = pathTo(state, id);
7183
+ const chosen = path.slice(1).reduce((at, entry) => withNext(at, entry.parent ?? 0, entry.id), state);
7184
+ return { ...chosen, current: id };
7185
+ };
7186
+ var depth = (state) => pathTo(state, state.current).length - 1;
7187
+ // src/kernel/lockstep.ts
7188
+ var sortedPeers = (members) => members.toSorted((a, b) => a.peer < b.peer ? -1 : a.peer > b.peer ? 1 : 0);
7189
+ var memberAt = (member, turn) => member.from <= turn && (member.until === null || turn < member.until);
7190
+ var membersAt = (state, turn) => sortedPeers(state.members.filter((member) => memberAt(member, turn))).map((member) => member.peer);
7191
+ var leaderAt = (state, turn) => membersAt(state, turn)[0];
7192
+ var make2 = (self, lead = 2) => ({
7193
+ self,
7194
+ members: [{ peer: self, from: 0, until: null }],
7195
+ turn: 0,
7196
+ sent: 0,
7197
+ lead,
7198
+ pending: [],
7199
+ received: []
7200
+ });
7201
+ var join = (self, welcome, heard = []) => ({
7202
+ self,
7203
+ members: welcome.members,
7204
+ turn: welcome.turn,
7205
+ sent: welcome.turn,
7206
+ lead: welcome.lead,
7207
+ pending: [],
7208
+ received: heard.filter((packet) => packet.turn >= welcome.turn)
7209
+ });
7210
+ var submit = (state, input) => ({
7211
+ ...state,
7212
+ pending: [...state.pending, { _tag: "Input", input }]
7213
+ });
7214
+ var known = (state, peer) => state.members.some((member) => member.peer === peer && member.until === null) || state.pending.some((entry) => entry._tag === "Admit" && entry.peer === peer);
7215
+ var admit = (state, peer) => leaderAt(state, state.sent) !== state.self || known(state, peer) ? state : { ...state, pending: [...state.pending, { _tag: "Admit", peer }] };
7216
+ var hasPacket = (state, peer, turn) => state.received.some((packet) => packet.peer === peer && packet.turn === turn);
7217
+ var receive = (state, packet) => {
7218
+ if (packet.turn < state.turn || hasPacket(state, packet.peer, packet.turn))
7219
+ return state;
7220
+ const member = state.members.find((each) => each.peer === packet.peer);
7221
+ if (member !== undefined && !memberAt(member, packet.turn))
7222
+ return state;
7223
+ return { ...state, received: [...state.received, packet] };
7224
+ };
7225
+ var send = (state) => {
7226
+ const member = state.members.find((each) => each.peer === state.self);
7227
+ const ahead = state.sent >= state.turn + state.lead;
7228
+ if (member === undefined || !memberAt(member, state.sent) || ahead) {
7229
+ return { state, packet: undefined };
7230
+ }
7231
+ const packet = { peer: state.self, turn: state.sent, entries: state.pending };
7232
+ return {
7233
+ state: { ...state, sent: state.sent + 1, pending: [], received: [...state.received, packet] },
7234
+ packet
7235
+ };
7236
+ };
7237
+ var waitingOn = (state) => membersAt(state, state.turn).filter((peer) => !hasPacket(state, peer, state.turn));
7238
+ var packetsOf = (state, peer, turn) => state.received.filter((packet) => packet.peer === peer && packet.turn === turn);
7239
+ var applyTurn = (state) => {
7240
+ const turn = state.turn;
7241
+ const packets = membersAt(state, turn).flatMap((peer) => packetsOf(state, peer, turn));
7242
+ const entries = packets.flatMap((packet) => packet.entries.map((entry) => ({ peer: packet.peer, entry })));
7243
+ const admitted = entries.flatMap(({ entry }) => entry._tag === "Admit" && !state.members.some((member) => member.peer === entry.peer) ? [{ peer: entry.peer, from: turn + state.lead + 1, until: null }] : []);
7244
+ const applied = entries.flatMap(({ peer, entry }) => entry._tag === "Input" ? [{ turn, peer, input: entry.input }] : []);
7245
+ return {
7246
+ state: {
7247
+ ...state,
7248
+ turn: turn + 1,
7249
+ members: [...state.members, ...admitted],
7250
+ received: state.received.filter((packet) => packet.turn > turn)
7251
+ },
7252
+ applied
7253
+ };
7254
+ };
7255
+ var advance = (state) => {
7256
+ let current = state;
7257
+ const applied = [];
7258
+ while (membersAt(current, current.turn).length > 0 && waitingOn(current).length === 0) {
7259
+ const next = applyTurn(current);
7260
+ current = next.state;
7261
+ applied.push(...next.applied);
7262
+ }
7263
+ return { state: current, applied };
7264
+ };
7265
+ var welcomeDue = (before, state) => state.members.filter((member) => member.from > before && member.from <= state.turn).filter((member) => leaderAt(state, member.from - 1) === state.self).map((member) => member.peer);
7266
+ var leave = (state) => state.sent - 1;
7267
+ var remove = (state, peer, until) => ({
7268
+ ...state,
7269
+ members: state.members.map((member) => member.peer === peer && (member.until === null || until < member.until) ? { ...member, until } : member),
7270
+ received: state.received.filter((packet) => packet.peer !== peer || packet.turn < until)
7271
+ });
7272
+ // src/kernel/timeline.ts
7273
+ var make3 = (frontier = 0) => ({
7274
+ inputs: [],
7275
+ applied: 0,
7276
+ reached: null,
7277
+ frontier
7278
+ });
7279
+ var schedule = (timeline, step, input) => {
7280
+ const at = timeline.inputs.findIndex((scheduled) => scheduled.step > step);
7281
+ const index = at < 0 ? timeline.inputs.length : at;
7282
+ return {
7283
+ ...timeline,
7284
+ inputs: [...timeline.inputs.slice(0, index), { step, input }, ...timeline.inputs.slice(index)]
7285
+ };
7286
+ };
7287
+ var extend = (timeline, frontier) => frontier <= timeline.frontier ? timeline : { ...timeline, frontier };
7288
+ var reach = (timeline, step) => timeline.reached === step ? timeline : { ...timeline, reached: step };
7289
+ var due = (timeline) => {
7290
+ const { reached } = timeline;
7291
+ if (reached === null)
7292
+ return { timeline, inputs: [] };
7293
+ let applied = timeline.applied;
7294
+ while ((timeline.inputs[applied]?.step ?? Infinity) <= reached)
7295
+ applied += 1;
7296
+ if (applied === timeline.applied)
7297
+ return { timeline, inputs: [] };
7298
+ return {
7299
+ timeline: { ...timeline, applied },
7300
+ inputs: timeline.inputs.slice(timeline.applied, applied).map((scheduled) => scheduled.input)
7301
+ };
7302
+ };
7303
+ var until = (timeline) => Math.min(timeline.inputs[timeline.applied]?.step ?? Infinity, timeline.frontier);
7304
+ var restart = (timeline) => ({
7305
+ ...timeline,
7306
+ applied: 0,
7307
+ reached: null
7308
+ });
7309
+ // src/kernel/describeScene.ts
7310
+ var extentOf = (geometry) => {
7311
+ switch (geometry._tag) {
7312
+ case "Box":
7313
+ case "RoundedBox":
7314
+ return [geometry.width, geometry.height, geometry.depth];
7315
+ case "Sphere":
7316
+ return [geometry.radius * 2, geometry.radius * 2, geometry.radius * 2];
7317
+ case "Cone":
7318
+ return [geometry.radius * 2, geometry.height, geometry.radius * 2];
7319
+ case "Cylinder": {
7320
+ const across = Math.max(geometry.radiusTop, geometry.radiusBottom) * 2;
7321
+ return [across, geometry.height, across];
7322
+ }
7323
+ case "Capsule":
7324
+ return [geometry.radius * 2, geometry.height + geometry.radius * 2, geometry.radius * 2];
7325
+ case "Torus": {
7326
+ const across = (geometry.radius + geometry.tube) * 2;
7327
+ return [across, across, geometry.tube * 2];
7328
+ }
7329
+ case "Plane":
7330
+ return [geometry.width, geometry.height, 0];
7331
+ case "Circle":
7332
+ return [geometry.radius * 2, geometry.radius * 2, 0];
7333
+ case "Ring":
7334
+ return [geometry.outerRadius * 2, geometry.outerRadius * 2, 0];
7335
+ default:
7336
+ return;
7337
+ }
7338
+ };
7339
+ var scaled3 = (extent, scale) => [
7340
+ extent[0] * Math.abs(scale[0]),
7341
+ extent[1] * Math.abs(scale[1]),
7342
+ extent[2] * Math.abs(scale[2])
7343
+ ];
7344
+ var shapeOf = (geometry, description) => {
7345
+ if (geometry._tag === "LentGeometry")
7346
+ return;
7347
+ if (geometry._tag !== "SharedGeometry")
7348
+ return geometry;
7349
+ return description.resources.geometries.find((entry) => entry.id === geometry.id)?.geometry;
7350
+ };
7351
+ var sizeOf2 = (node, description) => {
7352
+ if (node._tag !== "Mesh")
7353
+ return null;
7354
+ const shape = shapeOf(node.geometry, description);
7355
+ const extent = shape === undefined ? undefined : extentOf(shape);
7356
+ return extent === undefined ? null : scaled3(extent, node.scale);
7357
+ };
7358
+ var colourOf = (node) => node._tag === "Mesh" && ("color" in node.material) ? node.material.color : null;
7359
+ var kindOf = (node) => node._tag === "Mesh" ? `Mesh (${node.geometry._tag})` : node._tag;
7360
+ var isLight = (node) => node._tag.endsWith("Light");
7361
+ var lightOf = (node) => {
7362
+ if (!isLight(node) || !("color" in node) || !("intensity" in node))
7363
+ return [];
7364
+ return [
7365
+ {
7366
+ key: node.key,
7367
+ kind: node._tag,
7368
+ colour: node.color,
7369
+ intensity: node.intensity,
7370
+ position: "position" in node ? node.position : null
7371
+ }
7372
+ ];
7373
+ };
7374
+ var thingsIn = (nodes, parent, description) => nodes.flatMap((node) => [
7375
+ ...isLight(node) ? [] : [
7376
+ {
7377
+ key: node.key,
7378
+ kind: kindOf(node),
7379
+ position: "position" in node ? node.position : [0, 0, 0],
7380
+ size: sizeOf2(node, description),
7381
+ colour: colourOf(node),
7382
+ parent
7383
+ }
7384
+ ],
7385
+ ...node._tag === "Group" ? thingsIn(node.children, node.key, description) : []
7386
+ ]);
7387
+ var lightsIn = (nodes) => nodes.flatMap((node) => [
7388
+ ...lightOf(node),
7389
+ ...node._tag === "Group" ? lightsIn(node.children) : []
7390
+ ]);
7391
+ var thingsBounds2 = (things) => {
7392
+ const sized = things.filter((thing) => thing.parent === null && thing.size !== null);
7393
+ if (sized.length === 0)
7394
+ return null;
7395
+ const edge = (axis, sign) => sized.map((thing) => thing.position[axis] + sign * ((thing.size?.[axis] ?? 0) / 2));
7396
+ const low = (axis) => Math.min(...edge(axis, -1));
7397
+ const high = (axis) => Math.max(...edge(axis, 1));
7398
+ return { min: [low(0), low(1), low(2)], max: [high(0), high(1), high(2)] };
7399
+ };
7400
+ var stagingOf = (description) => [
7401
+ ...description.environment === null ? [] : ["environment lighting"],
7402
+ ...description.sky === null ? [] : ["sky"],
7403
+ ...description.render.shadows === "off" ? [] : [`${description.render.shadows} shadows`],
7404
+ ...description.contactShadows === null ? [] : ["contact shadows"],
7405
+ ...description.render.effects.map((effect) => effect._tag.toLowerCase()),
7406
+ ...description.physics === null ? [] : ["physics"]
7407
+ ];
7408
+ var describeScene2 = (description) => {
7409
+ const things = thingsIn(description.children, null, description);
7410
+ return {
7411
+ camera: {
7412
+ kind: description.camera._tag,
7413
+ position: description.camera.position,
7414
+ lookAt: description.camera.lookAt
7415
+ },
7416
+ things,
7417
+ lights: lightsIn(description.children),
7418
+ bounds: thingsBounds2(things),
7419
+ staging: stagingOf(description)
7420
+ };
7421
+ };
7422
+ var number2 = (value) => String(Math.round(value * 100) / 100);
7423
+ var point2 = (at) => `(${at.map(number2).join(", ")})`;
7424
+ var hex = (colour) => `#${colour.toString(16).padStart(6, "0")}`;
7425
+ var thingLine = (thing) => [
7426
+ `- ${thing.key}: ${thing.kind}`,
7427
+ thing.size === null ? "" : `, ${thing.size.map(number2).join(" × ")}`,
7428
+ thing.colour === null ? "" : `, ${hex(thing.colour)}`,
7429
+ `, at ${point2(thing.position)}`,
7430
+ thing.parent === null ? "" : ` in ${thing.parent}`
7431
+ ].join("");
7432
+ var lightLine = (light) => `- ${light.key}: ${light.kind}, ${hex(light.colour)} × ${number2(light.intensity)}${light.position === null ? "" : ` at ${point2(light.position)}`}`;
7433
+ var formatSummary2 = (summary) => [
7434
+ `Camera: ${summary.camera.kind.toLowerCase()} at ${point2(summary.camera.position)}, looking at ${point2(summary.camera.lookAt)}`,
7435
+ `Things (${summary.things.length})${summary.bounds === null ? "" : `, within ${point2(summary.bounds.min)} to ${point2(summary.bounds.max)}`}:`,
7436
+ ...summary.things.map(thingLine),
7437
+ `Lights (${summary.lights.length}):`,
7438
+ ...summary.lights.map(lightLine),
7439
+ `Staging: ${summary.staging.length === 0 ? "none" : summary.staging.join(", ")}`
7440
+ ].join(`
7441
+ `);
7442
+ // src/kernel/shots.ts
7443
+ import { Vector3 as Vector316 } from "three";
7444
+ var shotAngles2 = [
7445
+ "front",
7446
+ "back",
7447
+ "left",
7448
+ "right",
7449
+ "top",
7450
+ "three-quarter",
7451
+ "low"
7452
+ ];
7453
+ var directionOf = {
7454
+ front: [0, 0.15, 1],
7455
+ back: [0, 0.15, -1],
7456
+ left: [-1, 0.15, 0],
7457
+ right: [1, 0.15, 0],
7458
+ top: [0, 1, 0.0001],
7459
+ "three-quarter": [1, 0.7, 1],
7460
+ low: [0.35, -0.02, 1]
7461
+ };
7462
+ var shotCamera2 = (camera, subject, angle, area = { width: 16, height: 10 }, margin = 1.3) => {
7463
+ const centre = new Vector316(...subject.min).add(new Vector316(...subject.max)).multiplyScalar(0.5);
7464
+ const from = new Vector316(...directionOf[angle]).normalize().add(centre);
7465
+ const aimed = {
7466
+ ...camera,
7467
+ position: [from.x, from.y, from.z],
7468
+ lookAt: [centre.x, centre.y, centre.z]
7469
+ };
7470
+ return fitCamera2(aimed, subject, area, margin);
7471
+ };
7472
+ var moods2 = ["studio", "daylight", "sunset", "night"];
7473
+ var moodStaging2 = (mood, prefix = "mood") => {
7474
+ switch (mood) {
7475
+ case "studio":
7476
+ return {
7477
+ lights: [
7478
+ hemisphereLight2({
7479
+ key: `${prefix}:fill`,
7480
+ color: 16777215,
7481
+ groundColor: 7301730,
7482
+ intensity: 0.8
7483
+ }),
7484
+ directionalLight2({
7485
+ key: `${prefix}:key`,
7486
+ position: [4, 7, 3],
7487
+ intensity: 2,
7488
+ castShadow: true
7489
+ })
7490
+ ],
7491
+ environment: environment2({ environmentIntensity: 0.35 }),
7492
+ sky: null
7493
+ };
7494
+ case "daylight":
7495
+ return {
7496
+ lights: [
7497
+ hemisphereLight2({
7498
+ key: `${prefix}:sky`,
7499
+ color: 13625087,
7500
+ groundColor: 9075296,
7501
+ intensity: 1
7502
+ }),
7503
+ directionalLight2({
7504
+ key: `${prefix}:sun`,
7505
+ position: [6, 9, 4],
7506
+ intensity: 3,
7507
+ castShadow: true
7508
+ })
7509
+ ],
7510
+ environment: environment2({ environmentIntensity: 0.25 }),
7511
+ sky: sky2({ sunPosition: sunPositionFromAngles2(0.85, 0.35) })
7512
+ };
7513
+ case "sunset":
7514
+ return {
7515
+ lights: [
7516
+ hemisphereLight2({
7517
+ key: `${prefix}:sky`,
7518
+ color: 16757370,
7519
+ groundColor: 3811904,
7520
+ intensity: 0.6
7521
+ }),
7522
+ directionalLight2({
7523
+ key: `${prefix}:sun`,
7524
+ color: 16751194,
7525
+ position: [-8, 2.5, 3],
7526
+ intensity: 2.5,
7527
+ castShadow: true
7528
+ })
7529
+ ],
7530
+ environment: null,
7531
+ sky: sky2({ sunPosition: sunPositionFromAngles2(0.52, 0.25), turbidity: 8, rayleigh: 3 })
7532
+ };
7533
+ case "night":
7534
+ return {
7535
+ lights: [
7536
+ hemisphereLight2({
7537
+ key: `${prefix}:sky`,
7538
+ color: 3820160,
7539
+ groundColor: 1052696,
7540
+ intensity: 0.35
7541
+ }),
7542
+ directionalLight2({
7543
+ key: `${prefix}:moon`,
7544
+ color: 11190271,
7545
+ position: [-3, 8, -4],
7546
+ intensity: 0.8,
7547
+ castShadow: true
7548
+ })
7549
+ ],
7550
+ environment: null,
7551
+ sky: null
7552
+ };
7553
+ }
7554
+ };
7555
+ // src/kernel/inspect.ts
7556
+ var resolve = (ast) => ast._tag === "Suspend" && ("thunk" in ast) ? resolve(ast.thunk()) : ast;
7557
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7558
+ var labelOf = (path) => String(path.at(-1) ?? "value");
7559
+ var literalOf = (ast) => ("literal" in ast) ? ast.literal : undefined;
7560
+ var tagOf = (ast) => {
7561
+ const member = resolve(ast);
7562
+ if (!("propertySignatures" in member))
7563
+ return;
7564
+ const tag = member.propertySignatures.find((property) => property.name === "_tag");
7565
+ return tag === undefined ? undefined : literalOf(resolve(tag.type));
7566
+ };
7567
+ var memberFor = (types, value) => {
7568
+ if (isRecord(value) && "_tag" in value) {
7569
+ const tagged = types.find((member) => tagOf(member) === value["_tag"]);
7570
+ if (tagged !== undefined)
7571
+ return tagged;
7572
+ }
7573
+ if (value === null)
7574
+ return types.find((member) => resolve(member)._tag === "Null");
7575
+ return types.find((member) => resolve(member)._tag !== "Null") ?? types[0];
7576
+ };
7577
+ var fixed = (path, text) => ({
7578
+ _tag: "Fixed",
7579
+ path,
7580
+ label: labelOf(path),
7581
+ text
7582
+ });
7583
+ var isVector = (value) => Array.isArray(value) && value.length === 3 && value.every((n) => typeof n === "number");
7584
+ var walk = (given, value, path, hints) => {
7585
+ const ast = resolve(given);
7586
+ const label = labelOf(path);
7587
+ if (hints.colours.includes(ast) && typeof value === "number") {
7588
+ return [{ _tag: "Colour", path, label, value }];
7589
+ }
7590
+ if (hints.vectors.includes(ast) && isVector(value)) {
7591
+ return [{ _tag: "Vector", path, label, value }];
7592
+ }
7593
+ switch (ast._tag) {
7594
+ case "Number":
7595
+ return typeof value === "number" ? [{ _tag: "Number", path, label, value }] : [];
7596
+ case "String":
7597
+ return typeof value === "string" ? [{ _tag: "Text", path, label, value }] : [];
7598
+ case "Boolean":
7599
+ return typeof value === "boolean" ? [{ _tag: "Toggle", path, label, value }] : [];
7600
+ case "Literal":
7601
+ case "Null":
7602
+ return [fixed(path, String(value))];
7603
+ case "Union":
7604
+ return "types" in ast ? unionFields(ast.types, value, path, hints) : [];
7605
+ case "Objects":
7606
+ return "propertySignatures" in ast ? objectFields(ast, value, path, hints) : [];
7607
+ case "Arrays":
7608
+ return "elements" in ast ? arrayFields(ast, value, path, hints) : [];
7609
+ default:
7610
+ return [fixed(path, String(value))];
7611
+ }
7612
+ };
7613
+ var unionFields = (types, value, path, hints) => {
7614
+ const literals = types.map((member) => literalOf(resolve(member)));
7615
+ if (typeof value === "string" && literals.every((literal) => typeof literal === "string")) {
7616
+ return [{ _tag: "Choice", path, label: labelOf(path), value, options: literals.map(String) }];
7617
+ }
7618
+ const member = memberFor(types, value);
7619
+ return member === undefined ? [fixed(path, String(value))] : walk(member, value, path, hints);
7620
+ };
7621
+ var objectFields = (ast, value, path, hints) => {
7622
+ if (!isRecord(value))
7623
+ return [fixed(path, String(value))];
7624
+ return ast.propertySignatures.flatMap((property) => propertyFields(String(property.name), property.type, value, path, hints));
7625
+ };
7626
+ function propertyFields(name, type, value, path, hints) {
7627
+ if (!(name in value))
7628
+ return [];
7629
+ if (name === "_tag")
7630
+ return [fixed([...path, name], String(value[name]))];
7631
+ return walk(type, value[name], [...path, name], hints);
7632
+ }
7633
+ var arrayFields = (ast, value, path, hints) => {
7634
+ if (!Array.isArray(value))
7635
+ return [fixed(path, String(value))];
7636
+ if (ast.rest.length > 0) {
7637
+ return [fixed(path, `${value.length} ${value.length === 1 ? "item" : "items"}`)];
7638
+ }
7639
+ return ast.elements.flatMap((element, index) => walk(element, value[index], [...path, index], hints));
7640
+ };
7641
+ var inspectFields2 = (ast, value, hints = { colours: [], vectors: [] }) => walk(ast, value, [], hints);
7642
+ var setAt2 = (value, path, next) => {
7643
+ const [head, ...rest] = path;
7644
+ if (head === undefined)
7645
+ return next;
7646
+ if (Array.isArray(value) && typeof head === "number") {
7647
+ return value.map((item, index) => index === head ? setAt2(item, rest, next) : item);
7648
+ }
7649
+ if (isRecord(value) && typeof head === "string") {
7650
+ return { ...value, [head]: setAt2(value[head], rest, next) };
7651
+ }
7652
+ return value;
7653
+ };
7654
+ // src/kernel/sceneChanges.ts
7655
+ var settingOf = {
7656
+ SetCamera: "camera",
7657
+ SetViews: "views",
7658
+ SetRenderSettings: "render",
7659
+ DefineResources: "resources"
7660
+ };
7661
+ var comparedSettings = [
7662
+ "gestures",
7663
+ "overlays",
7664
+ "environment",
7665
+ "sky",
7666
+ "contactShadows",
7667
+ "physics",
7668
+ "xr",
7669
+ "measure"
7670
+ ];
7671
+ var entriesOf = (value) => Object.entries(value);
7672
+ var same = (a, b) => {
7673
+ if (a === b)
7674
+ return true;
7675
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
7676
+ return false;
7677
+ if (Array.isArray(a) !== Array.isArray(b))
7678
+ return false;
7679
+ const fieldsA = entriesOf(a);
7680
+ const fieldsB = new Map(entriesOf(b));
7681
+ return fieldsA.length === fieldsB.size && fieldsA.every(([key, value]) => fieldsB.has(key) && same(value, fieldsB.get(key)));
7682
+ };
7683
+ var nodeChange = (op, place) => {
7684
+ switch (op._tag) {
7685
+ case "Create":
7686
+ return [{ place, _tag: "Added", key: op.node.key, kind: op.node._tag, parent: op.parent }];
7687
+ case "Remove":
7688
+ return [{ place, _tag: "Removed", key: op.key }];
7689
+ case "Replace":
7690
+ return [{ place, _tag: "Replaced", key: op.node.key, kind: op.node._tag }];
7691
+ case "Update":
7692
+ return [{ place, _tag: "Changed", key: op.node.key, fields: op.fields }];
7693
+ case "Move":
7694
+ return [{ place, _tag: "Moved", key: op.key, parent: op.parent }];
7695
+ case "Order":
7696
+ return [{ place, _tag: "Reordered", parent: op.parent, keys: op.keys }];
7697
+ default:
7698
+ return [];
7699
+ }
7700
+ };
7701
+ var behavioursIn = (description) => {
7702
+ const names = new Set;
7703
+ const visit = (node) => {
7704
+ if ("behaviour" in node && node.behaviour !== null)
7705
+ names.add(node.behaviour.name);
7706
+ if (node._tag === "Group")
7707
+ node.children.forEach(visit);
7708
+ };
7709
+ description.children.forEach(visit);
7710
+ return names;
7711
+ };
7712
+ var startOf = (before) => {
7713
+ const indexed = indexScene2(before, behavioursIn(before));
7714
+ return indexed._tag === "Indexed" ? indexed.index : emptyIndex2;
7715
+ };
7716
+ var isSetting = (setting) => setting !== undefined;
7717
+ var settingsOf = (before, after, ops) => [
7718
+ ...ops.map((op) => settingOf[op._tag]).filter(isSetting),
7719
+ ...comparedSettings.filter((setting) => !same(before[setting], after[setting]))
7720
+ ];
7721
+ var changesIn = (before, after, place) => {
7722
+ const result = diff2(startOf(before), after, behavioursIn(after));
7723
+ if (result._tag === "Invalid")
7724
+ return result;
7725
+ const settings = settingsOf(before, after, result.ops);
7726
+ const settingsChange = settings.length === 0 ? [] : [{ place, _tag: "Settings", settings }];
7727
+ return {
7728
+ _tag: "Changes",
7729
+ changes: [...settingsChange, ...result.ops.flatMap((op) => nodeChange(op, place))]
7730
+ };
7731
+ };
7732
+ var nestedScene = (parent, children) => ({ ...parent, hud: null, renderTextures: [], children });
7733
+ var hudChanges = (before, after) => {
7734
+ if (before.hud === null && after.hud === null)
7735
+ return { _tag: "Changes", changes: [] };
7736
+ if (after.hud === null)
7737
+ return { _tag: "Changes", changes: [{ place: "hud", _tag: "HudRemoved" }] };
7738
+ const added = before.hud === null ? [{ place: "hud", _tag: "HudAdded" }] : [];
7739
+ const inside = changesIn(nestedScene(before, before.hud?.children ?? []), nestedScene(before, after.hud.children), "hud");
7740
+ return inside._tag === "Invalid" ? inside : { _tag: "Changes", changes: [...added, ...inside.changes] };
7741
+ };
7742
+ var textureChanges = (before, after) => {
7743
+ const changes = [];
7744
+ for (const texture of after.renderTextures) {
7745
+ const was = before.renderTextures.find((candidate) => candidate.id === texture.id);
7746
+ const place = { texture: texture.id };
7747
+ if (was === undefined)
7748
+ changes.push({ place, _tag: "TextureAdded", id: texture.id });
7749
+ const inside = changesIn(nestedScene(before, was?.children ?? []), nestedScene(before, texture.children), place);
7750
+ if (inside._tag === "Invalid")
7751
+ return inside;
7752
+ changes.push(...inside.changes);
7753
+ }
7754
+ for (const texture of before.renderTextures) {
7755
+ if (!after.renderTextures.some((candidate) => candidate.id === texture.id)) {
7756
+ changes.push({ place: { texture: texture.id }, _tag: "TextureRemoved", id: texture.id });
7757
+ }
7758
+ }
7759
+ return { _tag: "Changes", changes };
7760
+ };
7761
+ var sceneChanges2 = (before, after) => {
7762
+ const parts = [
7763
+ changesIn(before, after, "scene"),
7764
+ hudChanges(before, after),
7765
+ textureChanges(before, after)
7766
+ ];
7767
+ const invalid = parts.find((part) => part._tag === "Invalid");
7768
+ if (invalid !== undefined)
7769
+ return invalid;
7770
+ return {
7771
+ _tag: "Changes",
7772
+ changes: parts.flatMap((part) => part._tag === "Changes" ? part.changes : [])
7773
+ };
7774
+ };
7775
+ var placeLabel = (place) => {
7776
+ if (place === "scene")
7777
+ return "";
7778
+ return place === "hud" ? "hud " : `texture ${place.texture} `;
7779
+ };
7780
+ var parentLabel = (parent) => parent === null ? "the root" : parent;
7781
+ var lineOf2 = (change) => {
7782
+ const where = placeLabel(change.place);
7783
+ switch (change._tag) {
7784
+ case "Added":
7785
+ return `${where}+ ${change.kind} ${change.key}${change.parent === null ? "" : ` in ${change.parent}`}`;
7786
+ case "Removed":
7787
+ return `${where}- ${change.key}`;
7788
+ case "Replaced":
7789
+ return `${where}± ${change.key}, now a ${change.kind}`;
7790
+ case "Changed":
7791
+ return `${where}~ ${change.key}: ${change.fields.join(", ")}`;
7792
+ case "Moved":
7793
+ return `${where}> ${change.key} to ${parentLabel(change.parent)}`;
7794
+ case "Reordered":
7795
+ return `${where}= order of ${parentLabel(change.parent)}: ${change.keys.join(", ")}`;
7796
+ case "Settings":
7797
+ return `${where}⋯ scene: ${change.settings.join(", ")}`;
7798
+ case "HudAdded":
7799
+ return "+ hud";
7800
+ case "HudRemoved":
7801
+ return "- hud";
7802
+ case "TextureAdded":
7803
+ return `+ texture ${change.id}`;
7804
+ case "TextureRemoved":
7805
+ return `- texture ${change.id}`;
7806
+ }
7807
+ };
7808
+ var formatChanges2 = (changes) => changes.length === 0 ? "No changes." : changes.map(lineOf2).join(`
7809
+ `);
7810
+ // src/kernel/tape.ts
7811
+ var tape2 = (options = {}) => ({
7812
+ from: null,
7813
+ to: null,
7814
+ pointer: null,
7815
+ snap: options.snap ?? null
7816
+ });
7817
+ var snapped2 = (state, point) => {
7818
+ const step = state.snap;
7819
+ if (step === null)
7820
+ return point;
7821
+ return [
7822
+ Math.round(point[0] / step) * step,
7823
+ Math.round(point[1] / step) * step,
7824
+ Math.round(point[2] / step) * step
7825
+ ];
7826
+ };
7827
+ var placeTapeEnd2 = (state, point) => {
7828
+ const at = snapped2(state, point);
7829
+ if (state.from !== null && state.to === null)
7830
+ return { ...state, to: at, pointer: null };
7831
+ return { ...state, from: at, to: null, pointer: null };
7832
+ };
7833
+ var pointTape2 = (state, point) => state.from !== null && state.to === null ? { ...state, pointer: point === null ? null : snapped2(state, point) } : state;
7834
+ var clearTape2 = (state) => ({ ...tape2({ snap: state.snap }) });
7835
+ var farEnd = (state) => state.to ?? state.pointer;
7836
+ var distance = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
7837
+ var tapeLength2 = (state) => {
7838
+ const end = farEnd(state);
7839
+ return state.from === null || end === null ? undefined : distance(state.from, end);
7840
+ };
7841
+ var tapeMiddle2 = (state) => {
7842
+ const end = farEnd(state);
7843
+ if (state.from === null || end === null)
7844
+ return;
7845
+ return [(state.from[0] + end[0]) / 2, (state.from[1] + end[1]) / 2, (state.from[2] + end[2]) / 2];
7846
+ };
7847
+ var endBall = (key, at, look) => mesh2({
7848
+ key,
7849
+ position: at,
7850
+ geometry: sphere2({ radius: look.endRadius, widthSegments: 16, heightSegments: 8 }),
7851
+ material: basic2({ color: look.color })
7852
+ });
7853
+ var tapeNodes2 = (key, state, look = {}) => {
7854
+ const shown = {
7855
+ color: look.color ?? 16763955,
7856
+ width: look.width ?? 3,
7857
+ endRadius: look.endRadius ?? 0.03
7858
+ };
7859
+ const end = farEnd(state);
7860
+ const ends = [state.from, state.to].flatMap((at, index) => at === null ? [] : [endBall(`${key}:end:${index}`, at, shown)]);
7861
+ const span = state.from === null || end === null ? [] : [
7862
+ line2({
7863
+ key: `${key}:line`,
7864
+ points: [state.from, end],
7865
+ color: shown.color,
7866
+ width: shown.width,
7867
+ dashed: state.to === null,
7868
+ dashSize: 0.08,
7869
+ gapSize: 0.05
7870
+ })
7871
+ ];
7872
+ return group2({ key, children: [...span, ...ends] });
7873
+ };
7874
+ // src/kernel/pivot.ts
7875
+ import { Euler as Euler7, Quaternion as Quaternion8, Vector3 as Vector317 } from "three";
7876
+ var pivot2 = (options = {}) => ({
7877
+ scale: options.scale ?? 1,
7878
+ depthTest: options.depthTest ?? true,
7879
+ disableAxes: options.disableAxes ?? false,
7880
+ disableSliders: options.disableSliders ?? false,
7881
+ disableRotations: options.disableRotations ?? false,
7882
+ disableScaling: options.disableScaling ?? false,
7883
+ hovered: null,
7884
+ grab: null
7885
+ });
7886
+ var unit = {
7887
+ x: new Vector317(1, 0, 0),
7888
+ y: new Vector317(0, 1, 0),
7889
+ z: new Vector317(0, 0, 1)
7890
+ };
7891
+ var planeOf2 = {
7892
+ z: ["x", "y"],
7893
+ y: ["z", "x"],
7894
+ x: ["y", "z"]
7895
+ };
7896
+ var turnOf2 = (rotation) => new Quaternion8().setFromEuler(new Euler7(rotation[0], rotation[1], rotation[2]));
7897
+ var toVec35 = (vector) => [vector.x, vector.y, vector.z];
7898
+ var kindOf2 = (handle) => handle.split("-")[0];
7899
+ var axisOf = (handle) => handle.split("-")[1];
7900
+ var frameOf = (handle, placement) => {
7901
+ const turn = turnOf2(placement.rotation);
7902
+ const axis = axisOf(handle);
7903
+ const [first, second] = planeOf2[axis];
7904
+ const kind = kindOf2(handle);
7905
+ const flat = kind === "slider" || kind === "rotator";
7906
+ const e1 = (flat ? unit[first] : unit[axis]).clone().applyQuaternion(turn);
7907
+ const e2 = (flat ? unit[second] : unit[axis]).clone().applyQuaternion(turn);
7908
+ return { e1, e2, normal: e1.clone().cross(e2).normalize() };
7909
+ };
7910
+ var pressPivot2 = (state, placement, handle, point) => {
7911
+ const { e1, e2, normal } = frameOf(handle, placement);
7912
+ return {
7913
+ ...state,
7914
+ grab: {
7915
+ handle,
7916
+ start: placement,
7917
+ point,
7918
+ e1: toVec35(e1),
7919
+ e2: toVec35(e2),
7920
+ normal: toVec35(kindOf2(handle) === "arrow" || kindOf2(handle) === "sphere" ? e1 : normal)
7921
+ }
7922
+ };
7923
+ };
7924
+ var releasePivot2 = (state) => ({ ...state, grab: null });
7925
+ var hoverPivot2 = (state, handle) => state.hovered === handle ? state : { ...state, hovered: handle };
7926
+ var offsetAlong = (point, direction, ray) => {
7927
+ const origin = new Vector317(...ray.origin);
7928
+ const toward = new Vector317(...ray.direction);
7929
+ const e1 = direction.dot(direction);
7930
+ const e2 = direction.dot(point) - direction.dot(origin);
7931
+ const e3 = direction.dot(toward);
7932
+ if (e3 === 0)
7933
+ return -e2 / e1;
7934
+ const v1 = toward.clone().multiplyScalar(e1 / e3).sub(direction);
7935
+ const v2 = toward.clone().multiplyScalar(e2 / e3).add(origin).sub(point);
7936
+ return -v1.dot(v2) / v1.dot(v1);
7937
+ };
7938
+ var meetPlane = (ray, at, normal) => {
7939
+ const origin = new Vector317(...ray.origin);
7940
+ const toward = new Vector317(...ray.direction);
7941
+ const facing = normal.dot(toward);
7942
+ if (facing === 0)
7943
+ return;
7944
+ return origin.addScaledVector(toward, normal.dot(at.clone().sub(origin)) / facing);
7945
+ };
7946
+ var angleBetween = (from, to, at, e1, e2) => {
7947
+ const a = from.clone().sub(at);
7948
+ const b = to.clone().sub(at);
7949
+ return Math.atan2(b.dot(e2), b.dot(e1)) - Math.atan2(a.dot(e2), a.dot(e1));
7950
+ };
7951
+ var vectorsOf = (grab) => ({
7952
+ point: new Vector317(...grab.point),
7953
+ e1: new Vector317(...grab.e1),
7954
+ e2: new Vector317(...grab.e2),
7955
+ normal: new Vector317(...grab.normal),
7956
+ at: new Vector317(...grab.start.position)
7957
+ });
7958
+ var moved2 = (start, by) => ({
7959
+ ...start,
7960
+ position: toVec35(new Vector317(...start.position).add(by))
7961
+ });
7962
+ var arrowDrag = (grab, ray) => {
7963
+ const { point, e1 } = vectorsOf(grab);
7964
+ return moved2(grab.start, e1.clone().multiplyScalar(offsetAlong(point, e1, ray)));
7965
+ };
7966
+ var sliderDrag = (grab, ray) => {
7967
+ const { point, e1, e2, normal, at } = vectorsOf(grab);
7968
+ const meet = meetPlane(ray, at, normal);
7969
+ if (meet === undefined)
7970
+ return grab.start;
7971
+ const offset = meet.sub(point);
7972
+ return moved2(grab.start, e1.multiplyScalar(offset.dot(e1)).add(e2.multiplyScalar(offset.dot(e2))));
7973
+ };
7974
+ var tenDegrees = Math.PI / 18;
7975
+ var rotatorDrag = (grab, ray, snap) => {
7976
+ const { point, e1, e2, normal, at } = vectorsOf(grab);
7977
+ const meet = meetPlane(ray, at, normal);
7978
+ if (meet === undefined)
7979
+ return grab.start;
7980
+ const turned = angleBetween(point, meet, at, e1, e2);
7981
+ const angle = snap ? Math.round(turned / tenDegrees) * tenDegrees : turned;
7982
+ const turn = new Quaternion8().setFromAxisAngle(normal, angle).multiply(turnOf2(grab.start.rotation));
7983
+ const euler = new Euler7().setFromQuaternion(turn.normalize());
7984
+ return { ...grab.start, rotation: [euler.x, euler.y, euler.z] };
7985
+ };
7986
+ var sphereDrag = (state, grab, ray, snap) => {
7987
+ const { point, e1 } = vectorsOf(grab);
7988
+ const grown = 2 ** (offsetAlong(point, e1, ray) / state.scale * 0.2);
7989
+ const factor = Math.max(snap ? Math.round(grown * 10) / 10 : grown, 0.00001);
7990
+ const index = { x: 0, y: 1, z: 2 }[axisOf(grab.handle)];
7991
+ const scale = [
7992
+ grab.start.scale[0] * (index === 0 ? factor : 1),
7993
+ grab.start.scale[1] * (index === 1 ? factor : 1),
7994
+ grab.start.scale[2] * (index === 2 ? factor : 1)
7995
+ ];
7996
+ return { ...grab.start, scale };
7997
+ };
7998
+ var dragPivot2 = (state, ray, options = {}) => {
7999
+ const grab = state.grab;
8000
+ if (grab === null)
8001
+ return;
8002
+ const snap = options.snap ?? false;
8003
+ const kind = kindOf2(grab.handle);
8004
+ if (kind === "arrow")
8005
+ return arrowDrag(grab, ray);
8006
+ if (kind === "slider")
8007
+ return sliderDrag(grab, ray);
8008
+ if (kind === "rotator")
8009
+ return rotatorDrag(grab, ray, snap);
8010
+ return sphereDrag(state, grab, ray, snap);
8011
+ };
8012
+ var colours2 = { x: 16719968, y: 2154368, z: 2130175 };
8013
+ var hoveredColour = 16777024;
8014
+ var handles = ["arrow", "slider", "rotator", "sphere"];
8015
+ var pivotHandleOf2 = (key, hit) => {
8016
+ const [owner, name] = hit.split(":");
8017
+ if (owner !== key)
8018
+ return;
8019
+ return handles.flatMap((kind) => ["x", "y", "z"].map((axis) => `${kind}-${axis}`)).find((handle) => handle === name);
8020
+ };
8021
+ var lookOf = (state, handle) => basic2({
8022
+ color: state.grab?.handle === handle || state.hovered === handle ? hoveredColour : colours2[axisOf(handle)],
8023
+ depthTest: state.depthTest,
8024
+ side: "double"
8025
+ });
8026
+ var unseen = basic2({ opacity: 0, transparent: true, depthTest: false });
8027
+ var pointAlong2 = {
8028
+ x: [0, 0, -Math.PI / 2],
8029
+ y: [0, 0, 0],
8030
+ z: [Math.PI / 2, 0, 0]
8031
+ };
8032
+ var along = (axis, distance) => {
8033
+ const { x, y, z } = unit[axis];
8034
+ return [x * distance, y * distance, z * distance];
8035
+ };
8036
+ var arrowNodes = (key, state, axis) => {
8037
+ const handle = `arrow-${axis}`;
8038
+ const size = state.scale;
8039
+ const coneLength = size / 5;
8040
+ const stem = size - coneLength;
8041
+ const material = lookOf(state, handle);
8042
+ return [
8043
+ mesh2({
8044
+ key: `${key}:${handle}:stem`,
8045
+ position: along(axis, stem / 2),
8046
+ rotation: fixed2(pointAlong2[axis]),
8047
+ geometry: cylinder2({ radiusTop: size / 80, radiusBottom: size / 80, height: stem }),
8048
+ material
8049
+ }),
8050
+ mesh2({
8051
+ key: `${key}:${handle}:hit`,
8052
+ position: along(axis, size / 2),
8053
+ rotation: fixed2(pointAlong2[axis]),
8054
+ geometry: cylinder2({ radiusTop: size * 0.07, radiusBottom: size * 0.07, height: size }),
8055
+ material: unseen,
8056
+ interactive: true
8057
+ }),
8058
+ mesh2({
8059
+ key: `${key}:${handle}:cone`,
8060
+ position: along(axis, stem + coneLength / 2),
8061
+ rotation: fixed2(pointAlong2[axis]),
8062
+ geometry: cone2({ radius: size / 20, height: coneLength, radialSegments: 24 }),
8063
+ material,
8064
+ interactive: true
8065
+ })
8066
+ ];
8067
+ };
8068
+ var ontoPlane = {
8069
+ z: [0, 0, 0],
8070
+ y: [-Math.PI / 2, 0, -Math.PI / 2],
8071
+ x: [Math.PI / 2, Math.PI / 2, 0]
8072
+ };
8073
+ var sliderNode = (key, state, axis) => {
8074
+ const handle = `slider-${axis}`;
8075
+ const out = state.scale / 7 * 1.7;
8076
+ const side = state.scale * 0.225;
8077
+ return group2({
8078
+ key: `${key}:${handle}`,
8079
+ rotation: fixed2(ontoPlane[axis]),
8080
+ children: [
8081
+ mesh2({
8082
+ key: `${key}:${handle}:square`,
8083
+ position: [out, out, 0],
8084
+ geometry: plane2({ width: side, height: side }),
8085
+ material: lookOf(state, handle),
8086
+ interactive: true
8087
+ })
8088
+ ]
8089
+ });
8090
+ };
8091
+ var arcSegments = 8;
8092
+ var rotatorNode = (key, state, axis) => {
8093
+ const handle = `rotator-${axis}`;
8094
+ const radius = state.scale * 0.65;
8095
+ const step = Math.PI / 2 / arcSegments;
8096
+ const chord = 2 * radius * Math.sin(step / 2);
8097
+ const segment = (index, part) => {
8098
+ const middle = step * (index + 0.5);
8099
+ const thickness = part === "line" ? radius / 50 : state.scale * 0.03;
8100
+ return mesh2({
8101
+ key: `${key}:${handle}:${part}:${index}`,
8102
+ position: [Math.cos(middle) * radius, Math.sin(middle) * radius, 0],
8103
+ rotation: fixed2([0, 0, middle]),
8104
+ geometry: cylinder2({ radiusTop: thickness, radiusBottom: thickness, height: chord }),
8105
+ material: part === "line" ? lookOf(state, handle) : unseen,
8106
+ interactive: part === "hit"
8107
+ });
8108
+ };
8109
+ const indices = Array.from({ length: arcSegments }, (_, index) => index);
8110
+ return group2({
8111
+ key: `${key}:${handle}`,
8112
+ rotation: fixed2(ontoPlane[axis]),
8113
+ children: [
8114
+ ...indices.map((index) => segment(index, "line")),
8115
+ ...indices.map((index) => segment(index, "hit"))
8116
+ ]
8117
+ });
8118
+ };
8119
+ var sphereNode = (key, state, axis) => mesh2({
8120
+ key: `${key}:sphere-${axis}`,
8121
+ position: along(axis, state.scale * 1.2),
8122
+ geometry: sphere2({ radius: state.scale / 22.5, widthSegments: 12, heightSegments: 12 }),
8123
+ material: lookOf(state, `sphere-${axis}`),
8124
+ interactive: true
8125
+ });
8126
+ var axesOf = ["x", "y", "z"];
8127
+ var pivotNodes2 = (key, state, placement) => group2({
8128
+ key,
8129
+ position: placement.position,
8130
+ rotation: fixed2(placement.rotation),
8131
+ children: [
8132
+ ...state.disableAxes ? [] : axesOf.flatMap((axis) => arrowNodes(key, state, axis)),
8133
+ ...state.disableSliders ? [] : axesOf.map((axis) => sliderNode(key, state, axis)),
8134
+ ...state.disableRotations ? [] : axesOf.map((axis) => rotatorNode(key, state, axis)),
8135
+ ...state.disableScaling ? [] : axesOf.map((axis) => sphereNode(key, state, axis))
8136
+ ]
8137
+ });
8138
+ // src/kernel/viewportGizmo.ts
8139
+ import { Euler as Euler8, Matrix4 as Matrix46, Quaternion as Quaternion9, Vector3 as Vector318 } from "three";
8140
+ var colours3 = { x: 16719968, y: 2154368, z: 2130175 };
8141
+ var ink = 0;
8142
+ var directions = {
8143
+ x: [1, 0, 0],
8144
+ y: [0, 1, 0],
8145
+ z: [0, 0, 1],
8146
+ "-x": [-1, 0, 0],
8147
+ "-y": [0, -1, 0],
8148
+ "-z": [0, 0, -1]
8149
+ };
8150
+ var axes2 = ["x", "y", "z", "-x", "-y", "-z"];
8151
+ var cameraTurn = (camera) => new Quaternion9().setFromRotationMatrix(new Matrix46().lookAt(new Vector318(...camera.position), new Vector318(...camera.lookAt), new Vector318(0, 1, 0)));
8152
+ var gizmoTurn = (camera) => {
8153
+ const turn = new Euler8().setFromQuaternion(cameraTurn(camera).invert());
8154
+ return [turn.x, turn.y, turn.z];
8155
+ };
8156
+ var stroke = (key, length, angle, at) => mesh2({
8157
+ key,
8158
+ position: [at[0], at[1], 0.01],
8159
+ rotation: fixed2([0, 0, angle]),
8160
+ geometry: box2({ width: length, height: 0.07, depth: 0.001 }),
8161
+ material: basic2({ color: ink })
8162
+ });
8163
+ var letters = {
8164
+ x: [
8165
+ [0.34, Math.PI / 4, [0, 0]],
8166
+ [0.34, -Math.PI / 4, [0, 0]]
8167
+ ],
8168
+ y: [
8169
+ [0.14, Math.PI / 2, [0, -0.07]],
8170
+ [0.17, Math.PI / 3, [0.04, 0.06]],
8171
+ [0.17, -Math.PI / 3, [-0.04, 0.06]]
8172
+ ],
8173
+ z: [
8174
+ [0.22, 0, [0, 0.11]],
8175
+ [0.22, 0, [0, -0.11]],
8176
+ [0.3, Math.PI / 4, [0, 0]]
8177
+ ]
8178
+ };
8179
+ var isPositive = (axis) => axis.length === 1;
8180
+ var colourOf2 = (axis) => colours3[isPositive(axis) ? axis : axis.slice(1)];
8181
+ var headScale = (axis, hovered) => (isPositive(axis) ? 1 : 0.75) * (axis === hovered ? 1.2 : 1);
8182
+ var labelOf2 = (key, axis) => isPositive(axis) ? letters[axis].map(([length, angle, at], index) => stroke(`${key}:${axis}:letter:${index}`, length, angle, at)) : [];
8183
+ var head = (key, axis, hovered) => {
8184
+ const scale = headScale(axis, hovered);
8185
+ return group2({
8186
+ key: `${key}:${axis}`,
8187
+ position: directions[axis],
8188
+ rotation: billboard2(),
8189
+ scale: [scale, scale, scale],
8190
+ interactive: true,
8191
+ children: [
8192
+ mesh2({
8193
+ key: `${key}:${axis}:disc`,
8194
+ geometry: circle2({ radius: 0.25, segments: 32 }),
8195
+ material: basic2({
8196
+ color: colourOf2(axis),
8197
+ opacity: isPositive(axis) ? 1 : 0.75,
8198
+ transparent: !isPositive(axis)
8199
+ })
8200
+ }),
8201
+ ...labelOf2(key, axis)
8202
+ ]
8203
+ });
8204
+ };
8205
+ var bar = (key, axis) => {
8206
+ const [x, y, z] = directions[axis];
8207
+ return mesh2({
8208
+ key: `${key}:${axis}:bar`,
8209
+ position: [x * 0.4, y * 0.4, z * 0.4],
8210
+ geometry: box2({
8211
+ width: axis === "x" ? 0.8 : 0.05,
8212
+ height: axis === "y" ? 0.8 : 0.05,
8213
+ depth: axis === "z" ? 0.8 : 0.05
8214
+ }),
8215
+ material: basic2({ color: colours3[axis] })
8216
+ });
8217
+ };
8218
+ var viewportGizmoNodes2 = (key, camera, options = {}) => {
8219
+ const size = options.size ?? 40;
8220
+ const hovered = options.hovered ?? null;
8221
+ const shown = options.hideNegativeAxes === true ? axes2.filter(isPositive) : axes2;
8222
+ return group2({
8223
+ key,
8224
+ rotation: fixed2(gizmoTurn(camera)),
8225
+ scale: [size, size, size],
8226
+ children: [
8227
+ bar(key, "x"),
8228
+ bar(key, "y"),
8229
+ bar(key, "z"),
8230
+ ...shown.map((axis) => head(key, axis, hovered))
8231
+ ]
8232
+ });
8233
+ };
8234
+ var viewportAxisOf2 = (key, hit) => {
8235
+ const [owner, axis] = hit.split(":");
8236
+ if (owner !== key)
8237
+ return;
8238
+ return axes2.find((candidate) => candidate === axis);
8239
+ };
8240
+ var cameraAlong2 = (camera, axis) => {
8241
+ const target = new Vector318(...camera.lookAt);
8242
+ const distance = new Vector318(...camera.position).distanceTo(target);
8243
+ const direction = new Vector318(...directions[axis]);
8244
+ if (axis === "y" || axis === "-y")
8245
+ direction.z = 0.000001;
8246
+ const position = direction.normalize().multiplyScalar(distance).add(target);
8247
+ return { ...camera, position: [position.x, position.y, position.z] };
8248
+ };
8249
+ export { __export, handJoints2, clipTime2, clipRunning2, createPose2, resetPose2, float2, builtinBehaviours2, spinAngle2, writeRotation2, buildMaterial2, updateMaterial2, vec3Equals2, rotationEquals2, shapeEquals2, geometryRefEquals2, instanceEquals2, instancesEqual2, behaviourEquals2, animationEquals2, transitionEquals2, materialRefEquals2, cameraEquals2, renderSettingsEquals2, effectsEqual2, viewsEqual2, resourcesEqual2, overlaysEqual2, environmentEquals2, skyEquals2, contactShadowsEquals2, bodyEquals2, assetsEqual2, physicsEquals2, xrEquals2, capacityFor2, createPerformanceMonitor2, easeProgress2, startTween2, applyTween2, fixed2, spin2, billboard2, box2, sphere2, plane2, cylinder2, torus2, cone2, capsule2, roundedBox2, backdrop2, ring2, circle2, text2, instant2, ease2, basic2, lambert2, phong2, standard2, physical2, normalMaterial2, shadowMaterial2, grid2, shaderGrid2, sharedGeometry2, lentGeometry2, sharedMaterial2, group2, detailed2, line2, points2, sparkles2, stars2, catmullRomPoints2, catmullRomPointAt2, catmullRomColors2, cubicBezierPoints2, quadraticBezierPoints2, headSpace2, jointSpace2, controllerSpace2, anchorSpace2, planeSpace2, meshSpace2, xrMeshGeometryId2, xrMesh2, xrAnchor2, mesh2, trail2, edges2, body2, interactionGroups2, cuboidCollider2, ballCollider2, capsuleCollider2, cylinderCollider2, coneCollider2, velocityMotor2, positionMotor2, physics2, bodyRef2, joint2, fixedJoint2, sphericalJoint2, revoluteJoint2, prismaticJoint2, ropeJoint2, springJoint2, glide2, xr2, instance2, instances2, behave2, play2, borrowed2, label2, colorRange2, ambientLight2, directionalLight2, pointLight2, spotLight2, hemisphereLight2, perspectiveCamera2, orthographicCamera2, cameraShake2, bloom2, vignette2, film2, renderSettings2, view2, renderTexture2, environment2, sunPositionFromAngles2, sky2, contactShadows2, modelAsset2, textureAsset2, environmentAsset2, fontAsset2, overlay2, hud2, scene2, emptyIndex2, indexScene2, changedFields2, diff2, createResourceStore2, createSceneClock2, createManualFrames2, createGatedFrames2, createFramePacer2, defaultOrbit2, orbitOptions2, rotateCamera2, dollyCamera2, panCamera2, dollyCameraToward2, orbit2, orbitKey2, autoRotated2, fitCamera2, clipCamera2, placeOverlays2, interactionBits2, snapshotText2, snapshotBytes2, colliderFor2, describedColliders2, collidersFor2, colliderSignature2, locomote2, teleport2, glided2, createXrTracker2, environmentPresets2, environmentPresetUrl2, stagePresets2, stage2, centerOffset2, stageBounds2, noModifiers2, pointOnPlane2, createPicker2, createPointerTracker2, createSceneLoop2, recordingRendererFactory2, createSceneGraph2, previewSvg2, gizmo2, pressGizmo2, releaseGizmo2, hoverGizmo2, dragGizmo2, movedAlike2, gizmoHandleOf2, gizmoNodes2, boxSelect2, exports_history, exports_lockstep, exports_timeline, thingsBounds2, describeScene2, formatSummary2, shotAngles2, shotCamera2, moods2, moodStaging2, inspectFields2, setAt2, sceneChanges2, formatChanges2, tape2, placeTapeEnd2, pointTape2, clearTape2, tapeLength2, tapeMiddle2, tapeNodes2, pivot2, pressPivot2, releasePivot2, hoverPivot2, dragPivot2, pivotHandleOf2, pivotNodes2, viewportGizmoNodes2, viewportAxisOf2, cameraAlong2 };
5419
8250
 
5420
- //# debugId=DD15A712F87B1C4C64756E2164756E21
5421
- //# sourceMappingURL=index-8cf8t6by.js.map
8251
+ //# debugId=0311CB843C4BF34064756E2164756E21
8252
+ //# sourceMappingURL=index-re3rrfwd.js.map