lecodes-sdk 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.md +5 -2
  2. package/dist/editor.d.ts +12 -0
  3. package/dist/global.d.ts +4 -8
  4. package/dist/host.d.ts +2 -3
  5. package/dist/types/animate/tween/Animation.d.ts +0 -3
  6. package/dist/types/animate/tween/animateValue.d.ts +4 -2
  7. package/dist/types/animate/tween/easing.d.ts +8 -0
  8. package/dist/types/animate/tween/spec.d.ts +15 -7
  9. package/dist/types/audio/audio.d.ts +2 -1
  10. package/dist/types/canvas/Canvas.d.ts +40 -108
  11. package/dist/types/canvas/gen/cssColor.d.ts +17 -0
  12. package/dist/types/canvas/gen/recorder.d.ts +118 -0
  13. package/dist/types/canvas/gen/spec.d.ts +144 -0
  14. package/dist/types/core/color.d.ts +3 -1
  15. package/dist/types/core/pins.d.ts +18 -0
  16. package/dist/types/g2/Node2D.d.ts +5 -8
  17. package/dist/types/g2/Scene2D.d.ts +6 -2
  18. package/dist/types/gl/Foliage.d.ts +30 -7
  19. package/dist/types/gl/Light.d.ts +8 -0
  20. package/dist/types/gl/Lightmap.d.ts +13 -2
  21. package/dist/types/gl/Material.d.ts +17 -3
  22. package/dist/types/gl/Model.d.ts +6 -2
  23. package/dist/types/gl/Node.d.ts +3 -6
  24. package/dist/types/gl/Scene.d.ts +41 -13
  25. package/dist/types/gl/Texture.d.ts +1 -1
  26. package/dist/types/gl/animation/Locomotion.d.ts +8 -1
  27. package/dist/types/inject.d.ts +11 -11
  28. package/dist/types/inject.editor.d.ts +1 -0
  29. package/dist/types/net/core.d.ts +7 -0
  30. package/dist/types/plugin.d.ts +76 -0
  31. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  32. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  33. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  34. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  35. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  36. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  37. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  38. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  39. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  40. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  41. package/dist/types/runtime/app.d.ts +9 -2
  42. package/dist/types/runtime/fetch.d.ts +2 -0
  43. package/dist/types/runtime/input.d.ts +1 -1
  44. package/dist/types/runtime/media.d.ts +6 -10
  45. package/dist/types/runtime/misc.d.ts +4 -1
  46. package/dist/types/runtime/net.d.ts +3 -2
  47. package/dist/types/runtime/touch.d.ts +32 -0
  48. package/dist/types/scene/defineScene.d.ts +43 -2
  49. package/dist/types/scene/editor.d.ts +52 -0
  50. package/dist/types/scene/gizmos.d.ts +7 -4
  51. package/dist/types/ui/NativeView.d.ts +6 -4
  52. package/dist/types/ui/UI.d.ts +1 -1
  53. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  54. package/dist/types/ui/UIButton.d.ts +12 -14
  55. package/dist/types/ui/UIContainer.d.ts +0 -6
  56. package/dist/types/ui/UIImage.d.ts +1 -4
  57. package/dist/types/ui/UIInput.d.ts +8 -24
  58. package/dist/types/ui/UIModal.d.ts +0 -2
  59. package/dist/types/ui/UINode.d.ts +70 -56
  60. package/dist/types/ui/UIPager.d.ts +28 -27
  61. package/dist/types/ui/UIPopover.d.ts +0 -2
  62. package/dist/types/ui/UIScreen.d.ts +12 -17
  63. package/dist/types/ui/UIScrollable.d.ts +1 -4
  64. package/dist/types/ui/UIText.d.ts +0 -2
  65. package/dist/types/ui/UIVideo.d.ts +3 -5
  66. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  67. package/dist/types/ui/UIWidget.d.ts +10 -9
  68. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  69. package/dist/types/ui/presentable.d.ts +46 -32
  70. package/dist/types/ui/router.d.ts +18 -7
  71. package/dist/types/ui/styleColor.d.ts +1 -0
  72. package/dist/types/ui/transitions.d.ts +18 -0
  73. package/dist/types/ui/tree.d.ts +75 -0
  74. package/dist/types/version.d.ts +10 -0
  75. package/dist/types.json +1 -1
  76. package/package.json +12 -3
  77. package/prompts/2d.md +2 -6
  78. package/prompts/3d.md +1 -5
  79. package/prompts/README.md +1 -1
  80. package/prompts/canvas.md +9 -8
  81. package/prompts/compose.ts +1 -1
  82. package/prompts/core.md +3 -3
  83. package/prompts/design.md +1 -1
  84. package/prompts/dist/2d-game.md +473 -239
  85. package/prompts/dist/3d-app.md +553 -205
  86. package/prompts/dist/ar-app.md +435 -202
  87. package/prompts/dist/design.md +113 -95
  88. package/prompts/dist/ui-app.md +386 -170
  89. package/prompts/ui-design.md +2 -3
  90. package/prompts/ui.md +45 -37
  91. package/src/animate/tween/Animation.ts +34 -150
  92. package/src/animate/tween/Timeline.ts +175 -175
  93. package/src/animate/tween/animateValue.ts +6 -3
  94. package/src/animate/tween/easing.ts +10 -3
  95. package/src/animate/tween/spec.ts +41 -15
  96. package/src/audio/Sound.ts +3 -3
  97. package/src/audio/audio.ts +2 -1
  98. package/src/bridges/2d.d.ts +317 -0
  99. package/src/bridges/app.d.ts +91 -0
  100. package/src/bridges/audio.d.ts +97 -0
  101. package/src/bridges/canvas.d.ts +79 -0
  102. package/src/bridges/device.d.ts +72 -0
  103. package/src/bridges/fetch.d.ts +80 -0
  104. package/src/bridges/files.d.ts +70 -0
  105. package/src/bridges/gl.d.ts +1133 -0
  106. package/src/bridges/input.d.ts +72 -0
  107. package/src/bridges/media.d.ts +55 -0
  108. package/src/bridges/nav.d.ts +71 -0
  109. package/src/bridges/net.d.ts +52 -0
  110. package/src/bridges/service.d.ts +52 -0
  111. package/src/bridges/socket.d.ts +31 -0
  112. package/src/bridges/storage.d.ts +33 -0
  113. package/src/bridges/tree.d.ts +301 -0
  114. package/src/bridges/types.d.ts +49 -0
  115. package/src/canvas/Canvas.ts +114 -159
  116. package/src/canvas/gen/cssColor.ts +224 -0
  117. package/src/canvas/gen/recorder.ts +212 -0
  118. package/src/canvas/gen/spec.ts +201 -0
  119. package/src/chisel.ts +193 -0
  120. package/src/compile/assetMacro.ts +1 -1
  121. package/src/compile/bundler.ts +11 -2
  122. package/src/compile/compileProject.ts +43 -4
  123. package/src/compile/fontMacro.ts +3 -4
  124. package/src/compile/header.ts +26 -5
  125. package/src/compile/index.ts +3 -1
  126. package/src/compile/liteMaterial.ts +1 -1
  127. package/src/compile/sceneEditor.ts +11 -26
  128. package/src/core/color.ts +73 -30
  129. package/src/core/pins.ts +51 -0
  130. package/src/core/signals.ts +8 -1
  131. package/src/g2/CharacterController2D.ts +3 -3
  132. package/src/g2/Node2D.ts +57 -39
  133. package/src/g2/Physics2D.ts +2 -2
  134. package/src/g2/Scene2D.ts +35 -23
  135. package/src/g2/Texture2D.ts +1 -1
  136. package/src/g2/loop.ts +4 -4
  137. package/src/gl/CameraPlace.ts +52 -52
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +10 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Mesh.ts +120 -120
  144. package/src/gl/Model.ts +21 -16
  145. package/src/gl/Node.ts +91 -24
  146. package/src/gl/Particles.ts +1 -1
  147. package/src/gl/Scene.ts +100 -44
  148. package/src/gl/Texture.ts +8 -7
  149. package/src/gl/animation/AnimationClip.ts +1 -1
  150. package/src/gl/animation/DynamicBone.ts +482 -482
  151. package/src/gl/animation/Locomotion.ts +8 -3
  152. package/src/gl/nav/NavMesh.ts +3 -4
  153. package/src/gl/physics/Physics.ts +2 -2
  154. package/src/gl/physics/physicsEvents.ts +3 -3
  155. package/src/gl/scenarios.ts +291 -291
  156. package/src/gl/terrain/Terrain.ts +4 -5
  157. package/src/gl/touch.ts +14 -15
  158. package/src/host.d.ts +2 -3
  159. package/src/inject.editor.ts +7 -0
  160. package/src/inject.ts +13 -16
  161. package/src/net/core.ts +6 -5
  162. package/src/net/index.ts +1 -1
  163. package/src/net/replication.ts +1 -1
  164. package/src/plugin.ts +191 -0
  165. package/src/plugins/gen/camera/contract.d.ts +27 -0
  166. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  167. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  168. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  169. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  170. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  171. package/src/plugins/gen/map/contract.d.ts +144 -0
  172. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  173. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  174. package/src/plugins/gen/push/contract.d.ts +61 -0
  175. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  176. package/src/plugins/gen/push/sdk/push.ts +105 -0
  177. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  178. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  179. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  180. package/src/plugins/permission.ts +5 -4
  181. package/src/runtime/app.ts +20 -9
  182. package/src/runtime/appEvents.ts +5 -4
  183. package/src/runtime/channel.ts +18 -15
  184. package/src/runtime/clipboard.ts +4 -3
  185. package/src/runtime/datetime.ts +2 -1
  186. package/src/runtime/device.ts +17 -15
  187. package/src/runtime/fetch.ts +30 -20
  188. package/src/runtime/files.ts +16 -15
  189. package/src/runtime/input.ts +12 -10
  190. package/src/runtime/media.ts +50 -46
  191. package/src/runtime/misc.ts +7 -3
  192. package/src/runtime/net.ts +8 -7
  193. package/src/runtime/rpc.ts +1 -3
  194. package/src/runtime/service.ts +19 -14
  195. package/src/runtime/share.ts +4 -3
  196. package/src/runtime/storage.ts +6 -4
  197. package/src/runtime/touch.ts +32 -0
  198. package/src/scene/defineScene.ts +61 -365
  199. package/src/scene/editor.ts +408 -0
  200. package/src/scene/editorPlugins.ts +3 -3
  201. package/src/scene/gizmos.ts +15 -9
  202. package/src/server/db/marci/query.ts +1 -1
  203. package/src/server/host.ts +1 -1
  204. package/src/server/runtime.ts +1 -1
  205. package/src/ui/NativeView.ts +72 -25
  206. package/src/ui/UI.ts +3 -3
  207. package/src/ui/UIBottomSheet.ts +16 -17
  208. package/src/ui/UIButton.ts +54 -16
  209. package/src/ui/UIContainer.ts +0 -6
  210. package/src/ui/UIImage.ts +34 -30
  211. package/src/ui/UIInput.ts +29 -37
  212. package/src/ui/UIModal.ts +1 -3
  213. package/src/ui/UINode.ts +347 -297
  214. package/src/ui/UIPager.ts +93 -78
  215. package/src/ui/UIPopover.ts +0 -2
  216. package/src/ui/UIScreen.ts +41 -36
  217. package/src/ui/UIScrollable.ts +19 -13
  218. package/src/ui/UISpacer.ts +1 -1
  219. package/src/ui/UITabs.ts +8 -6
  220. package/src/ui/UIText.ts +7 -17
  221. package/src/ui/UIVideo.ts +30 -27
  222. package/src/ui/UIVirtualizedList.ts +58 -59
  223. package/src/ui/UIWidget.ts +38 -18
  224. package/src/ui/colorKeys.gen.ts +37 -0
  225. package/src/ui/fonts.ts +2 -2
  226. package/src/ui/presentable.ts +59 -42
  227. package/src/ui/router.ts +52 -35
  228. package/src/ui/styleColor.ts +56 -0
  229. package/src/ui/theme.ts +7 -6
  230. package/src/ui/transitions.ts +249 -0
  231. package/src/ui/tree.ts +346 -0
  232. package/src/version.ts +24 -0
  233. package/tests/helpers/engineWorld.ts +11 -0
  234. package/tests/helpers/fakeTree.ts +353 -0
  235. package/tests/helpers/hostStubs.ts +31 -0
  236. package/tests/helpers/index.ts +14 -0
  237. package/tests/helpers/memoryMarci.ts +124 -0
  238. package/tests/helpers/phases.ts +23 -0
  239. package/tests/helpers/preload.ts +18 -0
  240. package/tests/helpers/stubApp.ts +2 -0
  241. package/tests/helpers/stubDevice.ts +2 -0
  242. package/tests/helpers/stubFetch.ts +2 -0
  243. package/tests/helpers/stubInput.ts +2 -0
  244. package/dist/inject.js +0 -4629
  245. package/dist/types/core/registry.d.ts +0 -7
  246. package/dist/types/plugins/camera.d.ts +0 -25
  247. package/dist/types/plugins/push.d.ts +0 -46
  248. package/src/bridges.d.ts +0 -1769
  249. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  250. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  251. package/src/compile/__tests__/assetName.test.ts +0 -55
  252. package/src/compile/__tests__/compile.test.ts +0 -310
  253. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  254. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  255. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  256. package/src/core/__tests__/stateMachine.test.ts +0 -132
  257. package/src/core/registry.ts +0 -23
  258. package/src/plugins/camera.ts +0 -81
  259. package/src/plugins/push.ts +0 -132
  260. package/src/plugins/qr.ts +0 -73
package/dist/inject.js DELETED
@@ -1,4629 +0,0 @@
1
- // src/math/Mathf.ts
2
- var clamp = (v, min, max) => v < min ? min : v > max ? max : v;
3
- var smoothDampNumber = (initial, smoothTime, maxSpeed) => {
4
- let current = initial;
5
- let velocity = 0;
6
- const damp = ((target, dt) => {
7
- smoothTime = Math.max(1e-4, smoothTime);
8
- const omega = 2 / smoothTime;
9
- const x = omega * dt;
10
- const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
11
- const maxDelta = maxSpeed * smoothTime;
12
- let delta = clamp(current - target, -maxDelta, maxDelta);
13
- const newTarget = current - delta;
14
- const temp = (velocity + omega * delta) * dt;
15
- velocity = (velocity - omega * temp) * exp;
16
- current = newTarget + (delta + temp) * exp;
17
- return current;
18
- });
19
- damp.setValue = (v) => {
20
- current = v;
21
- velocity = 0;
22
- };
23
- return damp;
24
- };
25
- function smoothDamp(initial, smoothTime, maxSpeed = Infinity) {
26
- return typeof initial === "number" ? smoothDampNumber(initial, smoothTime, maxSpeed) : smoothDampArray(initial, smoothTime, maxSpeed);
27
- }
28
- var smoothDampArray = (initial, smoothTime, maxSpeed) => {
29
- const n = initial.length;
30
- let current = initial.slice();
31
- const velocity = new Array(n).fill(0);
32
- const damp = ((target, dt) => {
33
- smoothTime = Math.max(1e-4, smoothTime);
34
- const omega = 2 / smoothTime;
35
- const x = omega * dt;
36
- const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
37
- const maxDelta = maxSpeed * smoothTime;
38
- const delta = new Array(n);
39
- let mag = 0;
40
- for (let i = 0; i < n; i++) {
41
- delta[i] = current[i] - target[i];
42
- mag += delta[i] * delta[i];
43
- }
44
- mag = Math.sqrt(mag);
45
- if (mag > maxDelta && mag > 0) {
46
- const s = maxDelta / mag;
47
- for (let i = 0; i < n; i++) delta[i] *= s;
48
- }
49
- const out = new Array(n);
50
- for (let i = 0; i < n; i++) {
51
- const newTarget = current[i] - delta[i];
52
- const temp = (velocity[i] + omega * delta[i]) * dt;
53
- velocity[i] = (velocity[i] - omega * temp) * exp;
54
- out[i] = newTarget + (delta[i] + temp) * exp;
55
- }
56
- current = out;
57
- return out.slice();
58
- });
59
- damp.setValue = (v) => {
60
- current = v.slice();
61
- velocity.fill(0);
62
- };
63
- return damp;
64
- };
65
- var Mathf = {
66
- PI: Math.PI,
67
- TAU: Math.PI * 2,
68
- EPSILON: 1e-6,
69
- DEG2RAD: Math.PI / 180,
70
- RAD2DEG: 180 / Math.PI,
71
- deg2rad: (deg) => deg * (Math.PI / 180),
72
- rad2deg: (rad) => rad * (180 / Math.PI),
73
- clamp,
74
- clamp01: (v) => clamp(v, 0, 1),
75
- lerp: (a2, b2, t) => a2 + (b2 - a2) * t,
76
- inverseLerp: (a2, b2, v) => a2 === b2 ? 0 : (v - a2) / (b2 - a2),
77
- remap: (v, inMin, inMax, outMin, outMax) => outMin + (v - inMin) / (inMax - inMin) * (outMax - outMin),
78
- /** Move `a` toward `b` by at most `maxDelta`. */
79
- moveTowards: (a2, b2, maxDelta) => {
80
- const d = b2 - a2;
81
- return Math.abs(d) <= maxDelta ? b2 : a2 + Math.sign(d) * maxDelta;
82
- },
83
- /** Frame-rate-independent exponential approach (good default: lambda 5..15). */
84
- damp: (a2, b2, lambda, dt) => a2 + (b2 - a2) * (1 - Math.exp(-lambda * dt)),
85
- sign: Math.sign,
86
- /** Loop a value into [0, length). */
87
- repeat: (t, length) => clamp(t - Math.floor(t / length) * length, 0, length),
88
- /** Ping-pong a value in [0, length]. */
89
- pingPong: (t, length) => {
90
- const r = clamp(t - Math.floor(t / (length * 2)) * (length * 2), 0, length * 2);
91
- return length - Math.abs(r - length);
92
- },
93
- random: (min = 0, max = 1) => min + Math.random() * (max - min),
94
- randomInt: (min, max) => Math.floor(min + Math.random() * (max - min + 1)),
95
- /**
96
- * Critically-damped follow (Unity SmoothDamp). Returns a stateful damper you call every frame:
97
- * const d = Mathf.smoothDamp([x, y], 0.15)
98
- * pos = d(targetPos, dt)
99
- * Works on a number or a numeric array (vec2 / vec3).
100
- */
101
- smoothDamp
102
- };
103
-
104
- // src/math/vec.ts
105
- var cx = (v) => v.x ?? v[0];
106
- var cy = (v) => v.y ?? v[1];
107
- var cz = (v) => v.z ?? v[2];
108
- var approx = (a2, b2, eps) => Math.abs(a2 - b2) <= eps * Math.max(1, Math.abs(a2), Math.abs(b2));
109
- var Vec2 = class _Vec2 {
110
- x;
111
- y;
112
- constructor(x = 0, y = 0) {
113
- if (typeof x === "number") {
114
- this.x = x;
115
- this.y = y;
116
- } else {
117
- this.x = cx(x);
118
- this.y = cy(x);
119
- }
120
- }
121
- // --- mutation (the only methods that change `this`) ---
122
- /** Mutate the components in place. Returns `this`. */
123
- set(x, y) {
124
- this.x = x;
125
- this.y = y;
126
- return this;
127
- }
128
- /** Copy another vector's components into this one. Returns `this`. */
129
- copy(v) {
130
- this.x = cx(v);
131
- this.y = cy(v);
132
- return this;
133
- }
134
- // --- pure arithmetic (each returns a NEW Vec2) ---
135
- add(v) {
136
- return new _Vec2(this.x + cx(v), this.y + cy(v));
137
- }
138
- sub(v) {
139
- return new _Vec2(this.x - cx(v), this.y - cy(v));
140
- }
141
- mul(v) {
142
- return new _Vec2(this.x * cx(v), this.y * cy(v));
143
- }
144
- div(v) {
145
- return new _Vec2(this.x / cx(v), this.y / cy(v));
146
- }
147
- scale(s) {
148
- return new _Vec2(this.x * s, this.y * s);
149
- }
150
- /** `this + v * s` (fused multiply-add). */
151
- scaleAndAdd(v, s) {
152
- return new _Vec2(this.x + cx(v) * s, this.y + cy(v) * s);
153
- }
154
- negate() {
155
- return new _Vec2(-this.x, -this.y);
156
- }
157
- // --- products / measures ---
158
- dot(v) {
159
- return this.x * cx(v) + this.y * cy(v);
160
- }
161
- /** 2D cross product → the scalar z of the 3D cross (signed parallelogram area). */
162
- cross(v) {
163
- return this.x * cy(v) - this.y * cx(v);
164
- }
165
- length() {
166
- return Math.hypot(this.x, this.y);
167
- }
168
- lengthSq() {
169
- return this.x * this.x + this.y * this.y;
170
- }
171
- distanceTo(v) {
172
- return Math.hypot(this.x - cx(v), this.y - cy(v));
173
- }
174
- distanceSqTo(v) {
175
- const dx = this.x - cx(v), dy = this.y - cy(v);
176
- return dx * dx + dy * dy;
177
- }
178
- normalize() {
179
- const l = Math.hypot(this.x, this.y);
180
- return l === 0 ? new _Vec2(0, 0) : new _Vec2(this.x / l, this.y / l);
181
- }
182
- lerp(v, t) {
183
- return new _Vec2(this.x + (cx(v) - this.x) * t, this.y + (cy(v) - this.y) * t);
184
- }
185
- clamp(min, max) {
186
- return new _Vec2(clampN(this.x, cx(min), cx(max)), clampN(this.y, cy(min), cy(max)));
187
- }
188
- min(v) {
189
- return new _Vec2(Math.min(this.x, cx(v)), Math.min(this.y, cy(v)));
190
- }
191
- max(v) {
192
- return new _Vec2(Math.max(this.x, cx(v)), Math.max(this.y, cy(v)));
193
- }
194
- /** Left-hand perpendicular: (-y, x), a 90° CCW turn. */
195
- perp() {
196
- return new _Vec2(-this.y, this.x);
197
- }
198
- /** Rotate by `rad` (CCW) around `origin` (default 0). */
199
- rotate(rad, origin = ZERO2) {
200
- const ox = cx(origin), oy = cy(origin);
201
- const c2 = Math.cos(rad), s = Math.sin(rad);
202
- const dx = this.x - ox, dy = this.y - oy;
203
- return new _Vec2(ox + dx * c2 - dy * s, oy + dx * s + dy * c2);
204
- }
205
- /** The vector's heading in radians: `atan2(y, x)`. */
206
- heading() {
207
- return Math.atan2(this.y, this.x);
208
- }
209
- /** Angle (radians) between this and `v`. */
210
- angle(v) {
211
- const mag = Math.sqrt(this.lengthSq() * (cx(v) * cx(v) + cy(v) * cy(v)));
212
- return mag === 0 ? 0 : Math.acos(clampN(this.dot(v) / mag, -1, 1));
213
- }
214
- withX(x) {
215
- return new _Vec2(x, this.y);
216
- }
217
- withY(y) {
218
- return new _Vec2(this.x, y);
219
- }
220
- // --- interop ---
221
- equals(v, eps = 1e-6) {
222
- return approx(this.x, cx(v), eps) && approx(this.y, cy(v), eps);
223
- }
224
- clone() {
225
- return new _Vec2(this.x, this.y);
226
- }
227
- toArray() {
228
- return [this.x, this.y];
229
- }
230
- *[Symbol.iterator]() {
231
- yield this.x;
232
- yield this.y;
233
- }
234
- static from(v) {
235
- return new _Vec2(cx(v), cy(v));
236
- }
237
- static get zero() {
238
- return new _Vec2(0, 0);
239
- }
240
- static get one() {
241
- return new _Vec2(1, 1);
242
- }
243
- static get up() {
244
- return new _Vec2(0, 1);
245
- }
246
- static get down() {
247
- return new _Vec2(0, -1);
248
- }
249
- static get left() {
250
- return new _Vec2(-1, 0);
251
- }
252
- static get right() {
253
- return new _Vec2(1, 0);
254
- }
255
- };
256
- var Vec3 = class _Vec3 {
257
- x;
258
- y;
259
- z;
260
- constructor(x = 0, y = 0, z = 0) {
261
- if (typeof x === "number") {
262
- this.x = x;
263
- this.y = y;
264
- this.z = z;
265
- } else {
266
- this.x = cx(x);
267
- this.y = cy(x);
268
- this.z = cz(x);
269
- }
270
- }
271
- // --- mutation ---
272
- set(x, y, z) {
273
- this.x = x;
274
- this.y = y;
275
- this.z = z;
276
- return this;
277
- }
278
- copy(v) {
279
- this.x = cx(v);
280
- this.y = cy(v);
281
- this.z = cz(v);
282
- return this;
283
- }
284
- // --- pure arithmetic ---
285
- add(v) {
286
- return new _Vec3(this.x + cx(v), this.y + cy(v), this.z + cz(v));
287
- }
288
- sub(v) {
289
- return new _Vec3(this.x - cx(v), this.y - cy(v), this.z - cz(v));
290
- }
291
- mul(v) {
292
- return new _Vec3(this.x * cx(v), this.y * cy(v), this.z * cz(v));
293
- }
294
- div(v) {
295
- return new _Vec3(this.x / cx(v), this.y / cy(v), this.z / cz(v));
296
- }
297
- scale(s) {
298
- return new _Vec3(this.x * s, this.y * s, this.z * s);
299
- }
300
- /** `this + v * s` (fused multiply-add). */
301
- scaleAndAdd(v, s) {
302
- return new _Vec3(this.x + cx(v) * s, this.y + cy(v) * s, this.z + cz(v) * s);
303
- }
304
- negate() {
305
- return new _Vec3(-this.x, -this.y, -this.z);
306
- }
307
- // --- products / measures ---
308
- dot(v) {
309
- return this.x * cx(v) + this.y * cy(v) + this.z * cz(v);
310
- }
311
- cross(v) {
312
- const bx = cx(v), by = cy(v), bz = cz(v);
313
- return new _Vec3(this.y * bz - this.z * by, this.z * bx - this.x * bz, this.x * by - this.y * bx);
314
- }
315
- length() {
316
- return Math.hypot(this.x, this.y, this.z);
317
- }
318
- lengthSq() {
319
- return this.x * this.x + this.y * this.y + this.z * this.z;
320
- }
321
- distanceTo(v) {
322
- return Math.hypot(this.x - cx(v), this.y - cy(v), this.z - cz(v));
323
- }
324
- distanceSqTo(v) {
325
- const dx = this.x - cx(v), dy = this.y - cy(v), dz = this.z - cz(v);
326
- return dx * dx + dy * dy + dz * dz;
327
- }
328
- normalize() {
329
- const l = Math.hypot(this.x, this.y, this.z);
330
- return l === 0 ? new _Vec3(0, 0, 0) : new _Vec3(this.x / l, this.y / l, this.z / l);
331
- }
332
- lerp(v, t) {
333
- return new _Vec3(this.x + (cx(v) - this.x) * t, this.y + (cy(v) - this.y) * t, this.z + (cz(v) - this.z) * t);
334
- }
335
- clamp(min, max) {
336
- return new _Vec3(clampN(this.x, cx(min), cx(max)), clampN(this.y, cy(min), cy(max)), clampN(this.z, cz(min), cz(max)));
337
- }
338
- min(v) {
339
- return new _Vec3(Math.min(this.x, cx(v)), Math.min(this.y, cy(v)), Math.min(this.z, cz(v)));
340
- }
341
- max(v) {
342
- return new _Vec3(Math.max(this.x, cx(v)), Math.max(this.y, cy(v)), Math.max(this.z, cz(v)));
343
- }
344
- /** Reflect this vector about a (unit) `normal`: `v - 2·(v·n)·n`. */
345
- reflect(normal) {
346
- const d = 2 * this.dot(normal);
347
- return new _Vec3(this.x - d * cx(normal), this.y - d * cy(normal), this.z - d * cz(normal));
348
- }
349
- /** Vector projection of this onto `onto`. */
350
- project(onto) {
351
- const ox = cx(onto), oy = cy(onto), oz = cz(onto);
352
- const denom = ox * ox + oy * oy + oz * oz;
353
- if (denom === 0) return new _Vec3(0, 0, 0);
354
- const k = this.dot(onto) / denom;
355
- return new _Vec3(ox * k, oy * k, oz * k);
356
- }
357
- /** Angle (radians) between this and `v`. */
358
- angle(v) {
359
- const bx = cx(v), by = cy(v), bz = cz(v);
360
- const mag = Math.sqrt(this.lengthSq() * (bx * bx + by * by + bz * bz));
361
- return mag === 0 ? 0 : Math.acos(clampN(this.dot(v) / mag, -1, 1));
362
- }
363
- // --- rotation ---
364
- /** Rotate this point around the X axis through `origin` (default 0) by `rad`. */
365
- rotateX(rad, origin = ZERO3) {
366
- const ox = cx(origin), oy = cy(origin), oz = cz(origin);
367
- const py = this.y - oy, pz = this.z - oz, c2 = Math.cos(rad), s = Math.sin(rad);
368
- return new _Vec3(this.x, oy + py * c2 - pz * s, oz + py * s + pz * c2);
369
- }
370
- rotateY(rad, origin = ZERO3) {
371
- const ox = cx(origin), oy = cy(origin), oz = cz(origin);
372
- const px = this.x - ox, pz = this.z - oz, c2 = Math.cos(rad), s = Math.sin(rad);
373
- return new _Vec3(ox + pz * s + px * c2, this.y, oz + pz * c2 - px * s);
374
- }
375
- rotateZ(rad, origin = ZERO3) {
376
- const ox = cx(origin), oy = cy(origin), oz = cz(origin);
377
- const px = this.x - ox, py = this.y - oy, c2 = Math.cos(rad), s = Math.sin(rad);
378
- return new _Vec3(ox + px * c2 - py * s, oy + px * s + py * c2, this.z);
379
- }
380
- /** Rotate this vector by a quaternion (x, y, z, w). Accepts a `Quat` or a raw `[x,y,z,w]`. */
381
- rotate(q) {
382
- const qx2 = cx(q), qy2 = cy(q), qz2 = cz(q);
383
- const qw2 = q.w ?? q[3];
384
- const vx = this.x, vy = this.y, vz = this.z;
385
- let tx = qy2 * vz - qz2 * vy, ty = qz2 * vx - qx2 * vz, tz = qx2 * vy - qy2 * vx;
386
- tx += tx;
387
- ty += ty;
388
- tz += tz;
389
- return new _Vec3(
390
- vx + qw2 * tx + qy2 * tz - qz2 * ty,
391
- vy + qw2 * ty + qz2 * tx - qx2 * tz,
392
- vz + qw2 * tz + qx2 * ty - qy2 * tx
393
- );
394
- }
395
- /** Transform this point by a column-major mat4 (a `Mat4`, its `.m`, or a raw length-16 array),
396
- * applying translation and the perspective divide. */
397
- transform(m) {
398
- const a2 = m.m ?? m;
399
- const x = this.x, y = this.y, z = this.z;
400
- let w = a2[3] * x + a2[7] * y + a2[11] * z + a2[15];
401
- w = w || 1;
402
- return new _Vec3(
403
- (a2[0] * x + a2[4] * y + a2[8] * z + a2[12]) / w,
404
- (a2[1] * x + a2[5] * y + a2[9] * z + a2[13]) / w,
405
- (a2[2] * x + a2[6] * y + a2[10] * z + a2[14]) / w
406
- );
407
- }
408
- withX(x) {
409
- return new _Vec3(x, this.y, this.z);
410
- }
411
- withY(y) {
412
- return new _Vec3(this.x, y, this.z);
413
- }
414
- withZ(z) {
415
- return new _Vec3(this.x, this.y, z);
416
- }
417
- // --- interop ---
418
- equals(v, eps = 1e-6) {
419
- return approx(this.x, cx(v), eps) && approx(this.y, cy(v), eps) && approx(this.z, cz(v), eps);
420
- }
421
- clone() {
422
- return new _Vec3(this.x, this.y, this.z);
423
- }
424
- toArray() {
425
- return [this.x, this.y, this.z];
426
- }
427
- *[Symbol.iterator]() {
428
- yield this.x;
429
- yield this.y;
430
- yield this.z;
431
- }
432
- static from(v) {
433
- return new _Vec3(cx(v), cy(v), cz(v));
434
- }
435
- static get zero() {
436
- return new _Vec3(0, 0, 0);
437
- }
438
- static get one() {
439
- return new _Vec3(1, 1, 1);
440
- }
441
- static get up() {
442
- return new _Vec3(0, 1, 0);
443
- }
444
- static get down() {
445
- return new _Vec3(0, -1, 0);
446
- }
447
- static get left() {
448
- return new _Vec3(-1, 0, 0);
449
- }
450
- static get right() {
451
- return new _Vec3(1, 0, 0);
452
- }
453
- /** Forward is −Z (the engine's cameras look down −Z; `node.forward` is −Z at identity). */
454
- static get forward() {
455
- return new _Vec3(0, 0, -1);
456
- }
457
- static get back() {
458
- return new _Vec3(0, 0, 1);
459
- }
460
- };
461
- var clampN = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
462
- var ZERO2 = new Vec2(0, 0);
463
- var ZERO3 = new Vec3(0, 0, 0);
464
-
465
- // src/math/quat.ts
466
- var cw = (q) => q.w ?? q[3];
467
- var DEG2RAD = Math.PI / 180;
468
- var RAD2DEG = 180 / Math.PI;
469
- var EPS = 1e-6;
470
- var approx2 = (a2, b2, eps) => Math.abs(a2 - b2) <= eps * Math.max(1, Math.abs(a2), Math.abs(b2));
471
- var clamp1 = (v) => v < -1 ? -1 : v > 1 ? 1 : v;
472
- var Quat = class _Quat {
473
- x;
474
- y;
475
- z;
476
- w;
477
- constructor(x = 0, y = 0, z = 0, w = 1) {
478
- if (typeof x === "number") {
479
- this.x = x;
480
- this.y = y;
481
- this.z = z;
482
- this.w = w;
483
- } else {
484
- this.x = cx(x);
485
- this.y = cy(x);
486
- this.z = cz(x);
487
- this.w = cw(x);
488
- }
489
- }
490
- // --- mutation ---
491
- set(x, y, z, w) {
492
- this.x = x;
493
- this.y = y;
494
- this.z = z;
495
- this.w = w;
496
- return this;
497
- }
498
- copy(q) {
499
- this.x = cx(q);
500
- this.y = cy(q);
501
- this.z = cz(q);
502
- this.w = cw(q);
503
- return this;
504
- }
505
- // --- pure ops ---
506
- /** Hamilton product `this ⊗ b` (apply `b` first, then `this`). */
507
- mul(b2) {
508
- const ax2 = this.x, ay2 = this.y, az2 = this.z, aw = this.w;
509
- const bx = cx(b2), by = cy(b2), bz = cz(b2), bw = cw(b2);
510
- return new _Quat(
511
- ax2 * bw + aw * bx + ay2 * bz - az2 * by,
512
- ay2 * bw + aw * by + az2 * bx - ax2 * bz,
513
- az2 * bw + aw * bz + ax2 * by - ay2 * bx,
514
- aw * bw - ax2 * bx - ay2 * by - az2 * bz
515
- );
516
- }
517
- normalize() {
518
- let l = Math.hypot(this.x, this.y, this.z, this.w);
519
- if (l === 0) return new _Quat(0, 0, 0, 1);
520
- l = 1 / l;
521
- return new _Quat(this.x * l, this.y * l, this.z * l, this.w * l);
522
- }
523
- conjugate() {
524
- return new _Quat(-this.x, -this.y, -this.z, this.w);
525
- }
526
- /** Inverse rotation. For a unit quaternion this equals `conjugate()`. */
527
- invert() {
528
- const d = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
529
- if (d === 0) return new _Quat(0, 0, 0, 0);
530
- const i = 1 / d;
531
- return new _Quat(-this.x * i, -this.y * i, -this.z * i, this.w * i);
532
- }
533
- dot(b2) {
534
- return this.x * cx(b2) + this.y * cy(b2) + this.z * cz(b2) + this.w * cw(b2);
535
- }
536
- /** Spherical interpolation toward `b` by `t ∈ [0, 1]`. */
537
- slerp(b2, t) {
538
- const ax2 = this.x, ay2 = this.y, az2 = this.z, aw = this.w;
539
- let bx = cx(b2), by = cy(b2), bz = cz(b2), bw = cw(b2);
540
- let cosom = ax2 * bx + ay2 * by + az2 * bz + aw * bw;
541
- if (cosom < 0) {
542
- cosom = -cosom;
543
- bx = -bx;
544
- by = -by;
545
- bz = -bz;
546
- bw = -bw;
547
- }
548
- let s0, s1;
549
- if (1 - cosom > EPS) {
550
- const omega = Math.acos(cosom), sinom = Math.sin(omega);
551
- s0 = Math.sin((1 - t) * omega) / sinom;
552
- s1 = Math.sin(t * omega) / sinom;
553
- } else {
554
- s0 = 1 - t;
555
- s1 = t;
556
- }
557
- return new _Quat(s0 * ax2 + s1 * bx, s0 * ay2 + s1 * by, s0 * az2 + s1 * bz, s0 * aw + s1 * bw);
558
- }
559
- /** Angular distance (radians) between two unit quaternions. */
560
- angle(b2) {
561
- const d = this.dot(b2);
562
- return Math.acos(clamp1(2 * d * d - 1));
563
- }
564
- /** Rotate a vector by this quaternion → a new `Vec3`. */
565
- rotateVec3(v) {
566
- return new Vec3(v).rotate(this);
567
- }
568
- /** Extract euler angles (DEGREES) in the given order. */
569
- toEuler(order = "YXZ") {
570
- const x = this.x, y = this.y, z = this.z, w = this.w;
571
- const x2 = x + x, y2 = y + y, z2 = z + z;
572
- const xx = x * x2, xy = x * y2, xz = x * z2;
573
- const yy = y * y2, yz = y * z2, zz = z * z2;
574
- const wx = w * x2, wy = w * y2, wz = w * z2;
575
- const m = [
576
- 1 - (yy + zz),
577
- xy + wz,
578
- xz - wy,
579
- 0,
580
- xy - wz,
581
- 1 - (xx + zz),
582
- yz + wx,
583
- 0,
584
- xz + wy,
585
- yz - wx,
586
- 1 - (xx + yy),
587
- 0
588
- ];
589
- return eulerFromColMajor(m, order);
590
- }
591
- // --- interop ---
592
- equals(b2, eps = 1e-6) {
593
- return approx2(this.x, cx(b2), eps) && approx2(this.y, cy(b2), eps) && approx2(this.z, cz(b2), eps) && approx2(this.w, cw(b2), eps);
594
- }
595
- /** True if the two unit quaternions represent the same rotation (±q are equal). */
596
- sameRotation(b2, eps = 1e-6) {
597
- return Math.abs(this.dot(b2)) >= 1 - eps;
598
- }
599
- clone() {
600
- return new _Quat(this.x, this.y, this.z, this.w);
601
- }
602
- toArray() {
603
- return [this.x, this.y, this.z, this.w];
604
- }
605
- *[Symbol.iterator]() {
606
- yield this.x;
607
- yield this.y;
608
- yield this.z;
609
- yield this.w;
610
- }
611
- // --- factories ---
612
- static from(q) {
613
- return new _Quat(cx(q), cy(q), cz(q), cw(q));
614
- }
615
- static get identity() {
616
- return new _Quat(0, 0, 0, 1);
617
- }
618
- /** From euler angles in DEGREES (default order "YXZ"). Inverts `toEuler`. */
619
- static fromEuler(x, y, z, order = "YXZ") {
620
- const c1 = Math.cos(x * DEG2RAD / 2), c2 = Math.cos(y * DEG2RAD / 2), c3 = Math.cos(z * DEG2RAD / 2);
621
- const s1 = Math.sin(x * DEG2RAD / 2), s2 = Math.sin(y * DEG2RAD / 2), s3 = Math.sin(z * DEG2RAD / 2);
622
- switch (order) {
623
- case "XYZ":
624
- return new _Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3);
625
- case "YXZ":
626
- return new _Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3);
627
- case "ZXY":
628
- return new _Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3);
629
- case "ZYX":
630
- return new _Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3);
631
- case "YZX":
632
- return new _Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3);
633
- case "XZY":
634
- return new _Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3);
635
- }
636
- }
637
- /** From a rotation `axis` and an angle in RADIANS. The axis is normalized for you. */
638
- static fromAxisAngle(axis, rad) {
639
- let ax2 = cx(axis), ay2 = cy(axis), az2 = cz(axis);
640
- const l = Math.hypot(ax2, ay2, az2);
641
- if (l === 0) return new _Quat(0, 0, 0, 1);
642
- ax2 /= l;
643
- ay2 /= l;
644
- az2 /= l;
645
- const s = Math.sin(rad / 2);
646
- return new _Quat(ax2 * s, ay2 * s, az2 * s, Math.cos(rad / 2));
647
- }
648
- /** The shortest rotation that takes direction `a` onto direction `b`. */
649
- static fromTo(a2, b2) {
650
- const A = new Vec3(a2).normalize(), B = new Vec3(b2).normalize();
651
- const d = A.dot(B);
652
- if (d > 0.999999) return new _Quat(0, 0, 0, 1);
653
- if (d < -0.999999) {
654
- let axis = new Vec3(1, 0, 0).cross(A);
655
- if (axis.lengthSq() < 1e-12) axis = new Vec3(0, 1, 0).cross(A);
656
- return _Quat.fromAxisAngle(axis.normalize(), Math.PI);
657
- }
658
- const c2 = A.cross(B);
659
- return new _Quat(c2.x, c2.y, c2.z, 1 + d).normalize();
660
- }
661
- /** A rotation whose forward (−Z) points along `forward`, with `up` as the reference up axis. */
662
- static lookRotation(forward, up = Vec3.up) {
663
- const zc = new Vec3(forward).normalize().negate();
664
- let xc = new Vec3(up).cross(zc);
665
- if (xc.lengthSq() < 1e-12) xc = new Vec3(up).cross(zc.add([1e-4, 0, 0]));
666
- xc = xc.normalize();
667
- const yc = zc.cross(xc);
668
- return quatFromColMajor([xc.x, xc.y, xc.z, 0, yc.x, yc.y, yc.z, 0, zc.x, zc.y, zc.z]);
669
- }
670
- };
671
- var quatFromColMajor = (m) => {
672
- const m00 = m[0], m01 = m[1], m02 = m[2];
673
- const m10 = m[4], m11 = m[5], m12 = m[6];
674
- const m20 = m[8], m21 = m[9], m22 = m[10];
675
- const trace = m00 + m11 + m22;
676
- let S;
677
- if (trace > 0) {
678
- S = Math.sqrt(trace + 1) * 2;
679
- return new Quat((m12 - m21) / S, (m20 - m02) / S, (m01 - m10) / S, 0.25 * S);
680
- } else if (m00 > m11 && m00 > m22) {
681
- S = Math.sqrt(1 + m00 - m11 - m22) * 2;
682
- return new Quat(0.25 * S, (m01 + m10) / S, (m20 + m02) / S, (m12 - m21) / S);
683
- } else if (m11 > m22) {
684
- S = Math.sqrt(1 + m11 - m00 - m22) * 2;
685
- return new Quat((m01 + m10) / S, 0.25 * S, (m12 + m21) / S, (m20 - m02) / S);
686
- } else {
687
- S = Math.sqrt(1 + m22 - m00 - m11) * 2;
688
- return new Quat((m20 + m02) / S, (m12 + m21) / S, 0.25 * S, (m01 - m10) / S);
689
- }
690
- };
691
- var eulerFromColMajor = (m, order = "YXZ") => {
692
- const m11 = m[0], m21 = m[1], m31 = m[2];
693
- const m12 = m[4], m22 = m[5], m32 = m[6];
694
- const m13 = m[8], m23 = m[9], m33 = m[10];
695
- let x = 0, y = 0, z = 0;
696
- const LIM = 0.9999999;
697
- switch (order) {
698
- case "XYZ":
699
- y = Math.asin(clamp1(m13));
700
- if (Math.abs(m13) < LIM) {
701
- x = Math.atan2(-m23, m33);
702
- z = Math.atan2(-m12, m11);
703
- } else {
704
- x = Math.atan2(m32, m22);
705
- z = 0;
706
- }
707
- break;
708
- case "YXZ":
709
- x = Math.asin(-clamp1(m23));
710
- if (Math.abs(m23) < LIM) {
711
- y = Math.atan2(m13, m33);
712
- z = Math.atan2(m21, m22);
713
- } else {
714
- y = Math.atan2(-m31, m11);
715
- z = 0;
716
- }
717
- break;
718
- case "ZXY":
719
- x = Math.asin(clamp1(m32));
720
- if (Math.abs(m32) < LIM) {
721
- y = Math.atan2(-m31, m33);
722
- z = Math.atan2(-m12, m22);
723
- } else {
724
- y = 0;
725
- z = Math.atan2(m21, m11);
726
- }
727
- break;
728
- case "ZYX":
729
- y = Math.asin(-clamp1(m31));
730
- if (Math.abs(m31) < LIM) {
731
- x = Math.atan2(m32, m33);
732
- z = Math.atan2(m21, m11);
733
- } else {
734
- x = 0;
735
- z = Math.atan2(-m12, m22);
736
- }
737
- break;
738
- case "YZX":
739
- z = Math.asin(clamp1(m21));
740
- if (Math.abs(m21) < LIM) {
741
- x = Math.atan2(-m23, m22);
742
- y = Math.atan2(-m31, m11);
743
- } else {
744
- x = 0;
745
- y = Math.atan2(m13, m33);
746
- }
747
- break;
748
- case "XZY":
749
- z = Math.asin(-clamp1(m12));
750
- if (Math.abs(m12) < LIM) {
751
- x = Math.atan2(m32, m22);
752
- y = Math.atan2(m13, m11);
753
- } else {
754
- x = Math.atan2(-m23, m33);
755
- y = 0;
756
- }
757
- break;
758
- }
759
- return new Vec3(x * RAD2DEG, y * RAD2DEG, z * RAD2DEG);
760
- };
761
-
762
- // src/math/mat4.ts
763
- var arr = (m) => m.m ?? m;
764
- var IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
765
- var Mat4 = class _Mat4 {
766
- /** The raw column-major elements (length 16). Public on purpose — read/write directly when you want
767
- * the metal: `mat.m[12] += vx * dt`. The fluent methods never mutate it (they return a new Mat4). */
768
- m;
769
- constructor(src) {
770
- if (src === void 0) {
771
- this.m = IDENTITY.slice();
772
- return;
773
- }
774
- const s = arr(src);
775
- this.m = [s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]];
776
- }
777
- // --- mutation ---
778
- set(values) {
779
- for (let i = 0; i < 16; i++) this.m[i] = values[i];
780
- return this;
781
- }
782
- copy(src) {
783
- return this.set(arr(src));
784
- }
785
- // --- products (pure) ---
786
- /** `this · b` (apply `b` first in local space, then `this`). */
787
- mul(b2) {
788
- const a2 = this.m, o = arr(b2), out = new Array(16);
789
- const a00 = a2[0], a01 = a2[1], a02 = a2[2], a03 = a2[3];
790
- const a10 = a2[4], a11 = a2[5], a12 = a2[6], a13 = a2[7];
791
- const a20 = a2[8], a21 = a2[9], a22 = a2[10], a23 = a2[11];
792
- const a30 = a2[12], a31 = a2[13], a32 = a2[14], a33 = a2[15];
793
- for (let i = 0; i < 4; i++) {
794
- const b0 = o[i * 4], b1 = o[i * 4 + 1], b22 = o[i * 4 + 2], b3 = o[i * 4 + 3];
795
- out[i * 4] = b0 * a00 + b1 * a10 + b22 * a20 + b3 * a30;
796
- out[i * 4 + 1] = b0 * a01 + b1 * a11 + b22 * a21 + b3 * a31;
797
- out[i * 4 + 2] = b0 * a02 + b1 * a12 + b22 * a22 + b3 * a32;
798
- out[i * 4 + 3] = b0 * a03 + b1 * a13 + b22 * a23 + b3 * a33;
799
- }
800
- return fromArray(out);
801
- }
802
- /** `b · this` — multiply on the other side (apply `this` first, then `b`). */
803
- premul(b2) {
804
- return new _Mat4(b2).mul(this);
805
- }
806
- invert() {
807
- const a2 = this.m;
808
- const a00 = a2[0], a01 = a2[1], a02 = a2[2], a03 = a2[3];
809
- const a10 = a2[4], a11 = a2[5], a12 = a2[6], a13 = a2[7];
810
- const a20 = a2[8], a21 = a2[9], a22 = a2[10], a23 = a2[11];
811
- const a30 = a2[12], a31 = a2[13], a32 = a2[14], a33 = a2[15];
812
- const b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10;
813
- const b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12;
814
- const b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30;
815
- const b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32;
816
- let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
817
- if (!det) return new _Mat4();
818
- det = 1 / det;
819
- return fromArray([
820
- (a11 * b11 - a12 * b10 + a13 * b09) * det,
821
- (a02 * b10 - a01 * b11 - a03 * b09) * det,
822
- (a31 * b05 - a32 * b04 + a33 * b03) * det,
823
- (a22 * b04 - a21 * b05 - a23 * b03) * det,
824
- (a12 * b08 - a10 * b11 - a13 * b07) * det,
825
- (a00 * b11 - a02 * b08 + a03 * b07) * det,
826
- (a32 * b02 - a30 * b05 - a33 * b01) * det,
827
- (a20 * b05 - a22 * b02 + a23 * b01) * det,
828
- (a10 * b10 - a11 * b08 + a13 * b06) * det,
829
- (a01 * b08 - a00 * b10 - a03 * b06) * det,
830
- (a30 * b04 - a31 * b02 + a33 * b00) * det,
831
- (a21 * b02 - a20 * b04 - a23 * b00) * det,
832
- (a11 * b07 - a10 * b09 - a12 * b06) * det,
833
- (a00 * b09 - a01 * b07 + a02 * b06) * det,
834
- (a31 * b01 - a30 * b03 - a32 * b00) * det,
835
- (a20 * b03 - a21 * b01 + a22 * b00) * det
836
- ]);
837
- }
838
- transpose() {
839
- const a2 = this.m;
840
- return fromArray([a2[0], a2[4], a2[8], a2[12], a2[1], a2[5], a2[9], a2[13], a2[2], a2[6], a2[10], a2[14], a2[3], a2[7], a2[11], a2[15]]);
841
- }
842
- determinant() {
843
- const a2 = this.m;
844
- const a00 = a2[0], a01 = a2[1], a02 = a2[2], a03 = a2[3];
845
- const a10 = a2[4], a11 = a2[5], a12 = a2[6], a13 = a2[7];
846
- const a20 = a2[8], a21 = a2[9], a22 = a2[10], a23 = a2[11];
847
- const a30 = a2[12], a31 = a2[13], a32 = a2[14], a33 = a2[15];
848
- const b0 = a00 * a11 - a01 * a10, b1 = a00 * a12 - a02 * a10, b2 = a01 * a12 - a02 * a11;
849
- const b3 = a20 * a31 - a21 * a30, b4 = a20 * a32 - a22 * a30, b5 = a21 * a32 - a22 * a31;
850
- const b6 = a00 * b5 - a01 * b4 + a02 * b3, b7 = a10 * b5 - a11 * b4 + a12 * b3;
851
- const b8 = a20 * b2 - a21 * b1 + a22 * b0, b9 = a30 * b2 - a31 * b1 + a32 * b0;
852
- return a13 * b6 - a03 * b7 + a33 * b8 - a23 * b9;
853
- }
854
- // --- local-space transforms (post-multiply: this · T) ---
855
- /** `this · translate(v)`. */
856
- translate(v) {
857
- const a2 = this.m, x = ax(v), y = ay(v), z = az(v), out = a2.slice();
858
- out[12] = a2[0] * x + a2[4] * y + a2[8] * z + a2[12];
859
- out[13] = a2[1] * x + a2[5] * y + a2[9] * z + a2[13];
860
- out[14] = a2[2] * x + a2[6] * y + a2[10] * z + a2[14];
861
- out[15] = a2[3] * x + a2[7] * y + a2[11] * z + a2[15];
862
- return fromArray(out);
863
- }
864
- /** `this · scale(v)`. Accepts a vector or a uniform scalar. */
865
- scale(v) {
866
- const a2 = this.m;
867
- const x = typeof v === "number" ? v : ax(v), y = typeof v === "number" ? v : ay(v), z = typeof v === "number" ? v : az(v);
868
- return fromArray([
869
- a2[0] * x,
870
- a2[1] * x,
871
- a2[2] * x,
872
- a2[3] * x,
873
- a2[4] * y,
874
- a2[5] * y,
875
- a2[6] * y,
876
- a2[7] * y,
877
- a2[8] * z,
878
- a2[9] * z,
879
- a2[10] * z,
880
- a2[11] * z,
881
- a2[12],
882
- a2[13],
883
- a2[14],
884
- a2[15]
885
- ]);
886
- }
887
- /** `this · rotate(axis, rad)`. */
888
- rotate(rad, axis) {
889
- let x = ax(axis), y = ay(axis), z = az(axis);
890
- let len = Math.hypot(x, y, z);
891
- if (len < 1e-6) return this.clone();
892
- len = 1 / len;
893
- x *= len;
894
- y *= len;
895
- z *= len;
896
- const s = Math.sin(rad), c2 = Math.cos(rad), t = 1 - c2;
897
- const a2 = this.m;
898
- const a00 = a2[0], a01 = a2[1], a02 = a2[2], a03 = a2[3];
899
- const a10 = a2[4], a11 = a2[5], a12 = a2[6], a13 = a2[7];
900
- const a20 = a2[8], a21 = a2[9], a22 = a2[10], a23 = a2[11];
901
- const b00 = x * x * t + c2, b01 = y * x * t + z * s, b02 = z * x * t - y * s;
902
- const b10 = x * y * t - z * s, b11 = y * y * t + c2, b12 = z * y * t + x * s;
903
- const b20 = x * z * t + y * s, b21 = y * z * t - x * s, b22 = z * z * t + c2;
904
- return fromArray([
905
- a00 * b00 + a10 * b01 + a20 * b02,
906
- a01 * b00 + a11 * b01 + a21 * b02,
907
- a02 * b00 + a12 * b01 + a22 * b02,
908
- a03 * b00 + a13 * b01 + a23 * b02,
909
- a00 * b10 + a10 * b11 + a20 * b12,
910
- a01 * b10 + a11 * b11 + a21 * b12,
911
- a02 * b10 + a12 * b11 + a22 * b12,
912
- a03 * b10 + a13 * b11 + a23 * b12,
913
- a00 * b20 + a10 * b21 + a20 * b22,
914
- a01 * b20 + a11 * b21 + a21 * b22,
915
- a02 * b20 + a12 * b21 + a22 * b22,
916
- a03 * b20 + a13 * b21 + a23 * b22,
917
- a2[12],
918
- a2[13],
919
- a2[14],
920
- a2[15]
921
- ]);
922
- }
923
- rotateX(rad) {
924
- return this.rotate(rad, AXIS_X);
925
- }
926
- rotateY(rad) {
927
- return this.rotate(rad, AXIS_Y);
928
- }
929
- rotateZ(rad) {
930
- return this.rotate(rad, AXIS_Z);
931
- }
932
- // --- vector transforms ---
933
- /** Transform a point (applies translation + perspective divide). */
934
- transformPoint(v) {
935
- return new Vec3(v).transform(this);
936
- }
937
- /** Transform a direction (rotation/scale only — no translation, no divide). */
938
- transformDirection(v) {
939
- const a2 = this.m, x = ax(v), y = ay(v), z = az(v);
940
- return new Vec3(a2[0] * x + a2[4] * y + a2[8] * z, a2[1] * x + a2[5] * y + a2[9] * z, a2[2] * x + a2[6] * y + a2[10] * z);
941
- }
942
- // --- decomposition (folded from transform.ts) ---
943
- get position() {
944
- const a2 = this.m;
945
- return new Vec3(a2[12], a2[13], a2[14]);
946
- }
947
- get scaling() {
948
- const a2 = this.m;
949
- return new Vec3(Math.hypot(a2[0], a2[1], a2[2]), Math.hypot(a2[4], a2[5], a2[6]), Math.hypot(a2[8], a2[9], a2[10]));
950
- }
951
- get rotation() {
952
- return quatFromColMajor(rotationOnly(this.m));
953
- }
954
- get eulerAngles() {
955
- return eulerFromColMajor(rotationOnly(this.m), "YXZ");
956
- }
957
- toEuler(order = "YXZ") {
958
- return eulerFromColMajor(rotationOnly(this.m), order);
959
- }
960
- /** `{ position, rotation, scale }` in one shot. */
961
- decompose() {
962
- return { position: this.position, rotation: this.rotation, scale: this.scaling };
963
- }
964
- /** Column basis vectors (the local X / Y / Z axes in world space). */
965
- get basisX() {
966
- const a2 = this.m;
967
- return new Vec3(a2[0], a2[1], a2[2]);
968
- }
969
- get basisY() {
970
- const a2 = this.m;
971
- return new Vec3(a2[4], a2[5], a2[6]);
972
- }
973
- get basisZ() {
974
- const a2 = this.m;
975
- return new Vec3(a2[8], a2[9], a2[10]);
976
- }
977
- // --- interop ---
978
- clone() {
979
- return new _Mat4(this);
980
- }
981
- toArray() {
982
- return this.m.slice();
983
- }
984
- toFloat32Array() {
985
- return Float32Array.from(this.m);
986
- }
987
- equals(b2, eps = 1e-6) {
988
- const o = arr(b2);
989
- for (let i = 0; i < 16; i++) {
990
- if (Math.abs(this.m[i] - o[i]) > eps * Math.max(1, Math.abs(this.m[i]), Math.abs(o[i]))) return false;
991
- }
992
- return true;
993
- }
994
- // --- factories ---
995
- static identity() {
996
- return new _Mat4();
997
- }
998
- static from(src) {
999
- return new _Mat4(src);
1000
- }
1001
- /** Translation · Rotation · Scale into one matrix. `scale` defaults to 1 and accepts a scalar. */
1002
- static compose(position, rotation, scale = 1) {
1003
- const x = qx(rotation), y = qy(rotation), z = qz(rotation), w = qw(rotation);
1004
- const x2 = x + x, y2 = y + y, z2 = z + z;
1005
- const xx = x * x2, xy = x * y2, xz = x * z2;
1006
- const yy = y * y2, yz = y * z2, zz = z * z2;
1007
- const wx = w * x2, wy = w * y2, wz = w * z2;
1008
- const sx = typeof scale === "number" ? scale : ax(scale);
1009
- const sy = typeof scale === "number" ? scale : ay(scale);
1010
- const sz = typeof scale === "number" ? scale : az(scale);
1011
- return fromArray([
1012
- (1 - (yy + zz)) * sx,
1013
- (xy + wz) * sx,
1014
- (xz - wy) * sx,
1015
- 0,
1016
- (xy - wz) * sy,
1017
- (1 - (xx + zz)) * sy,
1018
- (yz + wx) * sy,
1019
- 0,
1020
- (xz + wy) * sz,
1021
- (yz - wx) * sz,
1022
- (1 - (xx + yy)) * sz,
1023
- 0,
1024
- ax(position),
1025
- ay(position),
1026
- az(position),
1027
- 1
1028
- ]);
1029
- }
1030
- static fromQuat(q) {
1031
- return _Mat4.compose(ORIGIN, q, 1);
1032
- }
1033
- static fromTranslation(v) {
1034
- const m = new _Mat4();
1035
- m.m[12] = ax(v);
1036
- m.m[13] = ay(v);
1037
- m.m[14] = az(v);
1038
- return m;
1039
- }
1040
- static fromScale(v) {
1041
- return new _Mat4().scale(v);
1042
- }
1043
- static fromEuler(x, y, z, order = "YXZ") {
1044
- return _Mat4.fromQuat(Quat.fromEuler(x, y, z, order));
1045
- }
1046
- /** A view matrix that looks from `eye` toward `center` with `up`. */
1047
- static lookAt(eye, center, up = Vec3.up) {
1048
- const ex = ax(eye), ey = ay(eye), ez = az(eye);
1049
- let z0 = ex - ax(center), z1 = ey - ay(center), z2 = ez - az(center);
1050
- let len = Math.hypot(z0, z1, z2);
1051
- if (len < 1e-6) return new _Mat4();
1052
- len = 1 / len;
1053
- z0 *= len;
1054
- z1 *= len;
1055
- z2 *= len;
1056
- const ux = ax(up), uy = ay(up), uz = az(up);
1057
- let x0 = uy * z2 - uz * z1, x1 = uz * z0 - ux * z2, x2 = ux * z1 - uy * z0;
1058
- len = Math.hypot(x0, x1, x2);
1059
- if (len) {
1060
- len = 1 / len;
1061
- x0 *= len;
1062
- x1 *= len;
1063
- x2 *= len;
1064
- } else {
1065
- x0 = x1 = x2 = 0;
1066
- }
1067
- const y0 = z1 * x2 - z2 * x1, y1 = z2 * x0 - z0 * x2, y2 = z0 * x1 - z1 * x0;
1068
- return fromArray([
1069
- x0,
1070
- y0,
1071
- z0,
1072
- 0,
1073
- x1,
1074
- y1,
1075
- z1,
1076
- 0,
1077
- x2,
1078
- y2,
1079
- z2,
1080
- 0,
1081
- -(x0 * ex + x1 * ey + x2 * ez),
1082
- -(y0 * ex + y1 * ey + y2 * ez),
1083
- -(z0 * ex + z1 * ey + z2 * ez),
1084
- 1
1085
- ]);
1086
- }
1087
- /** A world matrix that places something at `eye` oriented to look at `target`. */
1088
- static targetTo(eye, target, up = Vec3.up) {
1089
- const ex = ax(eye), ey = ay(eye), ez = az(eye);
1090
- let z0 = ex - ax(target), z1 = ey - ay(target), z2 = ez - az(target);
1091
- let len = z0 * z0 + z1 * z1 + z2 * z2;
1092
- if (len > 0) {
1093
- len = 1 / Math.sqrt(len);
1094
- z0 *= len;
1095
- z1 *= len;
1096
- z2 *= len;
1097
- }
1098
- const ux = ax(up), uy = ay(up), uz = az(up);
1099
- let x0 = uy * z2 - uz * z1, x1 = uz * z0 - ux * z2, x2 = ux * z1 - uy * z0;
1100
- len = x0 * x0 + x1 * x1 + x2 * x2;
1101
- if (len > 0) {
1102
- len = 1 / Math.sqrt(len);
1103
- x0 *= len;
1104
- x1 *= len;
1105
- x2 *= len;
1106
- }
1107
- return fromArray([
1108
- x0,
1109
- x1,
1110
- x2,
1111
- 0,
1112
- z1 * x2 - z2 * x1,
1113
- z2 * x0 - z0 * x2,
1114
- z0 * x1 - z1 * x0,
1115
- 0,
1116
- z0,
1117
- z1,
1118
- z2,
1119
- 0,
1120
- ex,
1121
- ey,
1122
- ez,
1123
- 1
1124
- ]);
1125
- }
1126
- /** Perspective projection (WebGL/OpenGL clip, NDC z ∈ [−1, 1]). `fovy` is in RADIANS. */
1127
- static perspective(fovy, aspect, near, far) {
1128
- const f = 1 / Math.tan(fovy / 2), out = new Array(16).fill(0);
1129
- out[0] = f / aspect;
1130
- out[5] = f;
1131
- out[11] = -1;
1132
- if (far !== Infinity) {
1133
- const nf = 1 / (near - far);
1134
- out[10] = (far + near) * nf;
1135
- out[14] = 2 * far * near * nf;
1136
- } else {
1137
- out[10] = -1;
1138
- out[14] = -2 * near;
1139
- }
1140
- return fromArray(out);
1141
- }
1142
- /** Orthographic projection (WebGL/OpenGL clip, NDC z ∈ [−1, 1]). */
1143
- static ortho(left, right, bottom, top, near, far) {
1144
- const lr = 1 / (left - right), bt = 1 / (bottom - top), nf = 1 / (near - far);
1145
- return fromArray([
1146
- -2 * lr,
1147
- 0,
1148
- 0,
1149
- 0,
1150
- 0,
1151
- -2 * bt,
1152
- 0,
1153
- 0,
1154
- 0,
1155
- 0,
1156
- 2 * nf,
1157
- 0,
1158
- (left + right) * lr,
1159
- (top + bottom) * bt,
1160
- (far + near) * nf,
1161
- 1
1162
- ]);
1163
- }
1164
- };
1165
- var fromArray = (m) => {
1166
- const r = Object.create(Mat4.prototype);
1167
- r.m = m;
1168
- return r;
1169
- };
1170
- var rotationOnly = (a2) => {
1171
- const sx = Math.hypot(a2[0], a2[1], a2[2]) || 1, sy = Math.hypot(a2[4], a2[5], a2[6]) || 1, sz = Math.hypot(a2[8], a2[9], a2[10]) || 1;
1172
- const i1 = 1 / sx, i2 = 1 / sy, i3 = 1 / sz;
1173
- return [a2[0] * i1, a2[1] * i1, a2[2] * i1, 0, a2[4] * i2, a2[5] * i2, a2[6] * i2, 0, a2[8] * i3, a2[9] * i3, a2[10] * i3];
1174
- };
1175
- var ax = (v) => v.x ?? v[0];
1176
- var ay = (v) => v.y ?? v[1];
1177
- var az = (v) => v.z ?? v[2];
1178
- var qx = (q) => q.x ?? q[0];
1179
- var qy = (q) => q.y ?? q[1];
1180
- var qz = (q) => q.z ?? q[2];
1181
- var qw = (q) => q.w ?? q[3];
1182
- var AXIS_X = [1, 0, 0];
1183
- var AXIS_Y = [0, 1, 0];
1184
- var AXIS_Z = [0, 0, 1];
1185
- var ORIGIN = [0, 0, 0];
1186
-
1187
- // src/core/color.ts
1188
- var parseHexString = (input) => {
1189
- let s = input.trim();
1190
- if (s[0] === "#") s = s.slice(1);
1191
- if (s.length === 3 || s.length === 4) s = s.split("").map((c2) => c2 + c2).join("");
1192
- const int = parseInt(s, 16);
1193
- if (s.length === 8) return [int >>> 24 & 255, int >> 16 & 255, int >> 8 & 255, int & 255];
1194
- if (s.length === 6) return [int >> 16 & 255, int >> 8 & 255, int & 255, 255];
1195
- return [0, 0, 0, 255];
1196
- };
1197
- var toFloats = (c2) => {
1198
- if (typeof c2 === "number") return [(c2 >> 16 & 255) / 255, (c2 >> 8 & 255) / 255, (c2 & 255) / 255, 1];
1199
- if (typeof c2 === "string") {
1200
- const b2 = parseHexString(c2);
1201
- return [b2[0] / 255, b2[1] / 255, b2[2] / 255, b2[3] / 255];
1202
- }
1203
- return [c2[0], c2[1], c2[2], c2[3] ?? 1];
1204
- };
1205
- var toBytes = (c2) => {
1206
- const f = toFloats(c2);
1207
- return [Math.round(f[0] * 255), Math.round(f[1] * 255), Math.round(f[2] * 255), Math.round(f[3] * 255)];
1208
- };
1209
- var Color = {
1210
- /** [r,g,b] in 0..1 — for the 2D engine's float color setters. */
1211
- toRgb01(c2) {
1212
- const f = toFloats(c2);
1213
- return [f[0], f[1], f[2]];
1214
- },
1215
- /** [r,g,b,a] in 0..1. */
1216
- toRgba01(c2) {
1217
- return toFloats(c2);
1218
- },
1219
- /** 0xRRGGBB packed int — for the 3D engine's color uniforms / skybox. */
1220
- toPackedRgb(c2) {
1221
- const b2 = toBytes(c2);
1222
- return b2[0] << 16 | b2[1] << 8 | b2[2];
1223
- },
1224
- /** 0xRRGGBBAA packed int (unsigned). */
1225
- toPackedRgba(c2) {
1226
- const b2 = toBytes(c2);
1227
- return (b2[0] << 24 | b2[1] << 16 | b2[2] << 8 | b2[3]) >>> 0;
1228
- },
1229
- /** '#rrggbb' (or '#rrggbbaa' when alpha < 1) — for the 3D material color uniforms. */
1230
- toHexString(c2) {
1231
- const b2 = toBytes(c2);
1232
- const hex = (n) => n.toString(16).padStart(2, "0");
1233
- const base = `#${hex(b2[0])}${hex(b2[1])}${hex(b2[2])}`;
1234
- return b2[3] >= 255 ? base : base + hex(b2[3]);
1235
- }
1236
- };
1237
-
1238
- // src/runtime/misc.ts
1239
- var toast = (msg) => _creatorUI.toast(msg);
1240
- var SvgSource = (svg) => ({ svg, tintColor: null });
1241
-
1242
- // src/runtime/fetch.ts
1243
- var FetchResponseImpl = class {
1244
- status;
1245
- _id;
1246
- constructor(id, status) {
1247
- this._id = id;
1248
- this.status = status;
1249
- }
1250
- json() {
1251
- return _creatorUtils.fetchToJson(this._id);
1252
- }
1253
- text() {
1254
- return _creatorUtils.fetchToText(this._id);
1255
- }
1256
- dispose() {
1257
- _creatorUtils.disposeFetch(this._id);
1258
- }
1259
- };
1260
- var fetch = (url, options = {}) => new Promise((resolve, reject) => {
1261
- _creatorUtils.fetch(url, options, (systemId, statusCode) => {
1262
- resolve(new FetchResponseImpl(systemId, statusCode));
1263
- }, reject);
1264
- });
1265
- var fetchLocal = (path) => new FetchResponseImpl(_creatorUtils.fetchLocal(path), 200);
1266
- var File = class {
1267
- id;
1268
- name;
1269
- size;
1270
- constructor(id, name, size) {
1271
- this.id = id;
1272
- this.name = name;
1273
- this.size = size;
1274
- }
1275
- };
1276
- var FormData = class {
1277
- _entries = [];
1278
- append(name, value, filename) {
1279
- this._entries.push([name, value, filename ?? (typeof value === "object" ? value.name : void 0)]);
1280
- }
1281
- delete(name) {
1282
- const index = this._entries.findIndex((i) => i[0] === name);
1283
- if (index >= 0) this._entries.splice(index, 1);
1284
- }
1285
- };
1286
-
1287
- // src/runtime/storage.ts
1288
- var localStorage = {
1289
- getItem(key) {
1290
- return _creatorUtils.localStorageGetValue(key);
1291
- },
1292
- setItem(key, value) {
1293
- _creatorUtils.localStorageSetValue(key, value);
1294
- },
1295
- removeItem(key) {
1296
- _creatorUtils.localStorageRemoveValue(key);
1297
- }
1298
- };
1299
-
1300
- // src/runtime/device.ts
1301
- var resizeListeners = [];
1302
- var resizeRegistered = false;
1303
- var onResize = (width, height) => {
1304
- for (const cb2 of resizeListeners.slice()) cb2(width, height);
1305
- };
1306
- var device = {
1307
- get platform() {
1308
- return _creatorUtils.platform;
1309
- },
1310
- get language() {
1311
- return _creatorUtils.language;
1312
- },
1313
- addEventListener(channel, callback) {
1314
- if (channel !== "resize") return;
1315
- resizeListeners.push(callback);
1316
- if (!resizeRegistered) {
1317
- resizeRegistered = true;
1318
- _creatorUI.registerResizeEvent(onResize);
1319
- }
1320
- },
1321
- removeEventListener(channel, callback) {
1322
- if (channel !== "resize") return;
1323
- const i = resizeListeners.indexOf(callback);
1324
- if (i >= 0) resizeListeners.splice(i, 1);
1325
- }
1326
- };
1327
-
1328
- // src/runtime/input.ts
1329
- var Input = {
1330
- /**
1331
- * Is a key currently held? `code` is a KeyboardEvent.code-style string, e.g. 'ArrowRight',
1332
- * 'KeyW', 'Space'. Poll this inside setLoop for frame-independent movement.
1333
- */
1334
- key(code) {
1335
- return _creatorUtils.inputKey(code);
1336
- }
1337
- };
1338
-
1339
- // src/core/events.ts
1340
- var Emitter = class {
1341
- _listeners = {};
1342
- addEventListener(channel, callback) {
1343
- const list = this._listeners[channel];
1344
- if (list) list.push(callback);
1345
- else this._listeners[channel] = [callback];
1346
- }
1347
- removeEventListener(channel, callback) {
1348
- const list = this._listeners[channel];
1349
- if (!list) return;
1350
- const i = list.indexOf(callback);
1351
- if (i >= 0) list.splice(i, 1);
1352
- }
1353
- /** Fire all listeners on a channel. Protected: only the owning class emits. */
1354
- dispatch(channel, ...args) {
1355
- const list = this._listeners[channel];
1356
- if (!list) return;
1357
- for (const fn of list.slice()) fn(...args);
1358
- }
1359
- hasListeners(channel) {
1360
- const list = this._listeners[channel];
1361
- return !!list && list.length > 0;
1362
- }
1363
- /** Drop every listener on every channel (used when the owning object is disposed). */
1364
- clearListeners() {
1365
- this._listeners = {};
1366
- }
1367
- };
1368
-
1369
- // src/runtime/net.ts
1370
- var WebSocket = class extends Emitter {
1371
- _id = null;
1372
- constructor(url, headers) {
1373
- super();
1374
- this._id = _creatorUtils.websocketOpen(url, (channel, data) => this._onMessage(channel, data), headers);
1375
- }
1376
- send(message) {
1377
- if (this._id !== null) _creatorUtils.websocketSend(this._id, message);
1378
- }
1379
- close() {
1380
- if (this._id !== null) {
1381
- _creatorUtils.websocketClose(this._id);
1382
- this._id = null;
1383
- }
1384
- }
1385
- _onMessage(channel, data) {
1386
- if (data !== void 0 && data !== null) this.dispatch(channel, data);
1387
- else this.dispatch(channel);
1388
- }
1389
- };
1390
-
1391
- // src/gl/Texture.ts
1392
- var Texture = class _Texture {
1393
- /** @internal native texture handle. */
1394
- _id;
1395
- width;
1396
- height;
1397
- /** Horizontal wrap mode (U). */
1398
- wrapS = 1;
1399
- /** Vertical wrap mode (V). */
1400
- wrapT = 1;
1401
- constructor(width, height, id) {
1402
- this.width = width;
1403
- this.height = height;
1404
- this._id = id;
1405
- }
1406
- static load(source) {
1407
- if (typeof source === "string") {
1408
- return fetch(source, { useOnce: true }).then((resp) => {
1409
- if (resp.status >= 400) {
1410
- return Promise.reject(new Error(`Failed to fetch texture from ${source}. HTTP ${resp.status}`));
1411
- }
1412
- return _Texture.load(resp);
1413
- });
1414
- }
1415
- return new Promise((resolve, reject) => {
1416
- _creator.createTexture(source._id, (id, w, h) => resolve(new _Texture(w, h, id)), reject);
1417
- });
1418
- }
1419
- };
1420
-
1421
- // src/runtime/media.ts
1422
- var MediaPlayer = class extends Emitter {
1423
- _id = -1;
1424
- _src = "";
1425
- _playing = false;
1426
- _loop = false;
1427
- _volume = 1;
1428
- _disposed = false;
1429
- // Every host-touching operation runs through this so a disposed player can never address a freed
1430
- // (and possibly reused) host slot.
1431
- _assertAlive() {
1432
- if (this._disposed) throw new Error("MediaPlayer has been disposed");
1433
- }
1434
- play() {
1435
- this.playing = true;
1436
- }
1437
- pause() {
1438
- this.playing = false;
1439
- }
1440
- get playing() {
1441
- return this._playing;
1442
- }
1443
- set playing(value) {
1444
- this._assertAlive();
1445
- this._playing = value;
1446
- _creatorUtils.updateMediaPlayerPlaying(this._id, value);
1447
- }
1448
- get volume() {
1449
- return this._volume;
1450
- }
1451
- set volume(value) {
1452
- this._assertAlive();
1453
- this._volume = value;
1454
- _creatorUtils.updateMediaPlayerVolume(this._id, value);
1455
- }
1456
- get loop() {
1457
- return this._loop;
1458
- }
1459
- set loop(value) {
1460
- this._assertAlive();
1461
- this._loop = value;
1462
- _creatorUtils.updateMediaPlayerLoop(this._id, value);
1463
- }
1464
- /** Current playback position, in seconds. Settable to seek. */
1465
- get time() {
1466
- this._assertAlive();
1467
- return _creatorUtils.getMediaPlayerTime(this._id);
1468
- }
1469
- set time(value) {
1470
- this._assertAlive();
1471
- _creatorUtils.setMediaPlayerTime(this._id, value);
1472
- }
1473
- /** Total media length in seconds (0 until metadata has loaded). */
1474
- get duration() {
1475
- this._assertAlive();
1476
- return _creatorUtils.getMediaPlayerDuration(this._id);
1477
- }
1478
- /** True once {@link dispose} has been called; the player is unusable afterwards. */
1479
- get disposed() {
1480
- return this._disposed;
1481
- }
1482
- /**
1483
- * Release the underlying host media resource (decoder, network, buffers) immediately and detach
1484
- * all listeners. Idempotent. Any further use of the player throws.
1485
- */
1486
- dispose() {
1487
- if (this._disposed) return;
1488
- this._disposed = true;
1489
- this._playing = false;
1490
- this.clearListeners();
1491
- _creatorUtils.removeMediaPlayer(this._id);
1492
- }
1493
- /** @internal — the host calls this when a play-through finishes. */
1494
- _finished() {
1495
- if (this._disposed) return;
1496
- if (this._loop) {
1497
- this.dispatch("loopReached");
1498
- } else {
1499
- this._playing = false;
1500
- this.dispatch("completed");
1501
- }
1502
- }
1503
- };
1504
- var AudioPlayer = class extends MediaPlayer {
1505
- constructor(src) {
1506
- super();
1507
- this._src = src ?? "";
1508
- this._id = _creatorUtils.createAudioPlayer(this._src, () => this._finished());
1509
- }
1510
- };
1511
- var VideoPlayer = class extends MediaPlayer {
1512
- constructor(src) {
1513
- super();
1514
- this._src = src ?? "";
1515
- this._id = _creatorUtils.createVideoPlayer(this._src, () => this._finished());
1516
- }
1517
- /** A 3D Texture that samples this video (e.g. for a Material.video()). */
1518
- get texture() {
1519
- return new Texture(0, 0, _creator.createMediaPlayerTexture(this._id));
1520
- }
1521
- };
1522
-
1523
- // src/runtime/files.ts
1524
- function openFilePicker(options = {}) {
1525
- return new Promise((resolve, reject) => {
1526
- _creatorUtils.openFilePicker((resp) => {
1527
- if (!resp || resp.length === 0) {
1528
- resolve(options.multiple ? [] : null);
1529
- return;
1530
- }
1531
- if (options.multiple) resolve(resp.map((i) => new File(i[0], i[1], i[2])));
1532
- else resolve(new File(resp[0][0], resp[0][1], resp[0][2]));
1533
- }, reject, !!options.multiple, options.accept);
1534
- });
1535
- }
1536
-
1537
- // src/runtime/touch.ts
1538
- var ClickEvent = class {
1539
- type = "click";
1540
- pointerId;
1541
- clientX;
1542
- clientY;
1543
- target;
1544
- constructor(clientX, clientY, pointerId) {
1545
- this.clientX = clientX;
1546
- this.clientY = clientY;
1547
- this.pointerId = pointerId;
1548
- }
1549
- };
1550
- var TouchStartEvent = class {
1551
- type = "touchstart";
1552
- pointerId;
1553
- clientX;
1554
- clientY;
1555
- target;
1556
- /** @internal — read by the dispatch system after the handler runs. */
1557
- _trackHandler;
1558
- constructor(clientX, clientY, pointerId) {
1559
- this.clientX = clientX;
1560
- this.clientY = clientY;
1561
- this.pointerId = pointerId;
1562
- }
1563
- /** Capture subsequent moves/end for this pointer (drag/pan gestures). */
1564
- track(handler2) {
1565
- this._trackHandler = handler2;
1566
- }
1567
- };
1568
-
1569
- // src/core/registry.ts
1570
- var Registry = class {
1571
- map = /* @__PURE__ */ new Map();
1572
- set(id, value) {
1573
- this.map.set(id, value);
1574
- }
1575
- get(id) {
1576
- return this.map.get(id);
1577
- }
1578
- delete(id) {
1579
- this.map.delete(id);
1580
- }
1581
- has(id) {
1582
- return this.map.has(id);
1583
- }
1584
- };
1585
-
1586
- // src/g2/Node2D.ts
1587
- var node2dRegistry = new Registry();
1588
- var Node2D = class extends Emitter {
1589
- /** Native entity handle. */
1590
- id;
1591
- _x = 0;
1592
- _y = 0;
1593
- _rotation = 0;
1594
- _sx = 1;
1595
- _sy = 1;
1596
- _layer = 0;
1597
- _z = 0;
1598
- _visible = true;
1599
- constructor(internalId) {
1600
- super();
1601
- this.id = internalId ?? _creator2d.createEntity();
1602
- node2dRegistry.set(this.id, this);
1603
- }
1604
- // --- transform (Y-up world; at zoom 1, units == pixels) ---
1605
- get x() {
1606
- return this._x;
1607
- }
1608
- set x(v) {
1609
- this._x = v;
1610
- _creator2d.setPosition(this.id, v, this._y);
1611
- }
1612
- get y() {
1613
- return this._y;
1614
- }
1615
- set y(v) {
1616
- this._y = v;
1617
- _creator2d.setPosition(this.id, this._x, v);
1618
- }
1619
- get position() {
1620
- return new Vec2(this._x, this._y);
1621
- }
1622
- set position(v) {
1623
- this._x = cx(v);
1624
- this._y = cy(v);
1625
- _creator2d.setPosition(this.id, this._x, this._y);
1626
- }
1627
- /** Rotation in degrees (CCW). */
1628
- get rotation() {
1629
- return this._rotation;
1630
- }
1631
- set rotation(deg) {
1632
- this._rotation = deg;
1633
- _creator2d.setRotation(this.id, deg);
1634
- }
1635
- get scale() {
1636
- return new Vec2(this._sx, this._sy);
1637
- }
1638
- set scale(v) {
1639
- const sx = typeof v === "number" ? v : cx(v);
1640
- const sy = typeof v === "number" ? v : cy(v);
1641
- this._sx = sx;
1642
- this._sy = sy;
1643
- _creator2d.setScale(this.id, sx, sy);
1644
- }
1645
- /** Draw layer (int). Higher layers render on top. */
1646
- get layer() {
1647
- return this._layer;
1648
- }
1649
- set layer(v) {
1650
- this._layer = v;
1651
- _creator2d.setLayer(this.id, v);
1652
- }
1653
- /** Intra-layer depth for non-Y-sorted layers (higher z = on top). */
1654
- get z() {
1655
- return this._z;
1656
- }
1657
- set z(v) {
1658
- this._z = v;
1659
- _creator2d.setZ(this.id, v);
1660
- }
1661
- get visible() {
1662
- return this._visible;
1663
- }
1664
- set visible(v) {
1665
- this._visible = v;
1666
- _creator2d.setVisible(this.id, v);
1667
- }
1668
- /**
1669
- * Attach a collider for overlap detection. DEFERRED: native 2D physics/triggers are not built yet,
1670
- * so this records intent and is a no-op until the engine lands — the 'enter'/'exit' events won't
1671
- * fire. The API is here so game code that uses it compiles and stays stable.
1672
- */
1673
- collider(_options = {}) {
1674
- return this;
1675
- }
1676
- /** @internal — overridden by Sprite to translate native clip ids into named animation events. */
1677
- _dispatchAnimEvent(_clipId, _type) {
1678
- }
1679
- destroy() {
1680
- node2dRegistry.delete(this.id);
1681
- _creator2d.destroyEntity(this.id);
1682
- }
1683
- };
1684
-
1685
- // src/g2/loop.ts
1686
- var onUpdate = (callback) => _creator2d.onUpdate(callback);
1687
- var offUpdate = (callback) => _creator2d.offUpdate(callback);
1688
- var animEventsRegistered = false;
1689
- var ensureAnimEvents = () => {
1690
- if (animEventsRegistered) return;
1691
- animEventsRegistered = true;
1692
- _creator2d.setOnAnimEvent(({ entityId, clipId, type }) => {
1693
- node2dRegistry.get(entityId)?._dispatchAnimEvent(clipId, type);
1694
- });
1695
- };
1696
-
1697
- // src/g2/Camera2D.ts
1698
- var Camera2D = class {
1699
- _x = 0;
1700
- _y = 0;
1701
- _zoom = 1;
1702
- _rotation = 0;
1703
- _followTarget = null;
1704
- _followOpts = {};
1705
- _followCb = null;
1706
- sceneId;
1707
- constructor(sceneId) {
1708
- this.sceneId = sceneId;
1709
- }
1710
- get position() {
1711
- return new Vec2(this._x, this._y);
1712
- }
1713
- set position(v) {
1714
- this._x = cx(v);
1715
- this._y = cy(v);
1716
- _creator2d.cameraSetPosition(this.sceneId, this._x, this._y);
1717
- }
1718
- get zoom() {
1719
- return this._zoom;
1720
- }
1721
- set zoom(z) {
1722
- this._zoom = z;
1723
- _creator2d.cameraSetZoom(this.sceneId, z);
1724
- }
1725
- get rotation() {
1726
- return this._rotation;
1727
- }
1728
- set rotation(deg) {
1729
- this._rotation = deg;
1730
- _creator2d.cameraSetRotation(this.sceneId, deg);
1731
- }
1732
- screenToWorld(screenX, screenY) {
1733
- const o = _creator2d.cameraScreenToWorld(this.sceneId, screenX, screenY);
1734
- return new Vec2(o[0], o[1]);
1735
- }
1736
- worldToScreen(worldX, worldY) {
1737
- const o = _creator2d.cameraWorldToScreen(this.sceneId, worldX, worldY);
1738
- return new Vec2(o[0], o[1]);
1739
- }
1740
- /** Track a node each frame (snaps by default; pass `smooth` for eased follow). */
1741
- follow(node, options = {}) {
1742
- this._followTarget = node;
1743
- this._followOpts = options;
1744
- if (this._followCb) return this;
1745
- this._followCb = (dt) => {
1746
- const t = this._followTarget;
1747
- if (!t) return;
1748
- const [ox, oy] = this._followOpts.offset ?? [0, 0];
1749
- const tx = t.x + ox;
1750
- const ty = t.y + oy;
1751
- const s = this._followOpts.smooth ?? 0;
1752
- if (s > 0) {
1753
- const k = 1 - Math.exp(-dt / s);
1754
- this.position = [this._x + (tx - this._x) * k, this._y + (ty - this._y) * k];
1755
- } else {
1756
- this.position = [tx, ty];
1757
- }
1758
- };
1759
- onUpdate(this._followCb);
1760
- return this;
1761
- }
1762
- unfollow() {
1763
- this._followTarget = null;
1764
- if (this._followCb) {
1765
- offUpdate(this._followCb);
1766
- this._followCb = null;
1767
- }
1768
- }
1769
- };
1770
-
1771
- // src/g2/Scene2D.ts
1772
- var LayerHandle = class {
1773
- sceneId;
1774
- layer;
1775
- constructor(sceneId, layer) {
1776
- this.sceneId = sceneId;
1777
- this.layer = layer;
1778
- }
1779
- /** In this layer, lower-on-screen sprites overlap higher ones (top-down feet sorting). */
1780
- ySort(enabled = true) {
1781
- _creator2d.setLayerYSort(this.sceneId, this.layer, enabled);
1782
- return this;
1783
- }
1784
- };
1785
- var Scene2D = class _Scene2D {
1786
- /** Native scene handle. */
1787
- id;
1788
- camera;
1789
- static _active = null;
1790
- static get active() {
1791
- return _Scene2D._active;
1792
- }
1793
- constructor(options = {}) {
1794
- this.id = _creator2d.createScene();
1795
- this.camera = new Camera2D(this.id);
1796
- if (options.background !== void 0) this.background = options.background;
1797
- }
1798
- set background(color) {
1799
- const [r, g, b2, a2] = Color.toRgba01(color);
1800
- _creator2d.sceneSetClearColor(this.id, r, g, b2, a2);
1801
- }
1802
- /** Address a draw layer to configure it, e.g. scene2d.layer(1).ySort(). */
1803
- layer(n) {
1804
- return new LayerHandle(this.id, n);
1805
- }
1806
- add(...nodes) {
1807
- for (const n of nodes) _creator2d.addEntityToScene(this.id, n.id);
1808
- return this;
1809
- }
1810
- remove(...nodes) {
1811
- for (const n of nodes) _creator2d.removeEntityFromScene(this.id, n.id);
1812
- return this;
1813
- }
1814
- /** Make this the active scene (brings up the 2D engine on the canvas; closes any other). */
1815
- open() {
1816
- _creator2d.openScene(this.id);
1817
- _Scene2D._active = this;
1818
- return this;
1819
- }
1820
- close() {
1821
- _creator2d.closeScene();
1822
- if (_Scene2D._active === this) _Scene2D._active = null;
1823
- return this;
1824
- }
1825
- destroy() {
1826
- this.close();
1827
- _creator2d.destroyScene(this.id);
1828
- }
1829
- };
1830
-
1831
- // src/g2/Sprite.ts
1832
- var Sprite = class extends Node2D {
1833
- _texture;
1834
- _anchor = new Vec2(0.5, 0.5);
1835
- _color = 16777215;
1836
- _opacity = 1;
1837
- _flipX = false;
1838
- _flipY = false;
1839
- _size = null;
1840
- _clipIds = {};
1841
- _clipNames = {};
1842
- _current = null;
1843
- constructor(options = {}) {
1844
- super();
1845
- if (options.texture) this.texture = options.texture;
1846
- if (options.size) this.size = options.size;
1847
- if (options.anchor) this.anchor = options.anchor;
1848
- if (options.frame) this.frame = options.frame;
1849
- if (options.color !== void 0) this.color = options.color;
1850
- if (options.opacity !== void 0) this.opacity = options.opacity;
1851
- if (options.layer !== void 0) this.layer = options.layer;
1852
- if (options.position) this.position = options.position;
1853
- }
1854
- get texture() {
1855
- return this._texture;
1856
- }
1857
- set texture(t) {
1858
- this._texture = t;
1859
- if (!t) return;
1860
- _creator2d.setSprite(this.id, t.id);
1861
- if (this._size) _creator2d.setSpriteSize(this.id, this._size.x, this._size.y);
1862
- }
1863
- /** Current world size: explicit override if set, else the texture's pixel dimensions. */
1864
- get size() {
1865
- return this._size ?? (this._texture ? new Vec2(this._texture.width, this._texture.height) : new Vec2(0, 0));
1866
- }
1867
- set size(v) {
1868
- this._size = new Vec2(v);
1869
- _creator2d.setSpriteSize(this.id, this._size.x, this._size.y);
1870
- }
1871
- get anchor() {
1872
- return this._anchor.clone();
1873
- }
1874
- set anchor(v) {
1875
- this._anchor = new Vec2(v);
1876
- _creator2d.setSpriteAnchor(this.id, this._anchor.x, this._anchor.y);
1877
- }
1878
- /** Set the UV sub-rect (normalized). */
1879
- set frame(uv) {
1880
- _creator2d.setSpriteFrame(this.id, uv[0], uv[1], uv[2], uv[3]);
1881
- }
1882
- /** Set the sub-rect in texture pixels. */
1883
- setFramePx(px, py, pw, ph) {
1884
- _creator2d.setSpriteFramePx(this.id, px, py, pw, ph);
1885
- return this;
1886
- }
1887
- get color() {
1888
- return this._color;
1889
- }
1890
- set color(c2) {
1891
- this._color = c2;
1892
- const [r, g, b2] = Color.toRgb01(c2);
1893
- _creator2d.setSpriteColor(this.id, r, g, b2);
1894
- }
1895
- get opacity() {
1896
- return this._opacity;
1897
- }
1898
- set opacity(a2) {
1899
- this._opacity = a2;
1900
- _creator2d.setSpriteOpacity(this.id, a2);
1901
- }
1902
- get flipX() {
1903
- return this._flipX;
1904
- }
1905
- set flipX(v) {
1906
- this._flipX = v;
1907
- _creator2d.setSpriteFlip(this.id, v, this._flipY);
1908
- }
1909
- get flipY() {
1910
- return this._flipY;
1911
- }
1912
- set flipY(v) {
1913
- this._flipY = v;
1914
- _creator2d.setSpriteFlip(this.id, this._flipX, v);
1915
- }
1916
- // --- animation (chainable) ---
1917
- /**
1918
- * Define named sprite-sheet clips from a uniform grid. Frame indices are row-major into the grid.
1919
- * Sets the sprite's display size to one cell. Once defined, clips advance entirely in native code.
1920
- */
1921
- clips(def) {
1922
- const tex = this._texture;
1923
- if (!tex) throw new Error("Sprite.clips(): set a texture before defining clips");
1924
- const [fw, fh] = def.size;
1925
- const defFps = typeof def.fps === "number" ? def.fps : 12;
1926
- const defLoop = typeof def.loop === "boolean" ? def.loop : true;
1927
- const cols = Math.max(1, Math.floor(tex.width / fw));
1928
- for (const name of Object.keys(def)) {
1929
- if (name === "size" || name === "fps" || name === "loop") continue;
1930
- const value = def[name];
1931
- const indices = Array.isArray(value) ? value : value.frames;
1932
- const fps = Array.isArray(value) ? defFps : value.fps ?? defFps;
1933
- const loop = Array.isArray(value) ? defLoop : value.loop ?? defLoop;
1934
- const frames = new Float32Array(indices.length * 4);
1935
- indices.forEach((idx, i) => {
1936
- const cx2 = idx % cols * fw;
1937
- const cy2 = Math.floor(idx / cols) * fh;
1938
- frames[i * 4 + 0] = cx2 / tex.width;
1939
- frames[i * 4 + 1] = cy2 / tex.height;
1940
- frames[i * 4 + 2] = (cx2 + fw) / tex.width;
1941
- frames[i * 4 + 3] = (cy2 + fh) / tex.height;
1942
- });
1943
- const clipId = _creator2d.defineAnimation(this.id, frames, fps, loop);
1944
- this._clipIds[name] = clipId;
1945
- this._clipNames[clipId] = name;
1946
- }
1947
- if (!this._size) _creator2d.setSpriteSize(this.id, fw, fh);
1948
- ensureAnimEvents();
1949
- return this;
1950
- }
1951
- /** Play a clip by name. Re-playing the active clip is a no-op (keeps it running smoothly). */
1952
- play(name) {
1953
- const id = this._clipIds[name];
1954
- if (id === void 0) throw new Error(`Sprite.play(): unknown clip "${name}"`);
1955
- if (this._current === name) return this;
1956
- this._current = name;
1957
- _creator2d.playAnimation(this.id, id);
1958
- return this;
1959
- }
1960
- stop() {
1961
- this._current = null;
1962
- _creator2d.stopAnimation(this.id);
1963
- return this;
1964
- }
1965
- set animationSpeed(value) {
1966
- _creator2d.setAnimationSpeed(this.id, value);
1967
- }
1968
- get currentClip() {
1969
- return this._current;
1970
- }
1971
- get animationFrame() {
1972
- return _creator2d.getAnimationFrame(this.id);
1973
- }
1974
- /** @internal — translate a native clip id back to its name and emit the animation event. */
1975
- _dispatchAnimEvent(clipId, type) {
1976
- const name = this._clipNames[clipId] ?? "";
1977
- this.dispatch(type === 1 ? "completed" : "loopReached", name);
1978
- }
1979
- };
1980
-
1981
- // src/g2/Tilemap.ts
1982
- var Tilemap = class extends Node2D {
1983
- cols;
1984
- rows;
1985
- constructor(options) {
1986
- const data = options.data instanceof Int32Array ? options.data : Int32Array.from(options.data);
1987
- const tileW = typeof options.tile === "number" ? options.tile : options.tile[0];
1988
- const tileH = typeof options.tile === "number" ? options.tile : options.tile[1];
1989
- const [atlasCols, atlasRows] = options.atlas;
1990
- const id = _creator2d.createTilemap(
1991
- options.texture.id,
1992
- options.cols,
1993
- options.rows,
1994
- tileW,
1995
- tileH,
1996
- atlasCols,
1997
- atlasRows,
1998
- data
1999
- );
2000
- super(id);
2001
- this.cols = options.cols;
2002
- this.rows = options.rows;
2003
- }
2004
- setTile(x, y, index) {
2005
- _creator2d.setTile(this.id, x, y, index);
2006
- return this;
2007
- }
2008
- };
2009
-
2010
- // src/g2/Texture2D.ts
2011
- var Texture2D = class _Texture2D {
2012
- /** Native texture handle. */
2013
- id;
2014
- width;
2015
- height;
2016
- constructor(id, width, height) {
2017
- this.id = id;
2018
- this.width = width;
2019
- this.height = height;
2020
- }
2021
- static load(source) {
2022
- if (typeof source === "string") {
2023
- return fetch(source, { useOnce: true }).then((resp) => {
2024
- if (resp.status >= 400) {
2025
- return Promise.reject(new Error(`Failed to fetch texture from ${source}. HTTP ${resp.status}`));
2026
- }
2027
- return _Texture2D.load(resp);
2028
- });
2029
- }
2030
- return new Promise((resolve, reject) => {
2031
- _creator2d.createTexture(source.id ?? source._id, (id, w, h) => resolve(new _Texture2D(id, w, h)), reject);
2032
- });
2033
- }
2034
- destroy() {
2035
- _creator2d.destroyTexture(this.id);
2036
- }
2037
- };
2038
-
2039
- // src/ui/UINode.ts
2040
- function styleFunction(newStyle) {
2041
- Object.assign(this._style, newStyle);
2042
- if (this._id !== 0) {
2043
- _creatorUI.mergeStyle(this._id, newStyle);
2044
- }
2045
- return this;
2046
- }
2047
- var handler = {
2048
- get(target, key, receiver) {
2049
- if (key === "_raw") {
2050
- return target._el._style;
2051
- }
2052
- return target._el._style[key];
2053
- },
2054
- set(target, key, value) {
2055
- if (target._el._id !== 0) {
2056
- _creatorUI.updateStyle(target._el._id, key, value);
2057
- }
2058
- target._el._style[key] = value;
2059
- return true;
2060
- },
2061
- getOwnPropertyDescriptor(target, p) {
2062
- return Object.getOwnPropertyDescriptor(target._el._style, p);
2063
- },
2064
- ownKeys(target) {
2065
- return Object.keys(target._el._style);
2066
- }
2067
- };
2068
- function Style() {
2069
- }
2070
- var Element = class {
2071
- type;
2072
- /** Author-given semantic name (LeCodes `name`) — see BaseStyle.name. Extracted from the options
2073
- * object so it never reaches the layout engine; the renderer reads it off the node. */
2074
- name;
2075
- _style;
2076
- _id = 0;
2077
- _styleProxy;
2078
- _appear;
2079
- _disappear;
2080
- constructor(type, style) {
2081
- this.type = type;
2082
- if (style && typeof style.name === "string" && style.name.length > 0) {
2083
- this.name = style.name;
2084
- delete style.name;
2085
- }
2086
- this._style = style;
2087
- }
2088
- get style() {
2089
- if (this._styleProxy) return this._styleProxy;
2090
- const obj = styleFunction.bind(this);
2091
- obj._el = this;
2092
- const proxy = new Proxy(obj, handler);
2093
- this._styleProxy = proxy;
2094
- return proxy;
2095
- }
2096
- animateTo(style) {
2097
- _creatorUI.animateTo(this._id, style);
2098
- if (style.commit !== false) {
2099
- Object.assign(this._style, style);
2100
- }
2101
- return this;
2102
- }
2103
- animateFrom(style) {
2104
- _creatorUI.animateFrom(this._id, style);
2105
- return this;
2106
- }
2107
- ll;
2108
- onLayout(onLayout) {
2109
- if (this.ll) {
2110
- this.ll.push(onLayout);
2111
- } else {
2112
- this.ll = [onLayout];
2113
- }
2114
- return this;
2115
- }
2116
- };
2117
- var ContainerElement = class extends Element {
2118
- children = [];
2119
- constructor(type, style, children) {
2120
- super(type, style);
2121
- this.children = children;
2122
- }
2123
- append(...nodes) {
2124
- if (this._id === 0) {
2125
- this.children.push(...nodes);
2126
- return this;
2127
- }
2128
- let index = 0;
2129
- for (let child of this.children) {
2130
- if (child) {
2131
- index++;
2132
- }
2133
- }
2134
- for (let node of nodes) {
2135
- if (!node) continue;
2136
- _creatorUI.insertNode(index, this._id, node);
2137
- index++;
2138
- }
2139
- this.children.push(...nodes);
2140
- return this;
2141
- }
2142
- insert(index, ...nodes) {
2143
- if (this._id === 0) {
2144
- this.children.splice(index, 0, ...nodes);
2145
- return this;
2146
- }
2147
- let _index = 0;
2148
- for (let i = 0; i < index; i++) {
2149
- if (this.children[i]) {
2150
- _index++;
2151
- }
2152
- }
2153
- for (let node of nodes) {
2154
- if (!node) continue;
2155
- _creatorUI.insertNode(_index, this._id, node);
2156
- _index++;
2157
- }
2158
- this.children.splice(index, 0, ...nodes);
2159
- return this;
2160
- }
2161
- remove(...nodesToDelete) {
2162
- const set = new Set(nodesToDelete);
2163
- for (let i = 0; i < this.children.length; i++) {
2164
- if (set.has(this.children[i])) {
2165
- set.delete(this.children[i]);
2166
- this.children.splice(i, 1);
2167
- i--;
2168
- if (set.size === 0) {
2169
- break;
2170
- }
2171
- }
2172
- }
2173
- if (this._id === 0) {
2174
- return this;
2175
- }
2176
- for (let node of nodesToDelete) {
2177
- _creatorUI.removeNode(this._id, node);
2178
- }
2179
- return this;
2180
- }
2181
- setContent(children) {
2182
- if (this._id !== 0) {
2183
- _creatorUI.setContent(this._id, this.children, children);
2184
- }
2185
- this.children.length = 0;
2186
- this.children.push(...children);
2187
- return this;
2188
- }
2189
- };
2190
-
2191
- // src/ui/UIButton.ts
2192
- var ButtonElement = class extends ContainerElement {
2193
- clickListeners = [];
2194
- touchStartListeners = [];
2195
- onClick(callback) {
2196
- this.clickListeners.push(callback);
2197
- return this;
2198
- }
2199
- _emitClick(pointerId, clientX, clientY) {
2200
- const touchEvent = new ClickEvent(clientX, clientY, pointerId);
2201
- touchEvent.target = this;
2202
- for (let callback of this.clickListeners) {
2203
- callback(touchEvent);
2204
- }
2205
- return null;
2206
- }
2207
- onTouchStart(callback) {
2208
- this.touchStartListeners.push(callback);
2209
- return this;
2210
- }
2211
- _emitTouchStart(pointerId, clientX, clientY) {
2212
- const touchEvent = new TouchStartEvent(clientX, clientY, pointerId);
2213
- touchEvent.target = this;
2214
- for (let callback of this.touchStartListeners) {
2215
- callback(touchEvent);
2216
- if (touchEvent._trackHandler) {
2217
- return touchEvent._trackHandler;
2218
- }
2219
- }
2220
- return null;
2221
- }
2222
- isPressed() {
2223
- if (!this._id) return false;
2224
- return _creatorUI.isButtonPressed(this._id);
2225
- }
2226
- };
2227
- function UIButton(...args) {
2228
- if (args.length === 0) {
2229
- return new ButtonElement("button", {}, []);
2230
- } else if (args.length === 1) {
2231
- if (Array.isArray(args[0])) {
2232
- return new ButtonElement("button", {}, args[0]);
2233
- } else {
2234
- return new ButtonElement("button", args[0], []);
2235
- }
2236
- } else {
2237
- return new ButtonElement("button", args[0], args[1]);
2238
- }
2239
- }
2240
-
2241
- // src/ui/UIScreen.ts
2242
- var ScreenElement = class extends ContainerElement {
2243
- scrollable = false;
2244
- open() {
2245
- _creatorUI.openScreen(this);
2246
- }
2247
- close() {
2248
- _creatorUI.closeScreen();
2249
- }
2250
- ol = [];
2251
- onOpen(callback) {
2252
- this.ol.push(callback);
2253
- return this;
2254
- }
2255
- cl = [];
2256
- onClose(callback) {
2257
- this.cl.push(callback);
2258
- return this;
2259
- }
2260
- touchStartListeners = [];
2261
- _interactive = false;
2262
- onTouchStart(callback) {
2263
- this._interactive = true;
2264
- this.touchStartListeners.push(callback);
2265
- return this;
2266
- }
2267
- _emitTouchStart(pointerId, clientX, clientY) {
2268
- const touchEvent = new TouchStartEvent(clientX, clientY, pointerId);
2269
- touchEvent.target = this;
2270
- for (let callback of this.touchStartListeners) {
2271
- callback(touchEvent);
2272
- if (touchEvent._trackHandler) {
2273
- return touchEvent._trackHandler;
2274
- }
2275
- }
2276
- return null;
2277
- }
2278
- makeScrollable() {
2279
- this.scrollable = true;
2280
- Object.assign(this, {
2281
- sl: [],
2282
- osl: [],
2283
- onScroll(callback) {
2284
- this.sl.push(callback);
2285
- return this;
2286
- },
2287
- onOverscroll(callback) {
2288
- this.osl.push(callback);
2289
- return this;
2290
- },
2291
- onRefresh(callback) {
2292
- this._refreshCallback = callback;
2293
- return this;
2294
- }
2295
- });
2296
- return this;
2297
- }
2298
- _backButtonCallback;
2299
- onBackPressed(callback) {
2300
- this._backButtonCallback = callback;
2301
- return this;
2302
- }
2303
- };
2304
- function UIScreen(...args) {
2305
- if (args.length === 0) {
2306
- return new ScreenElement("screen", {}, []);
2307
- } else if (args.length === 1) {
2308
- if (Array.isArray(args[0])) {
2309
- return new ScreenElement("screen", {}, args[0]);
2310
- } else {
2311
- return new ScreenElement("screen", args[0], []);
2312
- }
2313
- } else {
2314
- return new ScreenElement("screen", args[0], args[1]);
2315
- }
2316
- }
2317
-
2318
- // src/ui/UISpacer.ts
2319
- function UISpacer(style) {
2320
- return new Element("spacer", style ?? {});
2321
- }
2322
-
2323
- // src/ui/UIText.ts
2324
- var TextElement = class extends Element {
2325
- _text;
2326
- constructor(style, text) {
2327
- super("text", style);
2328
- this._text = text;
2329
- }
2330
- get text() {
2331
- return this._text;
2332
- }
2333
- set text(value) {
2334
- this._text = value;
2335
- if (this._id !== 0) {
2336
- _creatorUI.updateNode(this._id, "text", value);
2337
- }
2338
- }
2339
- };
2340
- function UIText(...args) {
2341
- if (args.length === 1) {
2342
- return new TextElement({}, args[0]);
2343
- } else {
2344
- return new TextElement(args[0], args[1]);
2345
- }
2346
- }
2347
-
2348
- // src/ui/fonts.ts
2349
- async function registerFont(fontFamily, url, options = {}) {
2350
- return new Promise((res, rej) => {
2351
- _creatorUI.registerFont(fontFamily, url, options, res, rej);
2352
- });
2353
- }
2354
-
2355
- // src/ui/UIImage.ts
2356
- var ImageElement = class extends Element {
2357
- _src;
2358
- // Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
2359
- // just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
2360
- // the viewer reads it when it creates the element.
2361
- _sourceRect;
2362
- constructor(style, src) {
2363
- super("image", style);
2364
- this._src = src;
2365
- }
2366
- get src() {
2367
- return this._src;
2368
- }
2369
- set src(value) {
2370
- this._src = value;
2371
- if (this._id !== 0) {
2372
- _creatorUI.updateNode(this._id, "src", value);
2373
- }
2374
- }
2375
- setSourceRect(x, y, w, h) {
2376
- this._sourceRect = [x, y, w, h];
2377
- if (this._id !== 0) {
2378
- _creatorUI.setSourceRect(this._id, x, y, w, h);
2379
- }
2380
- return this;
2381
- }
2382
- };
2383
- function UIImage(...args) {
2384
- if (args.length === 1) {
2385
- return new ImageElement({}, args[0]);
2386
- } else {
2387
- return new ImageElement(args[0], args[1]);
2388
- }
2389
- }
2390
-
2391
- // src/ui/UIVideo.ts
2392
- var VideoElement = class extends Element {
2393
- _playerId;
2394
- _player;
2395
- constructor(style, player) {
2396
- super("video", style);
2397
- this._player = player;
2398
- this._playerId = player._id;
2399
- }
2400
- get player() {
2401
- return this._player;
2402
- }
2403
- };
2404
- function UIVideo(...args) {
2405
- if (args.length === 1) {
2406
- return new VideoElement({}, args[0]);
2407
- } else {
2408
- return new VideoElement(args[0], args[1]);
2409
- }
2410
- }
2411
-
2412
- // src/ui/UIInput.ts
2413
- var InputElement = class extends Element {
2414
- cl = [];
2415
- fl = [];
2416
- bl = [];
2417
- get value() {
2418
- return _creatorUI.getTextValue(this._id);
2419
- }
2420
- set value(value) {
2421
- if (this._id !== 0) {
2422
- _creatorUI.updateNode(this._id, "value", value);
2423
- }
2424
- }
2425
- onChange(callback) {
2426
- this.cl.push(callback);
2427
- return this;
2428
- }
2429
- onFocus(callback) {
2430
- this.fl.push(callback);
2431
- return this;
2432
- }
2433
- onBlur(callback) {
2434
- this.bl.push(callback);
2435
- return this;
2436
- }
2437
- };
2438
- function UIInput(style) {
2439
- return new InputElement("input", style ?? {});
2440
- }
2441
- function UITextArea(style) {
2442
- return new InputElement("textarea", style ?? {});
2443
- }
2444
-
2445
- // src/ui/UIWidget.ts
2446
- var WidgetElement = class extends ContainerElement {
2447
- scrollable = false;
2448
- show() {
2449
- _creatorUI.showWidget(this);
2450
- }
2451
- hide() {
2452
- _creatorUI.hideWidget(this);
2453
- }
2454
- get isShow() {
2455
- return this._id !== 0;
2456
- }
2457
- _interactive = false;
2458
- touchStartListeners = [];
2459
- onTouchStart(callback) {
2460
- this._interactive = true;
2461
- this.touchStartListeners.push(callback);
2462
- return this;
2463
- }
2464
- _emitTouchStart(pointerId, clientX, clientY) {
2465
- const touchEvent = new TouchStartEvent(clientX, clientY, pointerId);
2466
- touchEvent.target = this;
2467
- for (let callback of this.touchStartListeners) {
2468
- callback(touchEvent);
2469
- if (touchEvent._trackHandler) {
2470
- return touchEvent._trackHandler;
2471
- }
2472
- }
2473
- return null;
2474
- }
2475
- otl = [];
2476
- onOverlayTap(callback) {
2477
- this.otl.push(callback);
2478
- return this;
2479
- }
2480
- _backButtonCallback;
2481
- onBackPressed(callback) {
2482
- this._backButtonCallback = callback;
2483
- return this;
2484
- }
2485
- };
2486
- function UIWidget(...args) {
2487
- if (args.length === 0) {
2488
- return new WidgetElement("widget", {}, []);
2489
- } else if (args.length === 1) {
2490
- if (Array.isArray(args[0])) {
2491
- return new WidgetElement("widget", {}, args[0]);
2492
- } else {
2493
- return new WidgetElement("widget", args[0], []);
2494
- }
2495
- } else {
2496
- return new WidgetElement("widget", args[0], args[1]);
2497
- }
2498
- }
2499
-
2500
- // src/ui/UIContainer.ts
2501
- function UIColumn(...args) {
2502
- if (args.length === 0) {
2503
- return new ContainerElement("column", {}, []);
2504
- } else if (args.length === 1) {
2505
- if (Array.isArray(args[0])) {
2506
- return new ContainerElement("column", {}, args[0]);
2507
- } else {
2508
- return new ContainerElement("column", args[0], []);
2509
- }
2510
- } else {
2511
- return new ContainerElement("column", args[0], args[1]);
2512
- }
2513
- }
2514
- function UIRow(...args) {
2515
- if (args.length === 0) {
2516
- return new ContainerElement("row", {}, []);
2517
- } else if (args.length === 1) {
2518
- if (Array.isArray(args[0])) {
2519
- return new ContainerElement("row", {}, args[0]);
2520
- } else {
2521
- return new ContainerElement("row", args[0], []);
2522
- }
2523
- } else {
2524
- return new ContainerElement("row", args[0], args[1]);
2525
- }
2526
- }
2527
- function UIBox(...args) {
2528
- if (args.length === 0) {
2529
- return new ContainerElement("box", {}, []);
2530
- } else if (args.length === 1) {
2531
- if (Array.isArray(args[0])) {
2532
- return new ContainerElement("box", {}, args[0]);
2533
- } else {
2534
- return new ContainerElement("box", args[0], []);
2535
- }
2536
- } else {
2537
- return new ContainerElement("box", args[0], args[1]);
2538
- }
2539
- }
2540
-
2541
- // src/ui/UIScrollable.ts
2542
- var ScrollableElement = class extends ContainerElement {
2543
- osl = [];
2544
- sl = [];
2545
- srl = [];
2546
- get scrollListeners() {
2547
- return this.sl;
2548
- }
2549
- onScroll(callback) {
2550
- this.sl.push(callback);
2551
- return this;
2552
- }
2553
- onScrollRelease(callback) {
2554
- this.srl.push(callback);
2555
- return this;
2556
- }
2557
- onOverscroll(callback) {
2558
- this.osl.push(callback);
2559
- return this;
2560
- }
2561
- };
2562
- function UIScrollable(...args) {
2563
- if (args.length === 0) {
2564
- return new ScrollableElement("scrollable", {}, []);
2565
- } else if (args.length === 1) {
2566
- if (Array.isArray(args[0])) {
2567
- return new ScrollableElement("scrollable", {}, args[0]);
2568
- } else {
2569
- return new ScrollableElement("scrollable", args[0], []);
2570
- }
2571
- } else {
2572
- return new ScrollableElement("scrollable", args[0], args[1]);
2573
- }
2574
- }
2575
-
2576
- // src/ui/UIVirtualizedList.ts
2577
- var VListElement = class extends Element {
2578
- // --- читается нативкой в createUINode (copyProperty / copyNodePtr) ---
2579
- _overscan;
2580
- // -1 = "высота вьюпорта", резолвится в нативке
2581
- _inverted;
2582
- _keys = [];
2583
- // начальное состояние до маунта ноды
2584
- _estimates = [];
2585
- // параллельно _keys
2586
- _endThreshold = 0;
2587
- _startThreshold = 0;
2588
- // --- списки слушателей, нативка забирает через copyPropertyListPtr ---
2589
- sl = [];
2590
- // scroll
2591
- erl = [];
2592
- // endReached
2593
- strl = [];
2594
- // startReached
2595
- // --- приватное состояние JS-стороны ---
2596
- items = /* @__PURE__ */ new Map();
2597
- mounted = /* @__PURE__ */ new Map();
2598
- keyOf;
2599
- renderItem;
2600
- estimate;
2601
- constructor(config) {
2602
- super("vlist", {});
2603
- this.keyOf = config.keyOf;
2604
- this.renderItem = config.render;
2605
- this.estimate = typeof config.estimatedHeight === "function" ? config.estimatedHeight : () => config.estimatedHeight;
2606
- this._overscan = config.overscan ?? -1;
2607
- this._inverted = config.inverted ?? false;
2608
- }
2609
- get isAttached() {
2610
- return !!this._id && this._id !== 0;
2611
- }
2612
- register(items) {
2613
- const keys = [];
2614
- const estimates = [];
2615
- for (const item of items) {
2616
- const k = this.keyOf(item);
2617
- this.items.set(k, item);
2618
- keys.push(k);
2619
- estimates.push(this.estimate(item));
2620
- }
2621
- return [keys, estimates];
2622
- }
2623
- setData(items) {
2624
- const prevItems = new Map(this.items);
2625
- const prevMounted = [...this.mounted.keys()];
2626
- this.items.clear();
2627
- const [keys, estimates] = this.register(items);
2628
- this._keys = keys;
2629
- this._estimates = estimates;
2630
- if (this.isAttached) {
2631
- _creatorUI.vlistSetKeys(this, keys, estimates);
2632
- for (const k of prevMounted) {
2633
- if (this.mounted.has(k) && this.items.get(k) !== prevItems.get(k)) {
2634
- _creatorUI.vlistInvalidate(this, k);
2635
- }
2636
- }
2637
- }
2638
- return this;
2639
- }
2640
- append(...items) {
2641
- const [keys, estimates] = this.register(items);
2642
- this._keys.push(...keys);
2643
- this._estimates.push(...estimates);
2644
- if (this.isAttached) {
2645
- _creatorUI.vlistInsertKeys(this, -1, keys, estimates);
2646
- }
2647
- return this;
2648
- }
2649
- prepend(...items) {
2650
- const [keys, estimates] = this.register(items);
2651
- this._keys.unshift(...keys);
2652
- this._estimates.unshift(...estimates);
2653
- if (this.isAttached) {
2654
- _creatorUI.vlistInsertKeys(this, 0, keys, estimates);
2655
- }
2656
- return this;
2657
- }
2658
- update(...items) {
2659
- for (const item of items) {
2660
- const k = this.keyOf(item);
2661
- if (!this.items.has(k)) continue;
2662
- this.items.set(k, item);
2663
- if (this.isAttached && this.mounted.has(k)) {
2664
- _creatorUI.vlistInvalidate(this, k);
2665
- }
2666
- }
2667
- return this;
2668
- }
2669
- removeByKey(...keys) {
2670
- for (const k of keys) {
2671
- if (!this.items.delete(k)) continue;
2672
- const i = this._keys.indexOf(k);
2673
- if (i >= 0) {
2674
- this._keys.splice(i, 1);
2675
- this._estimates.splice(i, 1);
2676
- }
2677
- }
2678
- if (this.isAttached) {
2679
- _creatorUI.vlistRemoveKeys(this, keys);
2680
- }
2681
- return this;
2682
- }
2683
- get itemCount() {
2684
- return this.items.size;
2685
- }
2686
- getItem(key) {
2687
- return this.items.get(key);
2688
- }
2689
- // --- команды: офсеты и позиции знает только нативка (префикс-суммы) ---
2690
- scrollTo(offset, animated = true) {
2691
- _creatorUI.command(this, "scrollTo", offset, animated);
2692
- }
2693
- scrollToKey(key, animated = true) {
2694
- _creatorUI.command(this, "scrollToKey", key, animated);
2695
- }
2696
- scrollToEnd(animated = true) {
2697
- _creatorUI.command(this, "scrollToEnd", animated);
2698
- }
2699
- onScroll(callback) {
2700
- this.sl.push(callback);
2701
- return this;
2702
- }
2703
- onEndReached(thresholdPx, callback) {
2704
- this._endThreshold = thresholdPx;
2705
- this.erl.push(callback);
2706
- return this;
2707
- }
2708
- onStartReached(thresholdPx, callback) {
2709
- this._startThreshold = thresholdPx;
2710
- this.strl.push(callback);
2711
- return this;
2712
- }
2713
- /**
2714
- * Единственный колбэк из нативки (через jsPtr, как у button).
2715
- * Вызывается ТОЛЬКО когда окно пересекло границу — не на каждый кадр скролла.
2716
- * Порядок: сначала unmount (нативка уже снесла поддеревья и обнулила _id,
2717
- * здесь только чистим JS-ссылки), потом mount.
2718
- */
2719
- _syncWindow(unmountKeys, mountKeys) {
2720
- for (const k of unmountKeys) {
2721
- this.mounted.delete(k);
2722
- }
2723
- for (const k of mountKeys) {
2724
- const item = this.items.get(k);
2725
- if (item === void 0) continue;
2726
- const node = this.renderItem(item);
2727
- this.mounted.set(k, node);
2728
- _creatorUI.vlistMount(this, k, node);
2729
- }
2730
- }
2731
- };
2732
- function UIVirtualizedList(config) {
2733
- return new VListElement(config);
2734
- }
2735
-
2736
- // src/ui/router.ts
2737
- var currentScreen = null;
2738
- var onChange = (screen) => {
2739
- currentScreen = screen;
2740
- for (let callback of Router._changeListeners) {
2741
- callback(screen);
2742
- }
2743
- };
2744
- var Router = /* @__PURE__ */ Object.freeze({
2745
- init(homePage, opts = {}) {
2746
- _creatorUI.routerOpen(homePage, onChange, opts.showDefaultBackButton ?? false);
2747
- currentScreen = homePage;
2748
- },
2749
- push(screen) {
2750
- _creatorUI.routerPush(screen);
2751
- },
2752
- replace(screen, opts) {
2753
- _creatorUI.routerReplace(screen, opts?.transition ?? "fade");
2754
- },
2755
- pop(to) {
2756
- _creatorUI.routerPop(to ?? -1);
2757
- },
2758
- hide() {
2759
- _creatorUI.routerHide();
2760
- },
2761
- restore() {
2762
- _creatorUI.routerRestore();
2763
- },
2764
- get current() {
2765
- return currentScreen;
2766
- },
2767
- _changeListeners: [],
2768
- addEventListener(channel, callback) {
2769
- if (channel === "change") {
2770
- this._changeListeners.push(callback);
2771
- }
2772
- },
2773
- removeEventListener(channel, callback) {
2774
- if (channel === "change") {
2775
- let index = this._changeListeners.indexOf(callback);
2776
- if (index >= 0) {
2777
- this._changeListeners.splice(index, 1);
2778
- }
2779
- }
2780
- }
2781
- });
2782
-
2783
- // src/animate/animate.ts
2784
- var animate = (options) => {
2785
- let onRawUpdate;
2786
- if (typeof options.to === "number") {
2787
- const { onUpdate: onUpdate2, from, to } = options;
2788
- onRawUpdate = (t) => onUpdate2(from + (to - from) * t);
2789
- } else if (options.to === "string") {
2790
- const { onUpdate: onUpdate2, from, to } = options;
2791
- onRawUpdate = (t) => onUpdate2(from);
2792
- } else if (options.to.length === 2) {
2793
- const { onUpdate: onUpdate2, from, to } = options;
2794
- const tempVec = new Float32Array(3);
2795
- onRawUpdate = (t) => onUpdate2(lerp2(tempVec, from, to, t));
2796
- } else if (options.to.length === 3) {
2797
- const { onUpdate: onUpdate2, from, to } = options;
2798
- const tempVec = new Float32Array(3);
2799
- onRawUpdate = (t) => onUpdate2(lerp(tempVec, from, to, t));
2800
- } else if (options.to.length === 4) {
2801
- const { onUpdate: onUpdate2, from, to } = options;
2802
- const tempQuat = new Float32Array(4);
2803
- onRawUpdate = (t) => onUpdate2(slerp(tempQuat, from, to, t));
2804
- } else {
2805
- onRawUpdate = () => {
2806
- };
2807
- }
2808
- const animationId = _creatorUtils.animate((val) => {
2809
- const _val = options.easing ? options.easing(val) : val;
2810
- onRawUpdate(_val);
2811
- if (val === 1 && options.onComplete) {
2812
- options.onComplete();
2813
- }
2814
- }, options.duration);
2815
- return animationId;
2816
- };
2817
- var stopAnimation = (id) => _creatorUtils.stopAnimation(id);
2818
- var pauseAnimation = (id) => _creatorUtils.pauseAnimation(id);
2819
- var resumeAnimation = (id) => _creatorUtils.resumeAnimation(id);
2820
- var animateMat4 = (options) => {
2821
- const fromPosition = new Float32Array(3);
2822
- const fromQuaternion = new Float32Array(4);
2823
- const fromScale = new Float32Array(3);
2824
- const toPosition = new Float32Array(3);
2825
- const toQuaternion = new Float32Array(4);
2826
- const toScale = new Float32Array(3);
2827
- decompose(fromQuaternion, fromPosition, fromScale, options.from);
2828
- decompose(toQuaternion, toPosition, toScale, options.to);
2829
- const tempPosition = new Float32Array(3);
2830
- const tempQuat = new Float32Array(4);
2831
- const tempScale = new Float32Array(3);
2832
- const tempMat = new Float32Array(16);
2833
- const onUpdate2 = options.onUpdate;
2834
- const onRawUpdate = (t) => {
2835
- lerp(tempPosition, fromPosition, toPosition, t);
2836
- slerp(tempQuat, fromQuaternion, toQuaternion, t);
2837
- lerp(tempScale, fromScale, toScale, t);
2838
- onUpdate2(fromRotationTranslationScale(tempMat, tempQuat, tempPosition, tempScale));
2839
- };
2840
- const animationId = _creatorUtils.animate((val) => {
2841
- const _val = options.easing ? options.easing(val) : val;
2842
- onRawUpdate(_val);
2843
- if (val === 1 && options.onComplete) {
2844
- options.onComplete();
2845
- }
2846
- }, options.duration);
2847
- return animationId;
2848
- };
2849
- var EPSILON = 1e-6;
2850
- function slerp(out, a2, b2, t) {
2851
- const ax2 = a2[0], ay2 = a2[1], az2 = a2[2], aw = a2[3];
2852
- let bx = b2[0], by = b2[1], bz = b2[2], bw = b2[3];
2853
- let omega, cosom, sinom, scale0, scale1;
2854
- cosom = ax2 * bx + ay2 * by + az2 * bz + aw * bw;
2855
- if (cosom < 0) {
2856
- cosom = -cosom;
2857
- bx = -bx;
2858
- by = -by;
2859
- bz = -bz;
2860
- bw = -bw;
2861
- }
2862
- if (1 - cosom > EPSILON) {
2863
- omega = Math.acos(cosom);
2864
- sinom = Math.sin(omega);
2865
- scale0 = Math.sin((1 - t) * omega) / sinom;
2866
- scale1 = Math.sin(t * omega) / sinom;
2867
- } else {
2868
- scale0 = 1 - t;
2869
- scale1 = t;
2870
- }
2871
- out[0] = scale0 * ax2 + scale1 * bx;
2872
- out[1] = scale0 * ay2 + scale1 * by;
2873
- out[2] = scale0 * az2 + scale1 * bz;
2874
- out[3] = scale0 * aw + scale1 * bw;
2875
- return out;
2876
- }
2877
- function lerp(out, a2, b2, t) {
2878
- const ax2 = a2[0];
2879
- const ay2 = a2[1];
2880
- const az2 = a2[2];
2881
- out[0] = ax2 + t * (b2[0] - ax2);
2882
- out[1] = ay2 + t * (b2[1] - ay2);
2883
- out[2] = az2 + t * (b2[2] - az2);
2884
- return out;
2885
- }
2886
- function lerp2(out, a2, b2, t) {
2887
- const ax2 = a2[0];
2888
- const ay2 = a2[1];
2889
- const az2 = a2[2];
2890
- out[0] = ax2 + t * (b2[0] - ax2);
2891
- out[1] = ay2 + t * (b2[1] - ay2);
2892
- return out;
2893
- }
2894
- function fromRotationTranslationScale(out, q, v, s) {
2895
- const x = q[0], y = q[1], z = q[2], w = q[3];
2896
- const x2 = x + x;
2897
- const y2 = y + y;
2898
- const z2 = z + z;
2899
- const xx = x * x2;
2900
- const xy = x * y2;
2901
- const xz = x * z2;
2902
- const yy = y * y2;
2903
- const yz = y * z2;
2904
- const zz = z * z2;
2905
- const wx = w * x2;
2906
- const wy = w * y2;
2907
- const wz = w * z2;
2908
- const sx = s[0];
2909
- const sy = s[1];
2910
- const sz = s[2];
2911
- out[0] = (1 - (yy + zz)) * sx;
2912
- out[1] = (xy + wz) * sx;
2913
- out[2] = (xz - wy) * sx;
2914
- out[3] = 0;
2915
- out[4] = (xy - wz) * sy;
2916
- out[5] = (1 - (xx + zz)) * sy;
2917
- out[6] = (yz + wx) * sy;
2918
- out[7] = 0;
2919
- out[8] = (xz + wy) * sz;
2920
- out[9] = (yz - wx) * sz;
2921
- out[10] = (1 - (xx + yy)) * sz;
2922
- out[11] = 0;
2923
- out[12] = v[0];
2924
- out[13] = v[1];
2925
- out[14] = v[2];
2926
- out[15] = 1;
2927
- return out;
2928
- }
2929
- function decompose(out_r, out_t, out_s, mat) {
2930
- out_t[0] = mat[12];
2931
- out_t[1] = mat[13];
2932
- out_t[2] = mat[14];
2933
- const m11 = mat[0];
2934
- const m12 = mat[1];
2935
- const m13 = mat[2];
2936
- const m21 = mat[4];
2937
- const m22 = mat[5];
2938
- const m23 = mat[6];
2939
- const m31 = mat[8];
2940
- const m32 = mat[9];
2941
- const m33 = mat[10];
2942
- out_s[0] = Math.hypot(m11, m12, m13);
2943
- out_s[1] = Math.hypot(m21, m22, m23);
2944
- out_s[2] = Math.hypot(m31, m32, m33);
2945
- const is1 = 1 / out_s[0];
2946
- const is2 = 1 / out_s[1];
2947
- const is3 = 1 / out_s[2];
2948
- const sm11 = m11 * is1;
2949
- const sm12 = m12 * is2;
2950
- const sm13 = m13 * is3;
2951
- const sm21 = m21 * is1;
2952
- const sm22 = m22 * is2;
2953
- const sm23 = m23 * is3;
2954
- const sm31 = m31 * is1;
2955
- const sm32 = m32 * is2;
2956
- const sm33 = m33 * is3;
2957
- const trace = sm11 + sm22 + sm33;
2958
- let S = 0;
2959
- if (trace > 0) {
2960
- S = Math.sqrt(trace + 1) * 2;
2961
- out_r[3] = 0.25 * S;
2962
- out_r[0] = (sm23 - sm32) / S;
2963
- out_r[1] = (sm31 - sm13) / S;
2964
- out_r[2] = (sm12 - sm21) / S;
2965
- } else if (sm11 > sm22 && sm11 > sm33) {
2966
- S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
2967
- out_r[3] = (sm23 - sm32) / S;
2968
- out_r[0] = 0.25 * S;
2969
- out_r[1] = (sm12 + sm21) / S;
2970
- out_r[2] = (sm31 + sm13) / S;
2971
- } else if (sm22 > sm33) {
2972
- S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
2973
- out_r[3] = (sm31 - sm13) / S;
2974
- out_r[0] = (sm12 + sm21) / S;
2975
- out_r[1] = 0.25 * S;
2976
- out_r[2] = (sm23 + sm32) / S;
2977
- } else {
2978
- S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
2979
- out_r[3] = (sm12 - sm21) / S;
2980
- out_r[0] = (sm31 + sm13) / S;
2981
- out_r[1] = (sm23 + sm32) / S;
2982
- out_r[2] = 0.25 * S;
2983
- }
2984
- return out_r;
2985
- }
2986
-
2987
- // src/animate/bezier.ts
2988
- var a = (a1, a2) => 1 - 3 * a2 + 3 * a1;
2989
- var b = (a1, a2) => 3 * a2 - 6 * a1;
2990
- var c = (a1) => 3 * a1;
2991
- var calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;
2992
- var getSlope = (t, a1, a2) => 3 * a(a1, a2) * t * t + 2 * b(a1, a2) * t + c(a1);
2993
- var subdivisionPrecision = 1e-7;
2994
- var subdivisionMaxIterations = 10;
2995
- function binarySubdivide(aX, aA, aB, mX1, mX2) {
2996
- let currentX;
2997
- let currentT;
2998
- let i = 0;
2999
- do {
3000
- currentT = aA + (aB - aA) / 2;
3001
- currentX = calcBezier(currentT, mX1, mX2) - aX;
3002
- if (currentX > 0) {
3003
- aB = currentT;
3004
- } else {
3005
- aA = currentT;
3006
- }
3007
- } while (Math.abs(currentX) > subdivisionPrecision && ++i < subdivisionMaxIterations);
3008
- return currentT;
3009
- }
3010
- var newtonIterations = 8;
3011
- var newtonMinSlope = 1e-3;
3012
- function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
3013
- for (let i = 0; i < newtonIterations; ++i) {
3014
- const currentSlope = getSlope(aGuessT, mX1, mX2);
3015
- if (currentSlope === 0) {
3016
- return aGuessT;
3017
- }
3018
- const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
3019
- aGuessT -= currentX / currentSlope;
3020
- }
3021
- return aGuessT;
3022
- }
3023
- var kSplineTableSize = 11;
3024
- var kSampleStepSize = 1 / (kSplineTableSize - 1);
3025
- var linear = (p) => p;
3026
- function cubicBezier(mX1, mY1, mX2, mY2) {
3027
- if (mX1 === mY1 && mX2 === mY2) return linear;
3028
- const sampleValues = new Float32Array(kSplineTableSize);
3029
- for (let i = 0; i < kSplineTableSize; ++i) {
3030
- sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
3031
- }
3032
- function getTForX(aX) {
3033
- let intervalStart = 0;
3034
- let currentSample = 1;
3035
- const lastSample = kSplineTableSize - 1;
3036
- for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
3037
- intervalStart += kSampleStepSize;
3038
- }
3039
- --currentSample;
3040
- const dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
3041
- const guessForT = intervalStart + dist * kSampleStepSize;
3042
- const initialSlope = getSlope(guessForT, mX1, mX2);
3043
- if (initialSlope >= newtonMinSlope) {
3044
- return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
3045
- } else if (initialSlope === 0) {
3046
- return guessForT;
3047
- } else {
3048
- return binarySubdivide(
3049
- aX,
3050
- intervalStart,
3051
- intervalStart + kSampleStepSize,
3052
- mX1,
3053
- mX2
3054
- );
3055
- }
3056
- }
3057
- return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
3058
- }
3059
-
3060
- // src/animate/easings.ts
3061
- var DEFAULT_OVERSHOOT_STRENGTH = 1.525;
3062
- var BOUNCE_FIRST_THRESHOLD = 4 / 11;
3063
- var BOUNCE_SECOND_THRESHOLD = 8 / 11;
3064
- var BOUNCE_THIRD_THRESHOLD = 9 / 10;
3065
- var ca = 4356 / 361;
3066
- var cb = 35442 / 1805;
3067
- var cc = 16061 / 1805;
3068
- function linear2(p) {
3069
- return p;
3070
- }
3071
- function bounceOut(p) {
3072
- if (p === 1 || p === 0) return p;
3073
- const p2 = p * p;
3074
- return p < BOUNCE_FIRST_THRESHOLD ? 7.5625 * p2 : p < BOUNCE_SECOND_THRESHOLD ? 9.075 * p2 - 9.9 * p + 3.4 : p < BOUNCE_THIRD_THRESHOLD ? ca * p2 - cb * p + cc : 10.8 * p * p - 20.52 * p + 10.72;
3075
- }
3076
- function bounceInOut(p) {
3077
- return p < 0.5 ? 0.5 * (1 - bounceOut(1 - p * 2)) : 0.5 * bounceOut(p * 2 - 1) + 0.5;
3078
- }
3079
- function reverseEasing(easing) {
3080
- return (p) => 1 - easing(1 - p);
3081
- }
3082
- function mirrorEasing(easing) {
3083
- return (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
3084
- }
3085
- function createExpoIn(power) {
3086
- return (p) => p ** power;
3087
- }
3088
- function createBackIn(power) {
3089
- return (p) => p * p * ((power + 1) * p - power);
3090
- }
3091
- function createAnticipate(power) {
3092
- const backEasing = createBackIn(power);
3093
- return (p) => (p *= 2) < 1 ? 0.5 * backEasing(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
3094
- }
3095
- function bounceIn(p) {
3096
- return 1 - bounceOut(1 - p);
3097
- }
3098
- function easeIn(p) {
3099
- return p * p;
3100
- }
3101
- function easeOut(p) {
3102
- return 1 - easeIn(1 - p);
3103
- }
3104
- function easeInOut(p) {
3105
- return p <= 0.5 ? easeIn(2 * p) / 2 : (2 - easeIn(2 * (1 - p))) / 2;
3106
- }
3107
- function circIn(p) {
3108
- return 1 - Math.sin(Math.acos(p));
3109
- }
3110
- function circOut(p) {
3111
- return 1 - circIn(1 - p);
3112
- }
3113
- function circInOut(p) {
3114
- return p <= 0.5 ? circOut(2 * p) / 2 : (2 - circOut(2 * (1 - p))) / 2;
3115
- }
3116
- function backIn(p) {
3117
- const power = DEFAULT_OVERSHOOT_STRENGTH;
3118
- return p * p * ((power + 1) * p - power);
3119
- }
3120
- function backOut(p) {
3121
- return 1 - backIn(1 - p);
3122
- }
3123
- function backInOut(p) {
3124
- return p <= 0.5 ? backIn(2 * p) / 2 : (2 - backIn(2 * (1 - p))) / 2;
3125
- }
3126
- function anticipate(p) {
3127
- const doubled = p * 2;
3128
- if (doubled < 1) {
3129
- return 0.5 * backIn(doubled);
3130
- }
3131
- return 0.5 * (2 - Math.pow(2, -10 * (doubled - 1)));
3132
- }
3133
-
3134
- // src/plugins/qr.ts
3135
- var QRScanner = class _QRScanner {
3136
- _onData;
3137
- /** `onData` fires for every value the scanner decodes (null = a frame with no readable code). */
3138
- constructor(onData) {
3139
- this._onData = onData;
3140
- }
3141
- /** Whether this host provides a native QR scanner (the le.codes app does; many hosts don't). */
3142
- static get isSupported() {
3143
- return typeof _creatorUtils.openScanner === "function";
3144
- }
3145
- /** Ask for camera access, then open the native scanner. Rejects if the host can't scan or access is denied. */
3146
- async open() {
3147
- if (!_QRScanner.isSupported) {
3148
- throw new Error("QRScanner isn't available on this host — it needs the le.codes app's native scanner.");
3149
- }
3150
- await new Promise((res, rej) => _creator.requestCamera(res, rej));
3151
- await new Promise((res, rej) => _creatorUtils.openScanner(res, rej, this._onData));
3152
- }
3153
- /** Close the scanner and release the camera. */
3154
- close() {
3155
- _creatorUtils.closeScanner();
3156
- }
3157
- };
3158
-
3159
- // src/gl/Body.ts
3160
- var MOTION = { static: 0, kinematic: 1, dynamic: 2 };
3161
- var Body = class {
3162
- _bodyId = 0;
3163
- _motion;
3164
- constructor(node, options = {}) {
3165
- this._motion = options.motion ?? "dynamic";
3166
- const mass = options.mass ?? 1;
3167
- if (!_creator.physicsHasSupport || !_creator.physicsHasSupport()) return;
3168
- const motion = MOTION[this._motion];
3169
- const shape = resolveShape(node, options.shape ?? { type: "auto" });
3170
- if (shape.type === "sphere") {
3171
- this._bodyId = _creator.physicsCreateSphere(node.id, shape.radius, motion, mass);
3172
- } else if (shape.type === "cylinder") {
3173
- this._bodyId = _creator.physicsCreateCylinder(node.id, shape.halfHeight, shape.radius, motion, mass);
3174
- } else {
3175
- this._bodyId = _creator.physicsCreateBox(node.id, cx(shape.halfExtents), cy(shape.halfExtents), cz(shape.halfExtents), motion, mass);
3176
- }
3177
- }
3178
- /** Native Jolt body id. 0 until assigned, or if the build has no physics support. */
3179
- get id() {
3180
- return this._bodyId;
3181
- }
3182
- get motion() {
3183
- return this._motion;
3184
- }
3185
- /** Linear velocity in world units/second (fresh Vec3 on read). */
3186
- get velocity() {
3187
- const out = new Float32Array(3);
3188
- if (this._bodyId) _creator.physicsGetLinearVelocity(this._bodyId, out);
3189
- return new Vec3(out[0], out[1], out[2]);
3190
- }
3191
- set velocity(v) {
3192
- if (this._bodyId) _creator.physicsSetLinearVelocity(this._bodyId, cx(v), cy(v), cz(v));
3193
- }
3194
- /** Apply an instantaneous impulse (kg·m/s) and wake the body. */
3195
- applyImpulse(v) {
3196
- if (this._bodyId) _creator.physicsApplyImpulse(this._bodyId, cx(v), cy(v), cz(v));
3197
- return this;
3198
- }
3199
- /** Teleport / drive a kinematic (or dynamic) body to a world position. */
3200
- moveTo(p) {
3201
- if (this._bodyId) _creator.physicsSetBodyPosition(this._bodyId, cx(p), cy(p), cz(p));
3202
- return this;
3203
- }
3204
- /** Remove the body from the simulation. */
3205
- remove() {
3206
- if (this._bodyId) {
3207
- _creator.physicsRemoveBody(this._bodyId);
3208
- this._bodyId = 0;
3209
- }
3210
- }
3211
- };
3212
- var resolveShape = (node, shape) => {
3213
- if (shape.type !== "auto") return shape;
3214
- const scale = node.worldScale;
3215
- let hx = 0.5, hy = 0.5, hz = 0.5;
3216
- const geom = node.geometry;
3217
- if (geom) {
3218
- const v = geom.vertices;
3219
- let minX = Infinity, minY = Infinity, minZ = Infinity, maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
3220
- for (let i = 0; i + 2 < v.length; i += 3) {
3221
- if (v[i] < minX) minX = v[i];
3222
- if (v[i] > maxX) maxX = v[i];
3223
- if (v[i + 1] < minY) minY = v[i + 1];
3224
- if (v[i + 1] > maxY) maxY = v[i + 1];
3225
- if (v[i + 2] < minZ) minZ = v[i + 2];
3226
- if (v[i + 2] > maxZ) maxZ = v[i + 2];
3227
- }
3228
- hx = (maxX - minX) * 0.5;
3229
- hy = (maxY - minY) * 0.5;
3230
- hz = (maxZ - minZ) * 0.5;
3231
- }
3232
- return { type: "box", halfExtents: [hx * scale.x, hy * scale.y, hz * scale.z] };
3233
- };
3234
-
3235
- // src/gl/state.ts
3236
- var glState = { activeScene: null };
3237
-
3238
- // src/gl/touch.ts
3239
- var onTouchEnd = (pointerId, clientX, clientY, entityId) => {
3240
- const scene = glState.activeScene;
3241
- if (!scene) return null;
3242
- const ev = new ClickEvent(clientX, clientY, pointerId);
3243
- if (entityId > 0) {
3244
- const node = nodeRegistry.get(entityId) ?? null;
3245
- ev.target = node;
3246
- if (node) node._dispatchEvent("click", ev);
3247
- } else {
3248
- ev.target = null;
3249
- }
3250
- for (const listener of scene._clickListeners.slice()) listener(ev);
3251
- return null;
3252
- };
3253
- var onTouchStart = (pointerId, clientX, clientY, entityId) => {
3254
- const scene = glState.activeScene;
3255
- if (!scene) return null;
3256
- const ev = new TouchStartEvent(clientX, clientY, pointerId);
3257
- if (entityId > 0) {
3258
- const node = nodeRegistry.get(entityId) ?? null;
3259
- ev.target = node;
3260
- if (node) {
3261
- node._dispatchEvent("touchstart", ev);
3262
- if (ev._trackHandler) return ev._trackHandler;
3263
- }
3264
- } else {
3265
- ev.target = null;
3266
- }
3267
- for (const listener of scene._touchStartListeners.slice()) {
3268
- listener(ev);
3269
- if (ev._trackHandler) return ev._trackHandler;
3270
- }
3271
- return null;
3272
- };
3273
- var touchStartRegistered = false;
3274
- var registerTouchStartEvent = () => {
3275
- if (touchStartRegistered) return;
3276
- touchStartRegistered = true;
3277
- _creatorUI.registerTouchStartEvent(onTouchStart);
3278
- };
3279
- var touchEndRegistered = false;
3280
- var registerTouchEndEvent = () => {
3281
- if (touchEndRegistered) return;
3282
- touchEndRegistered = true;
3283
- _creatorUI.registerTouchEndEvent(onTouchEnd);
3284
- };
3285
-
3286
- // src/gl/Node.ts
3287
- var nodeRegistry = new Registry();
3288
- var animatorRegistered = false;
3289
- var ensureAnimator = () => {
3290
- if (animatorRegistered) return;
3291
- animatorRegistered = true;
3292
- _creator.setOnAnimationComplete((entityId, clipIndex) => nodeRegistry.get(entityId)?._onAnimationComplete(clipIndex) ?? true);
3293
- };
3294
- var Node = class _Node extends Emitter {
3295
- /** Native entity handle. */
3296
- id;
3297
- /** Physics handle once .physics() is called. */
3298
- body;
3299
- /** AR anchors only — whether the anchor is currently being tracked. Kept in step with the
3300
- * 'track' / 'untrack' events; `false` for any non-anchor node. */
3301
- isTracked = false;
3302
- _matrix;
3303
- _lastSync = 0;
3304
- _worldMatrix;
3305
- _scaleCache;
3306
- _animPlaying = false;
3307
- _animClip = 0;
3308
- _animSpeed = 1;
3309
- _animLoop = false;
3310
- constructor(internalId) {
3311
- super();
3312
- this.id = internalId ?? _creator.createEntity();
3313
- nodeRegistry.set(this.id, this);
3314
- }
3315
- /** Geometry, if this node is a Mesh (used by physics auto-shape). Overridden by Mesh. */
3316
- get geometry() {
3317
- return void 0;
3318
- }
3319
- // --- name / visibility ---
3320
- get name() {
3321
- return _creator.getName(this.id);
3322
- }
3323
- set name(value) {
3324
- _creator.setName(this.id, value);
3325
- }
3326
- get visible() {
3327
- return _creator.isVisible(this.id);
3328
- }
3329
- set visible(value) {
3330
- _creator.setVisible(this.id, value);
3331
- }
3332
- // --- local transform ---
3333
- // Native owns the authoritative matrix; we keep a Float32Array cache (re-synced lazily because physics
3334
- // can rewrite it each frame). The PUBLIC `matrix` getter hands back a fresh Mat4 *copy* (value
3335
- // semantics — mutating it never touches the node until you assign it back). `_sync()` is the internal
3336
- // hot path that returns the cache directly for cheap component reads.
3337
- _sync() {
3338
- if (!this._matrix) this._matrix = new Float32Array(16);
3339
- if (this._lastSync === 0 || Date.now() - this._lastSync > 10) {
3340
- _creator.getMatrix(this.id, this._matrix);
3341
- this._scaleCache = void 0;
3342
- this._lastSync = Date.now();
3343
- }
3344
- return this._matrix;
3345
- }
3346
- get matrix() {
3347
- return new Mat4(this._sync());
3348
- }
3349
- set matrix(m) {
3350
- if (!this._matrix) this._matrix = new Float32Array(16);
3351
- this._matrix.set(matArr(m));
3352
- this._lastSync = Date.now();
3353
- _creator.setMatrix(this.id, this._matrix);
3354
- }
3355
- get worldMatrix() {
3356
- if (this.parent === null) return this.matrix;
3357
- if (!this._worldMatrix) this._worldMatrix = new Float32Array(16);
3358
- _creator.getWorldMatrix(this.id, this._worldMatrix);
3359
- return new Mat4(this._worldMatrix);
3360
- }
3361
- set worldMatrix(m) {
3362
- if (this.parent === null) {
3363
- this.matrix = m;
3364
- return;
3365
- }
3366
- this._lastSync = 0;
3367
- _creator.setWorldMatrix(this.id, Float32Array.from(matArr(m)));
3368
- }
3369
- get position() {
3370
- const m = this._sync();
3371
- return new Vec3(m[12], m[13], m[14]);
3372
- }
3373
- set position(v) {
3374
- this._lastSync = 0;
3375
- _creator.setPosition(this.id, cx(v), cy(v), cz(v));
3376
- }
3377
- // Scalar position accessors — the loud, correct way to nudge one axis (`node.x = 3`), so nobody reaches
3378
- // for `node.position.x = 3` (a no-op: the getter returns a fresh copy).
3379
- get x() {
3380
- return this._sync()[12];
3381
- }
3382
- set x(v) {
3383
- const m = this._sync();
3384
- this._lastSync = 0;
3385
- _creator.setPosition(this.id, v, m[13], m[14]);
3386
- }
3387
- get y() {
3388
- return this._sync()[13];
3389
- }
3390
- set y(v) {
3391
- const m = this._sync();
3392
- this._lastSync = 0;
3393
- _creator.setPosition(this.id, m[12], v, m[14]);
3394
- }
3395
- get z() {
3396
- return this._sync()[14];
3397
- }
3398
- set z(v) {
3399
- const m = this._sync();
3400
- this._lastSync = 0;
3401
- _creator.setPosition(this.id, m[12], m[13], v);
3402
- }
3403
- get scale() {
3404
- if (this._lastSync === 0 || !this._scaleCache) {
3405
- const m = this._sync();
3406
- this._scaleCache = [Math.hypot(m[0], m[1], m[2]), Math.hypot(m[4], m[5], m[6]), Math.hypot(m[8], m[9], m[10])];
3407
- }
3408
- return new Vec3(this._scaleCache);
3409
- }
3410
- set scale(v) {
3411
- this._lastSync = 0;
3412
- if (typeof v === "number") _creator.setScale(this.id, v, v, v);
3413
- else _creator.setScale(this.id, cx(v), cy(v), cz(v));
3414
- }
3415
- get quaternion() {
3416
- return new Mat4(this._sync()).rotation;
3417
- }
3418
- set quaternion(v) {
3419
- this._lastSync = 0;
3420
- _creator.setQuaternion(this.id, cx(v), cy(v), cz(v), cw(v));
3421
- }
3422
- get eulerAngles() {
3423
- return new Mat4(this._sync()).eulerAngles;
3424
- }
3425
- set eulerAngles(v) {
3426
- this._lastSync = 0;
3427
- _creator.setEulerAngles(this.id, cx(v), cy(v), cz(v), 0);
3428
- }
3429
- // --- world-space reads ---
3430
- get forward() {
3431
- const v = new Float32Array(3);
3432
- _creator.getWorldDirection(this.id, 2, v);
3433
- return new Vec3(-v[0], -v[1], -v[2]);
3434
- }
3435
- get worldPosition() {
3436
- const v = new Float32Array(3);
3437
- _creator.getWorldPosition(this.id, v);
3438
- return new Vec3(v[0], v[1], v[2]);
3439
- }
3440
- get worldScale() {
3441
- return this.worldMatrix.scaling;
3442
- }
3443
- get worldQuaternion() {
3444
- return this.worldMatrix.rotation;
3445
- }
3446
- get worldEulerAngles() {
3447
- return this.worldMatrix.eulerAngles;
3448
- }
3449
- // --- hierarchy ---
3450
- get parent() {
3451
- const id = _creator.getParent(this.id);
3452
- return id === 0 ? null : nodeRegistry.get(id) ?? new _Node(id);
3453
- }
3454
- get children() {
3455
- return _creator.getChildren(this.id).map((id) => nodeRegistry.get(id) ?? new _Node(id));
3456
- }
3457
- get childCount() {
3458
- return _creator.getChildCount(this.id);
3459
- }
3460
- getChild(index) {
3461
- const id = _creator.getChild(this.id, index);
3462
- return id === 0 ? null : nodeRegistry.get(id) ?? new _Node(id);
3463
- }
3464
- /** Parent the given nodes under this one. */
3465
- add(...children) {
3466
- for (const c2 of children) _creator.setParent(c2.id, this.id, false);
3467
- return this;
3468
- }
3469
- setParent(parent, worldPositionStays = false) {
3470
- if (parent === null) _creator.setParentNull(this.id, worldPositionStays);
3471
- else _creator.setParent(this.id, parent.id, worldPositionStays);
3472
- if (worldPositionStays) this._lastSync = 0;
3473
- return this;
3474
- }
3475
- traverse(callback) {
3476
- callback(this);
3477
- _creator.traverse(this.id, (id) => callback(nodeRegistry.get(id) ?? new _Node(id)));
3478
- }
3479
- // --- behaviors ---
3480
- /** Add a physics body (reach it at node.body). Chainable. */
3481
- physics(options = {}) {
3482
- this.body = new Body(this, options);
3483
- return this;
3484
- }
3485
- /** Add a collider for click/touch hit-testing. Derives from the mesh by default. Chainable. */
3486
- collider(options = {}) {
3487
- const form = options.shape ?? "box";
3488
- if (form === "box" && options.center && options.size) {
3489
- _creator.setColliderBox(this.id, cx(options.center), cy(options.center), cz(options.center), cx(options.size), cy(options.size), cz(options.size));
3490
- } else if (form === "sphere" && options.center && options.radius !== void 0) {
3491
- _creator.setColliderSphere(this.id, cx(options.center), cy(options.center), cz(options.center), options.radius);
3492
- } else {
3493
- _creator.setColliderFromMesh(this.id, this.id, form === "sphere" ? 1 : 0);
3494
- }
3495
- return this;
3496
- }
3497
- /** Orient the node so the given axis (default "-z", "forward") points at a world point. */
3498
- lookAt(point, mode = "-z", ortho = [0, 1, 0]) {
3499
- const world = this.worldMatrix;
3500
- const pos = world.position;
3501
- let dir = new Vec3(point).sub(pos);
3502
- const len = dir.length();
3503
- if (len === 0) return this;
3504
- let axis = mode;
3505
- if (mode.startsWith("-")) {
3506
- dir = dir.scale(-1 / len);
3507
- axis = mode.slice(1);
3508
- } else dir = dir.scale(1 / len);
3509
- const o = new Vec3(ortho);
3510
- let forward, right, up;
3511
- if (axis === "x") {
3512
- right = dir;
3513
- up = o.cross(right).normalize();
3514
- forward = up.cross(right).normalize();
3515
- } else if (axis === "y") {
3516
- up = dir;
3517
- right = o.cross(up).normalize();
3518
- forward = up.cross(right).normalize();
3519
- } else {
3520
- forward = dir;
3521
- right = o.cross(dir).normalize();
3522
- up = dir.cross(right);
3523
- }
3524
- const s = world.scaling, w = world.m;
3525
- this.worldMatrix = Float32Array.of(
3526
- right.x * s.x,
3527
- right.y * s.x,
3528
- right.z * s.x,
3529
- 0,
3530
- up.x * s.y,
3531
- up.y * s.y,
3532
- up.z * s.y,
3533
- 0,
3534
- forward.x * s.z,
3535
- forward.y * s.z,
3536
- forward.z * s.z,
3537
- 0,
3538
- w[12],
3539
- w[13],
3540
- w[14],
3541
- w[15]
3542
- );
3543
- return this;
3544
- }
3545
- // --- GLB animation (no-op until a GLB is loaded as this node) ---
3546
- get clips() {
3547
- return _creator.getGlbAnimations(this.id);
3548
- }
3549
- get animationPlaying() {
3550
- return this._animPlaying;
3551
- }
3552
- set animationPlaying(value) {
3553
- this._animPlaying = value;
3554
- _creator.setAnimation(this.id, value, this._animClip, this._animSpeed);
3555
- }
3556
- /** Play a GLB clip by name or index. Chainable. */
3557
- play(clip, options = {}) {
3558
- if (typeof clip === "number") this._animClip = clip;
3559
- else if (typeof clip === "string") {
3560
- const i = this.clips.findIndex((c2) => c2.name === clip);
3561
- if (i >= 0) this._animClip = i;
3562
- }
3563
- if (options.loop !== void 0) this._animLoop = options.loop;
3564
- this.animationPlaying = true;
3565
- ensureAnimator();
3566
- return this;
3567
- }
3568
- stop() {
3569
- this.animationPlaying = false;
3570
- return this;
3571
- }
3572
- get animationLoop() {
3573
- return this._animLoop;
3574
- }
3575
- set animationLoop(v) {
3576
- this._animLoop = v;
3577
- }
3578
- get animationSpeed() {
3579
- return this._animSpeed;
3580
- }
3581
- set animationSpeed(v) {
3582
- this._animSpeed = v;
3583
- _creator.setAnimation(this.id, this._animPlaying, this._animClip, v);
3584
- }
3585
- get animationTime() {
3586
- return _creator.getAnimationTime(this.id);
3587
- }
3588
- set animationTime(v) {
3589
- _creator.setAnimationTime(this.id, this._animClip, v);
3590
- }
3591
- // --- events ---
3592
- addEventListener(channel, callback) {
3593
- if (channel === "click") registerTouchEndEvent();
3594
- else if (channel === "touchstart") registerTouchStartEvent();
3595
- super.addEventListener(channel, callback);
3596
- }
3597
- /** @internal — called by the touch system to deliver a hit. */
3598
- _dispatchEvent(channel, ...args) {
3599
- this.dispatch(channel, ...args);
3600
- }
3601
- /** @internal — host animation-complete callback; returns whether the clip should keep playing. */
3602
- _onAnimationComplete(clipIndex) {
3603
- if (clipIndex !== this._animClip) return true;
3604
- if (!this._animLoop) {
3605
- this._animPlaying = false;
3606
- this.dispatch("completed", clipIndex);
3607
- } else this.dispatch("loopReached", clipIndex);
3608
- return this._animPlaying;
3609
- }
3610
- destroy() {
3611
- nodeRegistry.delete(this.id);
3612
- _creator.destroyEntity(this.id);
3613
- }
3614
- };
3615
- var matArr = (m) => m.m ?? m;
3616
-
3617
- // src/gl/Ray.ts
3618
- var Ray = class {
3619
- origin;
3620
- dir;
3621
- constructor(origin, dir) {
3622
- this.origin = new Vec3(origin);
3623
- this.dir = new Vec3(dir);
3624
- }
3625
- /** The world-space point origin + t·dir. */
3626
- getPoint(t) {
3627
- return this.origin.scaleAndAdd(this.dir, t);
3628
- }
3629
- };
3630
-
3631
- // src/gl/Camera.ts
3632
- var Camera = class extends Node {
3633
- _sceneId = -1;
3634
- /** Live projection matrix (updated by the host when the viewport/fov changes). */
3635
- projectionMatrix = new Float32Array(16);
3636
- /** @internal — Scene wires the camera to its native scene id + projection callback. */
3637
- _attach(sceneId) {
3638
- this._sceneId = sceneId;
3639
- _creator.attachCameraToAR(sceneId, this.id, (mat) => {
3640
- for (let i = 0; i < 16; i++) this.projectionMatrix[i] = mat[i];
3641
- });
3642
- }
3643
- get fov() {
3644
- return _creator.getCameraFov(this._sceneId, 0);
3645
- }
3646
- get horizontalFov() {
3647
- return _creator.getCameraFov(this._sceneId, 1);
3648
- }
3649
- get displaySize() {
3650
- const s = _creator.getDisplaySize();
3651
- return [s[0], s[1]];
3652
- }
3653
- /** World-space ray direction through a screen pixel. */
3654
- getViewDirection(screenX, screenY) {
3655
- if (this._sceneId < 0) throw new Error("Camera is not attached to a scene");
3656
- const v = new Float32Array(3);
3657
- _creator.getCameraViewDirection(this._sceneId, screenX, screenY, v);
3658
- return new Vec3(v[0], v[1], v[2]);
3659
- }
3660
- /** A pick ray from the camera through a screen pixel. */
3661
- getRay(screenX, screenY) {
3662
- return new Ray(this.worldPosition, this.getViewDirection(screenX, screenY));
3663
- }
3664
- };
3665
-
3666
- // src/gl/controls.ts
3667
- var clamp2 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
3668
- var attachControls = (scene, target, options = {}) => {
3669
- const minScale = options.minScale ?? 0.08;
3670
- const maxScale = options.maxScale ?? 2;
3671
- const pan = options.pan ?? true;
3672
- const pinch = options.pinch ?? true;
3673
- const twist = options.twist ?? true;
3674
- const active = [];
3675
- const onTouchStart2 = (e) => {
3676
- const touch = { pointerId: e.pointerId, x: e.clientX, y: e.clientY };
3677
- active.push(touch);
3678
- e.track({
3679
- onMove(m) {
3680
- if (active.length === 2) {
3681
- const other = active.find((t) => t !== touch);
3682
- if (other) {
3683
- const axis = new Vec2(other.x - touch.x, other.y - touch.y);
3684
- const span = axis.length();
3685
- if (span > 1e-3) {
3686
- const dir = axis.scale(1 / span);
3687
- const delta = new Vec2(m.deltaX, m.deltaY);
3688
- if (pinch) {
3689
- const along = -dir.dot(delta);
3690
- target.scale = clamp2(target.scale.x * (span + along) / span, minScale * 0.4, maxScale * 1.5);
3691
- }
3692
- if (twist) {
3693
- const tangent = dir.perp().dot(delta);
3694
- target.quaternion = target.quaternion.mul(Quat.fromAxisAngle(Vec3.up, Math.atan2(tangent, span)));
3695
- }
3696
- }
3697
- }
3698
- } else if (active.length === 1 && pan) {
3699
- let dir = scene.camera.forward;
3700
- const parent = target.parent;
3701
- if (parent) dir = dir.rotate(parent.quaternion.invert());
3702
- dir = dir.withY(0);
3703
- const k = Math.hypot(dir.x, dir.z);
3704
- const screen = Math.max(...scene.camera.displaySize);
3705
- if (k > 1e-4 && screen > 0) {
3706
- const dist = target.worldPosition.distanceTo(scene.camera.worldPosition);
3707
- const right = new Vec3(-dir.z, 0, dir.x).scale(m.deltaX / k / screen * dist);
3708
- const fwd = dir.scale(-m.deltaY / k / screen * dist);
3709
- const move = right.add(fwd);
3710
- const p = target.position;
3711
- target.position = new Vec3(p.x + move.x, p.y, p.z + move.z);
3712
- }
3713
- }
3714
- touch.x = m.clientX;
3715
- touch.y = m.clientY;
3716
- },
3717
- onEnd() {
3718
- const i = active.indexOf(touch);
3719
- if (i >= 0) active.splice(i, 1);
3720
- }
3721
- });
3722
- };
3723
- scene.addEventListener("touchstart", onTouchStart2);
3724
- return { remove() {
3725
- scene.removeEventListener("touchstart", onTouchStart2);
3726
- active.length = 0;
3727
- } };
3728
- };
3729
-
3730
- // src/gl/Material.ts
3731
- var applyUniform = (id, key, value) => {
3732
- if (typeof value === "string") {
3733
- if (value.length > 7) _creator.setUniformRgba(id, key, parseInt(value.slice(1), 16));
3734
- else _creator.setUniformRgb(id, key, parseInt(value.slice(1), 16));
3735
- } else if (typeof value === "number") {
3736
- _creator.setUniformFloat(id, key, value);
3737
- } else if (typeof value === "boolean") {
3738
- _creator.setUniformBoolean(id, key, value);
3739
- } else if (value instanceof Texture) {
3740
- _creator.setUniformTexture(id, key, value._id, value.wrapS, value.wrapT);
3741
- } else if (value instanceof Float32Array) {
3742
- _creator.setUniformArray(id, key, value);
3743
- } else if (Array.isArray(value)) {
3744
- _creator.setUniformArray(id, key, Float32Array.of(...value));
3745
- } else {
3746
- _creator.setUniformNull(id, key);
3747
- }
3748
- };
3749
- var Material = class _Material {
3750
- /** @internal native material-instance handle. */
3751
- _id;
3752
- shader;
3753
- uniforms;
3754
- _colorKey = null;
3755
- _useMapFlag = true;
3756
- constructor(shader) {
3757
- if (typeof shader === "number") {
3758
- this.shader = "unknown";
3759
- this._id = shader;
3760
- } else {
3761
- this.shader = shader;
3762
- this._id = _creator.createMaterial(shader._id);
3763
- }
3764
- const store = {};
3765
- const id = () => this._id;
3766
- this.uniforms = new Proxy(store, {
3767
- get: (t, k) => t[k],
3768
- set: (t, k, v) => {
3769
- t[k] = v;
3770
- applyUniform(id(), k, v);
3771
- return true;
3772
- }
3773
- });
3774
- }
3775
- /** Set a uniform (chainable). */
3776
- set(key, value) {
3777
- this.uniforms[key] = value;
3778
- return this;
3779
- }
3780
- set color(c2) {
3781
- if (this._colorKey) this.uniforms[this._colorKey] = Color.toHexString(c2);
3782
- }
3783
- set map(value) {
3784
- if (value === null) {
3785
- if (this._useMapFlag) this.uniforms.useBaseColorMap = false;
3786
- } else {
3787
- this.uniforms.baseColorMap = value;
3788
- if (this._useMapFlag) this.uniforms.useBaseColorMap = true;
3789
- }
3790
- }
3791
- // --- factories ---
3792
- /** PBR lit material. */
3793
- static lit(options = {}) {
3794
- const m = new _Material({ _id: _creatorUtils.fetchLocal("lit.filamat") });
3795
- m._colorKey = "baseColor";
3796
- if (options.color !== void 0) m.color = options.color;
3797
- if (options.map) m.map = options.map;
3798
- return m;
3799
- }
3800
- /** Unlit (flat) material. */
3801
- static unlit(options = {}) {
3802
- const m = new _Material({ _id: _creatorUtils.fetchLocal("unlit.filamat") });
3803
- m._colorKey = "baseColor";
3804
- if (options.color !== void 0) m.color = options.color;
3805
- if (options.map) m.map = options.map;
3806
- return m;
3807
- }
3808
- /** Material that samples a VideoPlayer's texture. */
3809
- static video(map) {
3810
- const m = new _Material({ _id: _creatorUtils.fetchLocal("video.filamat") });
3811
- m._useMapFlag = false;
3812
- if (map) m.uniforms.baseColorMap = map;
3813
- return m;
3814
- }
3815
- /** Shadow-catcher material (transparent except where shadows fall). */
3816
- static shadow(color = "#000000aa") {
3817
- const m = new _Material({ _id: _creatorUtils.fetchLocal("shadow.filamat") });
3818
- m._colorKey = "color";
3819
- m.color = color;
3820
- return m;
3821
- }
3822
- /** Load a custom compiled shader (.mat URL) as a material. */
3823
- static load(url) {
3824
- return fetch(url, { useOnce: true }).then((resp) => {
3825
- if (resp.status >= 400) return Promise.reject(new Error(`Failed to fetch shader from ${url}. HTTP ${resp.status}`));
3826
- return new _Material(resp);
3827
- });
3828
- }
3829
- /** @internal */
3830
- static idOf(material) {
3831
- return material._id;
3832
- }
3833
- };
3834
-
3835
- // src/gl/Scene.ts
3836
- var Scene = class _Scene {
3837
- /** @internal native scene handle. */
3838
- _id;
3839
- camera;
3840
- /** @internal touch listeners, read by the dispatch system. */
3841
- _clickListeners = [];
3842
- _touchStartListeners = [];
3843
- _material;
3844
- static _active = null;
3845
- static get active() {
3846
- return _Scene._active;
3847
- }
3848
- constructor(options = {}) {
3849
- const parent = options._parent;
3850
- if (parent) {
3851
- this.camera = parent.camera;
3852
- this._id = _creator.createOverlayScene(parent._id);
3853
- } else {
3854
- this.camera = new Camera();
3855
- this._id = _creator.createScene();
3856
- }
3857
- this.camera._attach(this._id);
3858
- if (options.ibl !== false) _creator.setDefaultIbl(this._id, options.environmentIntensity ?? 2e4);
3859
- if (options.bloom) _creator.setBloomOptions(this._id, true, options.bloomIntensity ?? 0.2, 1);
3860
- if (options.skybox !== void 0) _creator.setSkybox(this._id, Color.toPackedRgb(options.skybox));
3861
- if (options.antialias) _creator.setSceneMultiSampleAntiAliasing(this._id, true, 4);
3862
- }
3863
- /** Scene occlusion material. */
3864
- get occlusionMaterial() {
3865
- if (!this._material) this._material = new Material(_creator.getSceneMaterial(this._id));
3866
- return this._material;
3867
- }
3868
- set skybox(color) {
3869
- _creator.setSkybox(this._id, Color.toPackedRgb(color));
3870
- }
3871
- setMaterialGlobalParameter(i, x, y, z, w) {
3872
- _creator.setMaterialGlobalParameter(this._id, i, x, y, z, w);
3873
- }
3874
- setAntialias(enabled, scale = 4) {
3875
- _creator.setSceneMultiSampleAntiAliasing(this._id, enabled, scale);
3876
- }
3877
- add(...nodes) {
3878
- for (const n of nodes) _creator.addEntityToScene(this._id, n.id);
3879
- return this;
3880
- }
3881
- remove(...nodes) {
3882
- for (const n of nodes) _creator.removeEntityFromScene(this._id, n.id);
3883
- return this;
3884
- }
3885
- createOverlay(options = {}) {
3886
- return new _Scene({ ...options, _parent: this });
3887
- }
3888
- warmRender() {
3889
- return new Promise((res) => _creator.warmRender(this._id, res));
3890
- }
3891
- /** Make this the active scene (brings up the 3D engine; closes any other). */
3892
- open() {
3893
- _creator.openScene(this._id);
3894
- _Scene._active = this;
3895
- glState.activeScene = this;
3896
- }
3897
- close() {
3898
- _creator.closeScene();
3899
- if (_Scene._active === this) _Scene._active = null;
3900
- if (glState.activeScene === this) glState.activeScene = null;
3901
- }
3902
- addEventListener(channel, callback) {
3903
- if (channel === "click") {
3904
- if (!this._clickListeners.includes(callback)) {
3905
- this._clickListeners.push(callback);
3906
- registerTouchEndEvent();
3907
- }
3908
- } else if (channel === "touchstart") {
3909
- if (!this._touchStartListeners.includes(callback)) {
3910
- this._touchStartListeners.push(callback);
3911
- registerTouchStartEvent();
3912
- }
3913
- }
3914
- }
3915
- removeEventListener(channel, callback) {
3916
- const list = channel === "click" ? this._clickListeners : this._touchStartListeners;
3917
- const i = list.indexOf(callback);
3918
- if (i >= 0) list.splice(i, 1);
3919
- }
3920
- };
3921
- var onTrackStateChange = (entityId, track) => {
3922
- const node = nodeRegistry.get(entityId);
3923
- if (!node) return;
3924
- node.isTracked = track;
3925
- if (track) node._dispatchEvent("track");
3926
- else node._dispatchEvent("untrack");
3927
- };
3928
- var ARScene = class extends Scene {
3929
- mode;
3930
- useWarmRender = true;
3931
- _root;
3932
- constructor(options = {}) {
3933
- super(options);
3934
- this.mode = options.mode ?? "default";
3935
- _creator.createARController(this._id, this.camera.id, this.mode, onTrackStateChange);
3936
- }
3937
- /** Root anchor node (not available in marker mode). */
3938
- get root() {
3939
- if (this._root) return this._root;
3940
- this._root = new Node(_creator.createRootAnchor(this._id));
3941
- return this._root;
3942
- }
3943
- /** Create an anchor from a tracking source (e.g. an image target). */
3944
- createAnchor(source, physicalWidth = 0.2) {
3945
- return new Node(_creator.createAnchor(this._id, physicalWidth, source._id));
3946
- }
3947
- /**
3948
- * Let the user place & manipulate a node with touch gestures — the staple AR object-placement
3949
- * interaction: ONE finger drags it across the floor (relative to where the camera looks), TWO fingers
3950
- * pinch to scale and twist to rotate. Returns a handle; call `.remove()` to detach. Tunable via
3951
- * options (min/max scale, and toggles for `pan` / `pinch` / `twist`).
3952
- */
3953
- addControls(target, options = {}) {
3954
- return attachControls(this, target, options);
3955
- }
3956
- async open() {
3957
- await new Promise((res, rej) => _creator.requestCamera(res, rej));
3958
- if (this.useWarmRender) {
3959
- _creator.createGLView();
3960
- await this.warmRender();
3961
- }
3962
- await new Promise((res, rej) => _creator.launchAR(this._id, res, rej));
3963
- super.open();
3964
- }
3965
- close() {
3966
- _creator.stopAR();
3967
- super.close();
3968
- }
3969
- };
3970
-
3971
- // src/gl/Geometry.ts
3972
- var cross = (a2, b2) => [a2[1] * b2[2] - a2[2] * b2[1], a2[2] * b2[0] - a2[0] * b2[2], a2[0] * b2[1] - a2[1] * b2[0]];
3973
- var normalize = (a2) => {
3974
- const l = Math.hypot(a2[0], a2[1], a2[2]);
3975
- return l === 0 ? [0, 0, 0] : [a2[0] / l, a2[1] / l, a2[2] / l];
3976
- };
3977
- var Geometry = class {
3978
- vertices;
3979
- normals;
3980
- indices;
3981
- uv;
3982
- /** @internal native mesh-type enum (0 triangles / 1 edges / 2 vertices). */
3983
- _meshKind = 0;
3984
- constructor(vertices, normals, indices, uv) {
3985
- this.vertices = vertices;
3986
- this.normals = normals;
3987
- this.indices = indices;
3988
- this.uv = uv;
3989
- }
3990
- translate(x, y, z) {
3991
- for (let i = 0; i < this.vertices.length; i += 3) {
3992
- this.vertices[i] += x;
3993
- this.vertices[i + 1] += y;
3994
- this.vertices[i + 2] += z;
3995
- }
3996
- return this;
3997
- }
3998
- scale(x, y, z) {
3999
- for (let i = 0; i < this.vertices.length; i += 3) {
4000
- this.vertices[i] *= x;
4001
- this.vertices[i + 1] *= y;
4002
- this.vertices[i + 2] *= z;
4003
- }
4004
- return this;
4005
- }
4006
- scaleUV(x, y) {
4007
- for (let i = 0; i < this.uv.length; i += 2) {
4008
- this.uv[i] *= x;
4009
- this.uv[i + 1] *= y;
4010
- }
4011
- return this;
4012
- }
4013
- set kind(kind) {
4014
- this._meshKind = kind === "edges" ? 1 : kind === "vertices" ? 2 : 0;
4015
- }
4016
- static box() {
4017
- return createCube();
4018
- }
4019
- static sphere(options) {
4020
- return createSphere(options);
4021
- }
4022
- static cylinder(options) {
4023
- return createCylinder(options);
4024
- }
4025
- static plane(options) {
4026
- return createPlane(options);
4027
- }
4028
- };
4029
- var createCube = () => {
4030
- const vertices = new Float32Array(6 * 4 * 3);
4031
- const normals = new Float32Array(6 * 4 * 3);
4032
- const uv = new Float32Array(6 * 4 * 3);
4033
- const indices = new Uint16Array(6 * 6);
4034
- let cnt = 0, indicesCnt = 0;
4035
- const sides = [-0.5, 0.5];
4036
- for (let i = 0; i < 6; i++) {
4037
- const s = cnt;
4038
- const side = i % 2 === 0 ? -0.5 : 0.5;
4039
- for (const a2 of sides) {
4040
- for (const b2 of sides) {
4041
- uv.set([a2 + 0.5, b2 + 0.5], cnt * 2);
4042
- if (i === 0 || i === 1) {
4043
- vertices.set([side, b2, -a2], cnt * 3);
4044
- normals.set([side * 2, 0, 0], cnt * 3);
4045
- }
4046
- if (i === 2 || i === 3) {
4047
- vertices.set([a2, side, -b2], cnt * 3);
4048
- normals.set([0, side * 2, 0], cnt * 3);
4049
- }
4050
- if (i === 4 || i === 5) {
4051
- vertices.set([a2, b2, side], cnt * 3);
4052
- normals.set([0, 0, side * 2], cnt * 3);
4053
- }
4054
- cnt++;
4055
- }
4056
- }
4057
- const s1 = s + 1, s2 = s + 2, s3 = s + 3;
4058
- if (i % 2 === 0) indices.set([s, s1, s2, s3, s2, s1], indicesCnt);
4059
- else indices.set([s2, s1, s, s1, s2, s3], indicesCnt);
4060
- indicesCnt += 6;
4061
- }
4062
- return new Geometry(vertices, normals, indices, uv);
4063
- };
4064
- var createSphere = (options = {}) => {
4065
- const { radius = 0.5, widthSegments = 32, heightSegments = 32 } = options;
4066
- const verticesCount = (widthSegments + 1) * (heightSegments - 1) + widthSegments * 2;
4067
- const vertices = new Float32Array(verticesCount * 3);
4068
- const normals = new Float32Array(verticesCount * 3);
4069
- const uvs = new Float32Array(verticesCount * 2);
4070
- const indices = new Uint16Array(widthSegments * (heightSegments - 2) * 6 + widthSegments * 3 * 2);
4071
- const phiLength = Math.PI * 2, thetaLength = Math.PI;
4072
- let vertexCnt = 0, indicesCnt = 0;
4073
- for (let iy = 1; iy < heightSegments; iy++) {
4074
- const v = iy / heightSegments;
4075
- for (let ix = 0; ix <= widthSegments; ix++) {
4076
- const u = ix / widthSegments;
4077
- const point = [
4078
- -Math.cos(u * phiLength) * Math.sin(v * thetaLength),
4079
- Math.cos(v * thetaLength),
4080
- Math.sin(u * phiLength) * Math.sin(v * thetaLength)
4081
- ];
4082
- vertices.set([point[0] * radius, point[1] * radius, point[2] * radius], vertexCnt * 3);
4083
- normals.set(point, vertexCnt * 3);
4084
- uvs.set([u, 1 - v], vertexCnt * 2);
4085
- if (iy > 1 && ix > 0) {
4086
- const s = vertexCnt - 1;
4087
- const s1 = vertexCnt - (widthSegments + 1) - 1;
4088
- indices.set([s + 1, s1, s, s1, s + 1, s1 + 1], indicesCnt);
4089
- indicesCnt += 6;
4090
- }
4091
- vertexCnt++;
4092
- }
4093
- }
4094
- const sides = [-radius, radius];
4095
- const circleStart2 = vertexCnt - (widthSegments + 1);
4096
- for (const side of sides) {
4097
- const circleStart = side > 0 ? 0 : circleStart2;
4098
- for (let ix = 0; ix < widthSegments; ix++) {
4099
- const u = (ix + 0.5) / widthSegments;
4100
- vertices.set([0, side, 0], vertexCnt * 3);
4101
- normals.set([0, side < 0 ? -1 : 1, 0], vertexCnt * 3);
4102
- uvs.set([u, side < 0 ? 0 : 1], vertexCnt * 2);
4103
- const s = circleStart + ix;
4104
- if (side > 0) indices.set([s, s + 1, vertexCnt], indicesCnt);
4105
- else indices.set([vertexCnt, s + 1, s], indicesCnt);
4106
- indicesCnt += 3;
4107
- vertexCnt++;
4108
- }
4109
- }
4110
- return new Geometry(vertices, normals, indices, uvs);
4111
- };
4112
- var createCylinder = (options = {}) => {
4113
- const { edges = 32, smooth = true, radius = 0.5, radiusTop = radius, radiusBottom = radius } = options;
4114
- const verticesCount = edges * 4 + (edges + 1) * 2;
4115
- const vertices = new Float32Array(verticesCount * 3);
4116
- const normals = new Float32Array(verticesCount * 3);
4117
- const uv = new Float32Array(verticesCount * 2);
4118
- const indices = new Uint16Array(edges * 6 + edges * 6);
4119
- const sides = [-0.5, 0.5];
4120
- let cnt = 0, indicesCnt = 0;
4121
- for (let i = 0; i < edges; i++) {
4122
- const s = cnt;
4123
- const normalX = Math.cos((i + 0.5) / edges * Math.PI * 2);
4124
- const normalY = Math.sin((i + 0.5) / edges * Math.PI * 2);
4125
- for (let j = 0; j < 2; j++) {
4126
- const x = Math.cos((i + j) / edges * Math.PI * 2);
4127
- const y = Math.sin((i + j) / edges * Math.PI * 2);
4128
- for (const side of sides) {
4129
- if (side > 0) vertices.set([x * radiusTop, side, y * radiusTop], cnt * 3);
4130
- else vertices.set([x * radiusBottom, side, y * radiusBottom], cnt * 3);
4131
- if (smooth) normals.set([x, 0, y], cnt * 3);
4132
- else normals.set([normalX, 0, normalY], cnt * 3);
4133
- uv.set([(i + j) / edges, side + 0.5], cnt * 2);
4134
- cnt++;
4135
- }
4136
- }
4137
- const s1 = s + 1, s2 = (s + 2) % (edges * 4), s3 = (s + 3) % (edges * 4);
4138
- indices.set([s, s1, s2, s3, s2, s1], indicesCnt);
4139
- indicesCnt += 6;
4140
- }
4141
- for (const side of sides) {
4142
- const center = cnt;
4143
- vertices.set([0, side, 0], cnt * 3);
4144
- normals.set([0, side * 2, 0], cnt * 3);
4145
- cnt++;
4146
- for (let i = 0; i < edges; i++) {
4147
- const x = Math.cos(i / edges * Math.PI * 2), y = Math.sin(i / edges * Math.PI * 2);
4148
- if (side > 0) vertices.set([x * radiusTop, side, y * radiusTop], cnt * 3);
4149
- else vertices.set([x * radiusBottom, side, y * radiusBottom], cnt * 3);
4150
- normals.set([0, side * 2, 0], cnt * 3);
4151
- if (side < 0) indices.set([center, center + i + 1, center + 1 + (i + 1) % edges], indicesCnt);
4152
- else indices.set([center + 1 + (i + 1) % edges, center + i + 1, center], indicesCnt);
4153
- indicesCnt += 3;
4154
- cnt++;
4155
- }
4156
- }
4157
- return new Geometry(vertices, normals, indices, uv);
4158
- };
4159
- var createPlane = (options = {}) => {
4160
- const normal = options.normal ? [cx(options.normal), cy(options.normal), cz(options.normal)] : [0, 0, 1];
4161
- const vertices = new Float32Array(4 * 3);
4162
- const normals = new Float32Array(4 * 3);
4163
- const uvs = new Float32Array(4 * 2);
4164
- const sides = [-0.5, 0.5];
4165
- let cnt = 0;
4166
- let upVector, rightVec;
4167
- if (normal[1] === 0 && normal[2] === 0) {
4168
- upVector = [0, 1, 0];
4169
- rightVec = [0, 0, normal[0]];
4170
- } else if (normal[0] === 0 && normal[2] === 0) {
4171
- upVector = [0, 0, 1];
4172
- rightVec = [normal[1], 0, 0];
4173
- } else if (normal[0] === 0 && normal[1] === 0) {
4174
- upVector = [0, 1, 0];
4175
- rightVec = [normal[2], 0, 0];
4176
- } else {
4177
- const temp = Math.abs(normal[0]) < 0.5 ? [1, 0, 0] : [0, 0, 1];
4178
- upVector = normalize(cross(temp, normal));
4179
- rightVec = normalize(cross(upVector, normal));
4180
- }
4181
- for (const a2 of sides) {
4182
- for (const b2 of sides) {
4183
- normals.set(normal, cnt * 3);
4184
- uvs.set([a2 + 0.5, b2 + 0.5], cnt * 2);
4185
- vertices.set([
4186
- rightVec[0] * a2 + upVector[0] * b2,
4187
- rightVec[1] * a2 + upVector[1] * b2,
4188
- rightVec[2] * a2 + upVector[2] * b2
4189
- ], cnt * 3);
4190
- cnt++;
4191
- }
4192
- }
4193
- const indices = new Uint16Array([2, 1, 0, 1, 2, 3]);
4194
- return new Geometry(vertices, normals, indices, uvs);
4195
- };
4196
-
4197
- // src/gl/Mesh.ts
4198
- var Mesh = class _Mesh extends Node {
4199
- _geometry;
4200
- _materials = [];
4201
- constructor(geometry, material) {
4202
- super();
4203
- if (geometry) {
4204
- const mat = material ?? Material.unlit();
4205
- this._geometry = geometry;
4206
- this._materials = [mat];
4207
- _creator.setMesh(this.id, Material.idOf(mat), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind);
4208
- }
4209
- }
4210
- get geometry() {
4211
- return this._geometry;
4212
- }
4213
- get material() {
4214
- return this._materials[0];
4215
- }
4216
- set material(m) {
4217
- this.setMaterial(m, 0);
4218
- }
4219
- setMaterial(material, index = 0) {
4220
- this._materials[index] = material;
4221
- _creator.setMaterial(this.id, Material.idOf(material), index);
4222
- return this;
4223
- }
4224
- getMaterial(index = 0) {
4225
- const existing = this._materials[index];
4226
- if (existing) return existing;
4227
- const id = _creator.getMaterial(this.id, index);
4228
- if (id === 0) return null;
4229
- const mat = new Material(id);
4230
- this._materials[index] = mat;
4231
- return mat;
4232
- }
4233
- set culling(v) {
4234
- _creator.setCulling(this.id, v);
4235
- }
4236
- set castShadows(v) {
4237
- _creator.setCastShadows(this.id, v);
4238
- }
4239
- set receiveShadows(v) {
4240
- _creator.setReceiveShadows(this.id, v);
4241
- }
4242
- // --- factories ---
4243
- static box(options = {}) {
4244
- const geo = Geometry.box();
4245
- const [sx, sy, sz] = sizeToVec(options.size ?? 1);
4246
- if (sx !== 1 || sy !== 1 || sz !== 1) geo.scale(sx, sy, sz);
4247
- return apply(new _Mesh(geo, options.material), options);
4248
- }
4249
- static sphere(options = {}) {
4250
- return apply(new _Mesh(Geometry.sphere(options), options.material), options);
4251
- }
4252
- static cylinder(options = {}) {
4253
- return apply(new _Mesh(Geometry.cylinder(options), options.material), options);
4254
- }
4255
- static plane(options = {}) {
4256
- return apply(new _Mesh(Geometry.plane(options), options.material), options);
4257
- }
4258
- /** Wrap a custom Geometry. */
4259
- static from(geometry, options = {}) {
4260
- return apply(new _Mesh(geometry, options.material), options);
4261
- }
4262
- /**
4263
- * Load a GLB model. Returns the model's root Node (a GLB is a node hierarchy); its animation clips
4264
- * are available via node.clips and driven with node.play(name|index).
4265
- */
4266
- static load(source, options = {}) {
4267
- if (typeof source === "string") {
4268
- return fetch(source, { useOnce: true, onProgress: options.onProgress }).then((resp) => {
4269
- if (resp.status >= 400) return Promise.reject(new Error(`Failed to load GLB from ${source}. HTTP ${resp.status}`));
4270
- return _Mesh.load(resp, options);
4271
- });
4272
- }
4273
- return new Promise((resolve, reject) => {
4274
- _creator.createGlb(source._id, (entityId) => {
4275
- if (entityId === 0) {
4276
- reject(new Error("Failed to load GLB"));
4277
- return;
4278
- }
4279
- const node = new Node(entityId);
4280
- if (options.culling !== true) _creator.setGlbCulling(entityId, false);
4281
- resolve(node);
4282
- }, reject);
4283
- });
4284
- }
4285
- };
4286
- var sizeToVec = (s) => typeof s === "number" ? [s, s, s] : [cx(s), cy(s), cz(s)];
4287
- var apply = (mesh, o) => {
4288
- if (o.position) mesh.position = o.position;
4289
- if (o.eulerAngles) mesh.eulerAngles = o.eulerAngles;
4290
- if (o.scale !== void 0) mesh.scale = typeof o.scale === "number" ? [o.scale, o.scale, o.scale] : o.scale;
4291
- if (o.name !== void 0) mesh.name = o.name;
4292
- if (o.castShadows !== void 0) mesh.castShadows = o.castShadows;
4293
- if (o.receiveShadows !== void 0) mesh.receiveShadows = o.receiveShadows;
4294
- return mesh;
4295
- };
4296
-
4297
- // src/gl/Light.ts
4298
- var Light = class _Light extends Node {
4299
- /** A directional sun light. */
4300
- static sun(options = {}) {
4301
- const light = new _Light();
4302
- const [dx, dy, dz] = options.direction ?? [0.548267, -0.473983, -0.689016];
4303
- _creator.createSunLight(
4304
- light.id,
4305
- dx,
4306
- dy,
4307
- dz,
4308
- options.intensity ?? 1e5,
4309
- Color.toPackedRgb(options.color ?? 16777215),
4310
- options.shadowsQuality ?? 1
4311
- );
4312
- return light;
4313
- }
4314
- };
4315
-
4316
- // src/gl/Plane.ts
4317
- var Plane = class _Plane {
4318
- /** Unit normal. */
4319
- normal;
4320
- /** Scalar offset: dot(normal, point). */
4321
- d;
4322
- constructor(normal, point) {
4323
- this.normal = new Vec3(normal).normalize();
4324
- this.d = this.normal.dot(point);
4325
- }
4326
- static fromPoints(a2, b2, c2) {
4327
- const A = new Vec3(a2);
4328
- return new _Plane(new Vec3(b2).sub(A).cross(new Vec3(c2).sub(A)), a2);
4329
- }
4330
- static fromCoefficients(a2, b2, c2, d) {
4331
- const len = Math.hypot(a2, b2, c2);
4332
- const l2 = len * len;
4333
- return new _Plane([a2, b2, c2], [a2 * d / l2, b2 * d / l2, c2 * d / l2]);
4334
- }
4335
- /** Ray-plane parameter t (≥0), or null on a miss (parallel / behind origin). */
4336
- intersectRay(ray) {
4337
- const denom = this.normal.dot(ray.dir);
4338
- if (Math.abs(denom) < 1e-8) return null;
4339
- const t = (this.d - this.normal.dot(ray.origin)) / denom;
4340
- return t < 0 ? null : t;
4341
- }
4342
- /** Like intersectRay but allows hits behind the origin (line-plane). */
4343
- intersectLine(ray) {
4344
- const denom = this.normal.dot(ray.dir);
4345
- if (Math.abs(denom) < 1e-8) return null;
4346
- return (this.d - this.normal.dot(ray.origin)) / denom;
4347
- }
4348
- /** Signed distance from a point (positive on the normal's side). */
4349
- signedDistance(point) {
4350
- return this.normal.dot(point) - this.d;
4351
- }
4352
- /** Project a point onto the plane. */
4353
- projectPoint(point) {
4354
- return new Vec3(point).scaleAndAdd(this.normal, -this.signedDistance(point));
4355
- }
4356
- };
4357
-
4358
- // src/gl/Noise.ts
4359
- var Noise = class {
4360
- _id;
4361
- constructor() {
4362
- this._id = _creator.createNoise();
4363
- }
4364
- /** Sample 2D noise. */
4365
- get(x, y) {
4366
- return _creator.getNoise2D(this._id, x, y);
4367
- }
4368
- /** Sample 3D noise. */
4369
- get3d(x, y, z) {
4370
- return _creator.getNoise3D(this._id, x, y, z);
4371
- }
4372
- _frequency = 0.01;
4373
- get frequency() {
4374
- return this._frequency;
4375
- }
4376
- set frequency(v) {
4377
- this._frequency = v;
4378
- _creator.setNoiseFrequency(this._id, v);
4379
- }
4380
- _octaves = 1;
4381
- get octaves() {
4382
- return this._octaves;
4383
- }
4384
- set octaves(v) {
4385
- this._octaves = v;
4386
- _creator.setNoiseOctaves(this._id, v);
4387
- }
4388
- _fractalLacunarity = 2;
4389
- get fractalLacunarity() {
4390
- return this._fractalLacunarity;
4391
- }
4392
- set fractalLacunarity(v) {
4393
- this._fractalLacunarity = v;
4394
- _creator.setNoiseFractalLunacrity(this._id, v);
4395
- }
4396
- _fractalGain = 0.5;
4397
- get fractalGain() {
4398
- return this._fractalGain;
4399
- }
4400
- set fractalGain(v) {
4401
- this._fractalGain = v;
4402
- _creator.setNoiseFractalGain(this._id, v);
4403
- }
4404
- };
4405
-
4406
- // src/gl/Particles.ts
4407
- var Particles = class extends Node {
4408
- _material;
4409
- /** Custom curve params (slots 0..3); size/rotation alias slots 0/1. */
4410
- custom;
4411
- constructor(options) {
4412
- super();
4413
- this._material = options.material;
4414
- _creator.createParticleSystem(this.id, Material.idOf(options.material));
4415
- const id = this.id;
4416
- this.custom = new Proxy([], {
4417
- set(target, prop, value) {
4418
- const i = parseInt(prop);
4419
- if (Number.isNaN(i)) {
4420
- target[prop] = value;
4421
- return true;
4422
- }
4423
- target[i] = value;
4424
- applyParam(id, i, value);
4425
- return true;
4426
- }
4427
- });
4428
- if (options.rate !== void 0) this.rate = options.rate;
4429
- if (options.shape !== void 0) this.shape = options.shape;
4430
- if (options.startVelocity !== void 0) this.startVelocity = options.startVelocity;
4431
- if (options.gravity !== void 0) this.gravity = options.gravity;
4432
- if (options.drag !== void 0) this.drag = options.drag;
4433
- if (options.lifetime !== void 0) this.lifetime = options.lifetime;
4434
- if (options.color !== void 0) this.color = options.color;
4435
- if (options.size !== void 0) this.custom[0] = options.size;
4436
- if (options.rotation !== void 0) this.custom[1] = options.rotation;
4437
- if (options.noise) this.noise = options.noise;
4438
- }
4439
- /** Emit `count` particles immediately (a burst). */
4440
- spawn(count) {
4441
- _creator.spawnParticles(this.id, count);
4442
- return this;
4443
- }
4444
- set rate(val) {
4445
- if (typeof val === "number") _creator.setParticleSystemRateValue(this.id, val);
4446
- }
4447
- set shape(shape) {
4448
- if (shape.type === "point") _creator.setParticleSystemShapePoint(this.id, cx(shape.v), cy(shape.v), cz(shape.v));
4449
- else _creator.setParticleSystemShapeBox(this.id, cx(shape.min), cy(shape.min), cz(shape.min), cx(shape.max), cy(shape.max), cz(shape.max));
4450
- }
4451
- set startVelocity(val) {
4452
- if (Array.isArray(val) || val instanceof Float32Array || val instanceof Vec3) {
4453
- _creator.setParticleSystemVelocityValue(this.id, 1, cx(val), cy(val), cz(val));
4454
- return;
4455
- }
4456
- const v = val;
4457
- if ("from" in v) _creator.setParticleSystemVelocityValue(this.id, 2, cx(v.from), cy(v.from), cz(v.from));
4458
- else if ("to" in v) _creator.setParticleSystemVelocityValue(this.id, 3, cx(v.to), cy(v.to), cz(v.to));
4459
- else if ("dir" in v) _creator.setParticleSystemVelocityValue(this.id, 1, cx(v.dir), cy(v.dir), cz(v.dir));
4460
- else _creator.setParticleSystemVelocityValue(this.id, 0, 0, 0, 0);
4461
- if ("speed" in v && v.speed !== void 0) {
4462
- if (typeof v.speed === "number") _creator.setParticleSystemVelocitySpeedValue(this.id, v.speed);
4463
- else _creator.setParticleSystemVelocitySpeedRange(this.id, v.speed.min, v.speed.max);
4464
- }
4465
- if (v.randomizeAngle) {
4466
- const a2 = v.randomizeAngle;
4467
- _creator.setParticleSystemVelocityRndAngle(this.id, cx(a2.min), cy(a2.min), cz(a2.min), cx(a2.max), cy(a2.max), cz(a2.max));
4468
- }
4469
- }
4470
- set gravity(val) {
4471
- _creator.setParticleSystemGravityValue(this.id, val);
4472
- }
4473
- set drag(val) {
4474
- _creator.setParticleSystemDragValue(this.id, val);
4475
- }
4476
- set lifetime(val) {
4477
- if (typeof val === "number") _creator.setParticleSystemLifetimeValue(this.id, val);
4478
- else _creator.setParticleSystemLifetimeRange(this.id, val.min, val.max);
4479
- }
4480
- set size(val) {
4481
- this.custom[0] = val;
4482
- }
4483
- set rotation(val) {
4484
- this.custom[1] = val;
4485
- }
4486
- set color(value) {
4487
- if (typeof value === "string") _creator.setParticleSystemColorValue(this.id, value);
4488
- else if ("min" in value) _creator.setParticleSystemColorRange(this.id, value.min, value.max);
4489
- else {
4490
- if (typeof value.value === "string") _creator.setParticleSystemColorValue(this.id, value.value);
4491
- else if (value.value) _creator.setParticleSystemColorRange(this.id, value.value.min, value.value.max);
4492
- if (value.mul) _creator.setParticleSystemColorCurveA(this.id, value.mul, value.t);
4493
- if (value.mul_b) _creator.setParticleSystemColorCurveB(this.id, value.mul_b, value.t);
4494
- }
4495
- }
4496
- get material() {
4497
- return this._material;
4498
- }
4499
- set material(material) {
4500
- this._material = material;
4501
- _creator.setMaterial(this.id, Material.idOf(material), 0);
4502
- }
4503
- set noise(noise) {
4504
- if (noise) _creator.setParticleSystemNoise(this.id, noise.strength ?? 1, noise.frequency ?? 1, noise.speed ?? 1);
4505
- else _creator.disableParticleSystemNoise(this.id);
4506
- }
4507
- };
4508
- var applyParam = (id, i, value) => {
4509
- if (typeof value === "number") {
4510
- _creator.setParticleSystemParamValue(id, i, value);
4511
- } else if ("min" in value) {
4512
- _creator.setParticleSystemParamRange(id, i, value.min, value.max);
4513
- } else {
4514
- if (typeof value.value === "number") _creator.setParticleSystemParamValue(id, i, value.value);
4515
- else if (value.value) _creator.setParticleSystemParamRange(id, i, value.value.min, value.value.max);
4516
- if (value.inc) _creator.setParticleSystemParamCurveA(id, i, value.inc, 1);
4517
- if (value.inc_b) _creator.setParticleSystemParamCurveB(id, i, value.inc_b);
4518
- if (value.mul) _creator.setParticleSystemParamCurveA(id, i, value.mul, 0);
4519
- if (value.mul_b) _creator.setParticleSystemParamCurveB(id, i, value.mul_b);
4520
- }
4521
- };
4522
- var dynamicColor = (baseValue, ...args) => {
4523
- const t = new Float32Array(args.length / 2);
4524
- const mul = [];
4525
- let mul_b;
4526
- for (let i = 0; i < args.length / 2; i++) {
4527
- if (typeof args[i * 2] !== "number") throw new Error(`dynamicColor: arg ${i * 2} must be a number`);
4528
- t[i] = args[i * 2];
4529
- const val = args[i * 2 + 1];
4530
- if (typeof val === "string") {
4531
- mul[i] = val;
4532
- if (mul_b) mul_b[i] = val;
4533
- } else if (typeof val === "number") throw new Error(`dynamicColor: arg ${i * 2 + 1} must be a string`);
4534
- else {
4535
- mul[i] = val.min;
4536
- if (!mul_b) mul_b = [...mul];
4537
- mul_b[i] = val.max;
4538
- }
4539
- }
4540
- return { value: baseValue, mul: mul.join("\0"), mul_b: mul_b?.join("\0"), t };
4541
- };
4542
- var dynamic = (baseValue, type, ...args) => {
4543
- const inc = new Float32Array(args.length);
4544
- let inc_b;
4545
- for (let i = 0; i < args.length; i += 2) {
4546
- if (typeof args[i] !== "number") throw new Error(`dynamic: arg ${i} must be a number`);
4547
- inc[i] = args[i];
4548
- if (inc_b) inc_b[i] = args[i];
4549
- const val = args[i + 1];
4550
- if (typeof val === "number") {
4551
- inc[i + 1] = val;
4552
- if (inc_b) inc_b[i + 1] = val;
4553
- } else {
4554
- inc[i + 1] = val.min;
4555
- if (!inc_b) inc_b = new Float32Array(inc);
4556
- inc_b[i + 1] = val.max;
4557
- }
4558
- }
4559
- return type === "inc" ? { value: baseValue, inc, inc_b } : { value: baseValue, mul: inc, mul_b: inc_b };
4560
- };
4561
- export {
4562
- ARScene,
4563
- AudioPlayer,
4564
- Camera,
4565
- ClickEvent,
4566
- Color,
4567
- File,
4568
- FormData,
4569
- Geometry,
4570
- Input,
4571
- Light,
4572
- Mat4,
4573
- Material,
4574
- Mathf,
4575
- Mesh,
4576
- Node,
4577
- Node2D,
4578
- Noise,
4579
- Particles,
4580
- Plane,
4581
- QRScanner,
4582
- Quat,
4583
- Ray,
4584
- Router,
4585
- Scene,
4586
- Scene2D,
4587
- Sprite,
4588
- SvgSource,
4589
- Texture,
4590
- Texture2D,
4591
- Tilemap,
4592
- TouchStartEvent,
4593
- UIBox,
4594
- UIButton,
4595
- UIColumn,
4596
- UIImage,
4597
- UIInput,
4598
- UIRow,
4599
- UIScreen,
4600
- UIScrollable,
4601
- UISpacer,
4602
- UIText,
4603
- UITextArea,
4604
- UIVideo,
4605
- UIVirtualizedList,
4606
- UIWidget,
4607
- Vec2,
4608
- Vec3,
4609
- VideoPlayer,
4610
- WebSocket,
4611
- animate,
4612
- animateMat4,
4613
- cubicBezier,
4614
- device,
4615
- dynamic,
4616
- dynamicColor,
4617
- easeIn,
4618
- easeInOut,
4619
- easeOut,
4620
- fetch,
4621
- fetchLocal,
4622
- localStorage,
4623
- openFilePicker,
4624
- pauseAnimation,
4625
- registerFont,
4626
- resumeAnimation,
4627
- stopAnimation,
4628
- toast
4629
- };