@nayan-ui/engine 0.0.0-stage → 1.0.0-beta.3

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 (189) hide show
  1. package/LICENSE +21 -0
  2. package/NayanEngine.podspec +28 -0
  3. package/README.md +210 -2
  4. package/lib/module/assets/AssetBytesCache.js +76 -0
  5. package/lib/module/assets/AssetCache.js +19 -0
  6. package/lib/module/assets/AssetLoader.js +52 -0
  7. package/lib/module/assets/CacheGeneration.js +7 -0
  8. package/lib/module/assets/IndexedDbCache.web.js +149 -0
  9. package/lib/module/assets/ProceduralCache.js +222 -0
  10. package/lib/module/assets/font.js +136 -0
  11. package/lib/module/assets/loft.js +142 -0
  12. package/lib/module/assets/model.js +74 -0
  13. package/lib/module/assets/modelling.js +254 -0
  14. package/lib/module/assets/shape.js +926 -0
  15. package/lib/module/assets/source.js +58 -0
  16. package/lib/module/assets/source.web.js +22 -0
  17. package/lib/module/assets/storage.js +4 -0
  18. package/lib/module/assets/storage.shared.js +36 -0
  19. package/lib/module/assets/storage.web.js +37 -0
  20. package/lib/module/assets/surface.js +47 -0
  21. package/lib/module/assets/texture.js +66 -0
  22. package/lib/module/drawing/DrawingCanvas.js +38 -0
  23. package/lib/module/drawing/DrawingCanvas.web.js +206 -0
  24. package/lib/module/drawing/DrawingDocument.js +256 -0
  25. package/lib/module/drawing/types.js +4 -0
  26. package/lib/module/globals.d.ts +12 -0
  27. package/lib/module/index.js +36 -0
  28. package/lib/module/input/Joystick.js +84 -0
  29. package/lib/module/input/Joystick.web.js +93 -0
  30. package/lib/module/input/keyboard.js +250 -0
  31. package/lib/module/media/audio-worklet.js +34 -0
  32. package/lib/module/media/audio.js +94 -0
  33. package/lib/module/media/haptics.js +82 -0
  34. package/lib/module/media/output.web.js +66 -0
  35. package/lib/module/native/BrowserEngine.js +551 -0
  36. package/lib/module/native/EngineBridge.js +4 -0
  37. package/lib/module/native/NativeNayanEngine.js +7 -0
  38. package/lib/module/native/NativeNayanEngine.web.js +62 -0
  39. package/lib/module/native/prepare.js +7 -0
  40. package/lib/module/native/prepare.web.js +17 -0
  41. package/lib/module/native/spec/NativeNayanEngine.js +17 -0
  42. package/lib/module/package.json +1 -0
  43. package/lib/module/render/GameView.js +249 -0
  44. package/lib/module/render/camera.js +34 -0
  45. package/lib/module/render/host.js +39 -0
  46. package/lib/module/render/host.web.js +104 -0
  47. package/lib/module/render/meshes.js +159 -0
  48. package/lib/module/render/quality.js +112 -0
  49. package/lib/module/render/responder.js +4 -0
  50. package/lib/module/render/responder.web.js +97 -0
  51. package/lib/module/render/touch.js +185 -0
  52. package/lib/module/types.js +30 -0
  53. package/lib/module/world/Scoreboard.js +143 -0
  54. package/lib/module/world/TransformBatch.js +126 -0
  55. package/lib/module/world/World.js +690 -0
  56. package/lib/module/world/desc.js +186 -0
  57. package/lib/module/world/effects.js +451 -0
  58. package/lib/module/world/quat.js +86 -0
  59. package/lib/module/world/types.js +4 -0
  60. package/lib/typescript/package.json +1 -0
  61. package/lib/typescript/src/assets/AssetBytesCache.d.ts +7 -0
  62. package/lib/typescript/src/assets/AssetCache.d.ts +9 -0
  63. package/lib/typescript/src/assets/AssetLoader.d.ts +11 -0
  64. package/lib/typescript/src/assets/CacheGeneration.d.ts +3 -0
  65. package/lib/typescript/src/assets/IndexedDbCache.web.d.ts +20 -0
  66. package/lib/typescript/src/assets/ProceduralCache.d.ts +33 -0
  67. package/lib/typescript/src/assets/font.d.ts +53 -0
  68. package/lib/typescript/src/assets/loft.d.ts +31 -0
  69. package/lib/typescript/src/assets/model.d.ts +48 -0
  70. package/lib/typescript/src/assets/modelling.d.ts +157 -0
  71. package/lib/typescript/src/assets/shape.d.ts +203 -0
  72. package/lib/typescript/src/assets/source.d.ts +10 -0
  73. package/lib/typescript/src/assets/source.web.d.ts +8 -0
  74. package/lib/typescript/src/assets/storage.d.ts +2 -0
  75. package/lib/typescript/src/assets/storage.shared.d.ts +7 -0
  76. package/lib/typescript/src/assets/storage.web.d.ts +7 -0
  77. package/lib/typescript/src/assets/surface.d.ts +17 -0
  78. package/lib/typescript/src/assets/texture.d.ts +30 -0
  79. package/lib/typescript/src/drawing/DrawingCanvas.d.ts +5 -0
  80. package/lib/typescript/src/drawing/DrawingCanvas.web.d.ts +4 -0
  81. package/lib/typescript/src/drawing/DrawingDocument.d.ts +210 -0
  82. package/lib/typescript/src/drawing/types.d.ts +12 -0
  83. package/lib/typescript/src/index.d.ts +42 -0
  84. package/lib/typescript/src/input/Joystick.d.ts +20 -0
  85. package/lib/typescript/src/input/Joystick.web.d.ts +13 -0
  86. package/lib/typescript/src/input/keyboard.d.ts +17 -0
  87. package/lib/typescript/src/media/audio.d.ts +46 -0
  88. package/lib/typescript/src/media/haptics.d.ts +22 -0
  89. package/lib/typescript/src/media/output.web.d.ts +4 -0
  90. package/lib/typescript/src/native/BrowserEngine.d.ts +8 -0
  91. package/lib/typescript/src/native/EngineBridge.d.ts +22 -0
  92. package/lib/typescript/src/native/NativeNayanEngine.d.ts +5 -0
  93. package/lib/typescript/src/native/NativeNayanEngine.web.d.ts +10 -0
  94. package/lib/typescript/src/native/prepare.d.ts +3 -0
  95. package/lib/typescript/src/native/prepare.web.d.ts +2 -0
  96. package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +182 -0
  97. package/lib/typescript/src/render/GameView.d.ts +95 -0
  98. package/lib/typescript/src/render/camera.d.ts +6 -0
  99. package/lib/typescript/src/render/host.d.ts +29 -0
  100. package/lib/typescript/src/render/host.web.d.ts +16 -0
  101. package/lib/typescript/src/render/meshes.d.ts +45 -0
  102. package/lib/typescript/src/render/quality.d.ts +46 -0
  103. package/lib/typescript/src/render/responder.d.ts +2 -0
  104. package/lib/typescript/src/render/responder.web.d.ts +40 -0
  105. package/lib/typescript/src/render/touch.d.ts +26 -0
  106. package/lib/typescript/src/types.d.ts +72 -0
  107. package/lib/typescript/src/world/Scoreboard.d.ts +65 -0
  108. package/lib/typescript/src/world/TransformBatch.d.ts +39 -0
  109. package/lib/typescript/src/world/World.d.ts +199 -0
  110. package/lib/typescript/src/world/desc.d.ts +17 -0
  111. package/lib/typescript/src/world/effects.d.ts +156 -0
  112. package/lib/typescript/src/world/quat.d.ts +31 -0
  113. package/lib/typescript/src/world/types.d.ts +233 -0
  114. package/lib/web/audio-worklet.js +29 -0
  115. package/lib/web/engine_core_bg.wasm +0 -0
  116. package/lib/web/index.js +7974 -0
  117. package/lib/web/types/src/assets/AssetBytesCache.d.ts +6 -0
  118. package/lib/web/types/src/assets/AssetCache.d.ts +8 -0
  119. package/lib/web/types/src/assets/AssetLoader.d.ts +10 -0
  120. package/lib/web/types/src/assets/CacheGeneration.d.ts +2 -0
  121. package/lib/web/types/src/assets/IndexedDbCache.d.ts +19 -0
  122. package/lib/web/types/src/assets/ProceduralCache.d.ts +32 -0
  123. package/lib/web/types/src/assets/font.d.ts +52 -0
  124. package/lib/web/types/src/assets/loft.d.ts +30 -0
  125. package/lib/web/types/src/assets/model.d.ts +47 -0
  126. package/lib/web/types/src/assets/modelling.d.ts +156 -0
  127. package/lib/web/types/src/assets/shape.d.ts +202 -0
  128. package/lib/web/types/src/assets/source.d.ts +7 -0
  129. package/lib/web/types/src/assets/storage.d.ts +6 -0
  130. package/lib/web/types/src/assets/storage.shared.d.ts +6 -0
  131. package/lib/web/types/src/assets/surface.d.ts +16 -0
  132. package/lib/web/types/src/assets/texture.d.ts +29 -0
  133. package/lib/web/types/src/drawing/DrawingCanvas.d.ts +3 -0
  134. package/lib/web/types/src/drawing/DrawingDocument.d.ts +209 -0
  135. package/lib/web/types/src/drawing/types.d.ts +11 -0
  136. package/lib/web/types/src/index.d.ts +41 -0
  137. package/lib/web/types/src/input/Joystick.d.ts +12 -0
  138. package/lib/web/types/src/input/keyboard.d.ts +16 -0
  139. package/lib/web/types/src/media/audio.d.ts +45 -0
  140. package/lib/web/types/src/media/haptics.d.ts +21 -0
  141. package/lib/web/types/src/media/output.d.ts +3 -0
  142. package/lib/web/types/src/native/BrowserEngine.d.ts +7 -0
  143. package/lib/web/types/src/native/EngineBridge.d.ts +21 -0
  144. package/lib/web/types/src/native/NativeNayanEngine.d.ts +9 -0
  145. package/lib/web/types/src/native/prepare.d.ts +1 -0
  146. package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +181 -0
  147. package/lib/web/types/src/render/GameView.d.ts +94 -0
  148. package/lib/web/types/src/render/camera.d.ts +5 -0
  149. package/lib/web/types/src/render/host.d.ts +15 -0
  150. package/lib/web/types/src/render/meshes.d.ts +44 -0
  151. package/lib/web/types/src/render/quality.d.ts +45 -0
  152. package/lib/web/types/src/render/responder.d.ts +39 -0
  153. package/lib/web/types/src/render/touch.d.ts +25 -0
  154. package/lib/web/types/src/types.d.ts +71 -0
  155. package/lib/web/types/src/world/Scoreboard.d.ts +64 -0
  156. package/lib/web/types/src/world/TransformBatch.d.ts +38 -0
  157. package/lib/web/types/src/world/World.d.ts +198 -0
  158. package/lib/web/types/src/world/desc.d.ts +16 -0
  159. package/lib/web/types/src/world/effects.d.ts +155 -0
  160. package/lib/web/types/src/world/quat.d.ts +30 -0
  161. package/lib/web/types/src/world/types.d.ts +232 -0
  162. package/native/android/CMakeLists.txt +34 -0
  163. package/native/android/NayanEngineSpec.h +15 -0
  164. package/native/android/build.gradle +26 -0
  165. package/native/android/codegen/CMakeLists.txt +3 -0
  166. package/native/android/proguard-rules.pro +2 -0
  167. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +234 -0
  168. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceViewManager.java +45 -0
  169. package/native/android/src/main/java/com/nayanui/engine/NayanEnginePackage.java +24 -0
  170. package/native/core/build/EngineCore.xcframework/Info.plist +43 -0
  171. package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
  172. package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
  173. package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
  174. package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
  175. package/native/core/build/android/x86/libengine_core.a +0 -0
  176. package/native/core/build/android/x86_64/libengine_core.a +0 -0
  177. package/native/core/include/drawing-licenses.txt +58 -0
  178. package/native/core/include/engine_core.h +322 -0
  179. package/native/cpp/NayanEngineModule.cpp +827 -0
  180. package/native/cpp/NayanEngineModule.h +144 -0
  181. package/native/cpp/generated/NayanEngineSpecJSI.cpp +2 -0
  182. package/native/cpp/generated/NayanEngineSpecJSI.h +921 -0
  183. package/native/ios/NayanEngineProvider.h +5 -0
  184. package/native/ios/NayanEngineProvider.mm +21 -0
  185. package/native/ios/NayanEngineSurface.mm +223 -0
  186. package/native/scripts/check-package.mjs +39 -0
  187. package/package.json +162 -4
  188. package/react-native.config.js +18 -0
  189. package/src/native/spec/NativeNayanEngine.ts +226 -0
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+
3
+ import NativeEngine from '../native/NativeNayanEngine';
4
+
5
+ /** Native document settings; React may display this snapshot but never owns the tool state. */
6
+
7
+ /** A committed stroke's geometry in document coordinates, for colliders, tracing and scoring. */
8
+
9
+ /** The composited ink under a document point (straight alpha; transparent paper is alpha 0). */
10
+
11
+ /**
12
+ * A native art document. Touches and live rendering never subscribe to React state.
13
+ *
14
+ * Games use the same document without `DrawingCanvas`: bind `texture()` to any entity, feed
15
+ * `input(..., 'document')` from the game's own touches or world hits, and read `strokes()`,
16
+ * `sample()` and `coverage()` for colliders and scoring. Create one document per game screen and
17
+ * `clear()` it on replay; `dispose()` it on unmount (this also frees its texture).
18
+ */
19
+ export class DrawingDocument {
20
+ disposed = false;
21
+ boundTexture = null;
22
+ constructor(width, height, pixelRatio = 2) {
23
+ if (!NativeEngine || typeof NativeEngine.drawingCreate !== 'function') {
24
+ throw new Error('Drawing requires a native app rebuild with the updated Nayan engine.');
25
+ }
26
+ NativeEngine.renderWarmUp?.();
27
+ const id = NativeEngine.drawingCreate(width, height, pixelRatio);
28
+ if (id < 0) throw new Error('Unable to create drawing: invalid dimensions or memory limit.');
29
+ this.id = id;
30
+ this.width = width;
31
+ this.height = height;
32
+ }
33
+ get isDisposed() {
34
+ return this.disposed;
35
+ }
36
+ call(command) {
37
+ if (this.disposed) throw new Error('Drawing document has been disposed');
38
+ const result = JSON.parse(NativeEngine.drawingCommand(this.id, JSON.stringify(command)));
39
+ if (!result.ok) throw new Error(result.error || 'Drawing operation failed');
40
+ return result.value;
41
+ }
42
+ setBrush(options) {
43
+ return this.call({
44
+ op: 'brush',
45
+ ...options
46
+ });
47
+ }
48
+ updateControls(options) {
49
+ return this.call({
50
+ op: 'controls',
51
+ ...options
52
+ });
53
+ }
54
+ /** Atomic import: errors leave the current artwork intact, including unknown legacy metadata. */
55
+ load(paths) {
56
+ return this.call({
57
+ op: 'load',
58
+ paths
59
+ });
60
+ }
61
+ get state() {
62
+ return this.call({
63
+ op: 'state'
64
+ });
65
+ }
66
+ exportPaths() {
67
+ return JSON.stringify(this.call({
68
+ op: 'paths'
69
+ }));
70
+ }
71
+ /** Rust checks idleness and revision atomically; autosave never finishes a live stroke. */
72
+ setEditingEnabled(enabled) {
73
+ return this.call({
74
+ op: 'editing',
75
+ enabled
76
+ });
77
+ }
78
+ captureDraft(afterRevision = -1) {
79
+ return this.call({
80
+ op: 'draft',
81
+ afterRevision
82
+ });
83
+ }
84
+ /** Browser change stream. Native canvases deliver changes directly from their display clock. */
85
+ readChange() {
86
+ return this.call({
87
+ op: 'event'
88
+ });
89
+ }
90
+ exportSVG() {
91
+ return this.call({
92
+ op: 'svg'
93
+ });
94
+ }
95
+ /** Full editable project: preserves layer order, visibility, locks and original SVG metadata. */
96
+ exportProject() {
97
+ return JSON.stringify(this.call({
98
+ op: 'project'
99
+ }));
100
+ }
101
+ layer(action, options = {}) {
102
+ return this.call({
103
+ op: 'layer',
104
+ action,
105
+ ...options
106
+ });
107
+ }
108
+ fill(x, y, tolerance = 0.08) {
109
+ return this.call({
110
+ op: 'fill',
111
+ x,
112
+ y,
113
+ tolerance
114
+ });
115
+ }
116
+ shapeCatalog() {
117
+ return this.call({
118
+ op: 'shapes'
119
+ });
120
+ }
121
+ addShape(kind, x, y, endX, endY, filled = false) {
122
+ return this.call({
123
+ op: 'shape',
124
+ kind,
125
+ x,
126
+ y,
127
+ endX,
128
+ endY,
129
+ filled
130
+ });
131
+ }
132
+ snapshot() {
133
+ return this.call({
134
+ op: 'png'
135
+ });
136
+ }
137
+ undo() {
138
+ return this.call({
139
+ op: 'undo'
140
+ });
141
+ }
142
+ redo() {
143
+ return this.call({
144
+ op: 'redo'
145
+ });
146
+ }
147
+ clear() {
148
+ return this.call({
149
+ op: 'clear'
150
+ });
151
+ }
152
+ invalidateView() {
153
+ return this.call({
154
+ op: 'invalidateView'
155
+ });
156
+ }
157
+ resetView() {
158
+ return this.call({
159
+ op: 'resetView'
160
+ });
161
+ }
162
+ /** Fit the full artwork or fill the viewport width without changing saved dimensions. */
163
+ setViewFit(fit) {
164
+ return this.call({
165
+ op: 'viewFit',
166
+ fit
167
+ });
168
+ }
169
+ finish() {
170
+ return this.call({
171
+ op: 'finish'
172
+ });
173
+ }
174
+ cancel() {
175
+ return this.call({
176
+ op: 'cancel'
177
+ });
178
+ }
179
+ /**
180
+ * Feeds one pointer sample: 0 begin, 1 move, 2 end, 3 cancel. `view` coordinates are relative to
181
+ * a `DrawingCanvas` and follow its zoom/pan (browser pointer sampling; native canvases feed
182
+ * touches straight into the Rust core). `document` coordinates skip the view mapping, for games
183
+ * that convert their own touches or world hits (e.g. a raycast's texture UV × document size).
184
+ */
185
+ input(phase, x, y, pressure = 1, space = 'view') {
186
+ this.call({
187
+ op: 'touch',
188
+ phase,
189
+ x,
190
+ y,
191
+ pressure,
192
+ space
193
+ });
194
+ }
195
+ /**
196
+ * A live texture of the artwork (transparent paper, straight alpha), usable as any entity's
197
+ * `texture`. In-progress strokes appear immediately; only changed rows are uploaded each frame.
198
+ * Created once per document and freed by `dispose()`. Its pixels are not readable through
199
+ * texture APIs; use `sample()`, `coverage()` or `snapshot()`.
200
+ */
201
+ texture() {
202
+ if (!this.boundTexture) this.boundTexture = Object.freeze(this.call({
203
+ op: 'texture'
204
+ }));
205
+ return this.boundTexture;
206
+ }
207
+ /** Committed strokes from index `from`, flattened to `tolerance` document units (default 0.5). */
208
+ strokes(options = {}) {
209
+ return this.call({
210
+ op: 'strokes',
211
+ ...options
212
+ });
213
+ }
214
+ /** The ink under a document point, or null off the paper. Includes an in-progress stroke. */
215
+ sample(x, y) {
216
+ return this.call({
217
+ op: 'sample',
218
+ x,
219
+ y
220
+ });
221
+ }
222
+ /** Fraction (0..1) of a document rectangle covered by ink, for colouring and scratch-off scoring. */
223
+ coverage(options = {}) {
224
+ return this.call({
225
+ op: 'coverage',
226
+ ...options
227
+ });
228
+ }
229
+ transform(dx, dy, scale = 1) {
230
+ return this.call({
231
+ op: 'transform',
232
+ dx,
233
+ dy,
234
+ scale
235
+ });
236
+ }
237
+ /** Browser presentation; iOS and Android use native display clocks with automatic suspension. */
238
+ present(width, height, background = '#ffffff', pixelRatio = 1) {
239
+ return this.call({
240
+ op: 'render',
241
+ surface: this.id,
242
+ width,
243
+ height,
244
+ background,
245
+ pixelWidth: Math.round(width * pixelRatio),
246
+ pixelHeight: Math.round(height * pixelRatio)
247
+ });
248
+ }
249
+ dispose() {
250
+ if (this.disposed) return;
251
+ NativeEngine.drawingDestroy(this.id);
252
+ this.disposed = true;
253
+ this.boundTexture = null;
254
+ }
255
+ }
256
+ //# sourceMappingURL=DrawingDocument.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,12 @@
1
+ // Hermes provides performance.now(); React Native's typings do not declare it.
2
+ declare const performance: { now(): number };
3
+
4
+ // Generated by the WASM build; allows checking source before the generated files exist.
5
+ declare module '*build/web/engine_core.js' {
6
+ export default function init(): Promise<import('./native/BrowserEngine').WasmCore>;
7
+ export function attach_canvas(id: number, canvas: HTMLCanvasElement): Promise<void>;
8
+ export function install_asset_storage(driver: unknown): void;
9
+ export function can_warm_asset_record(bytes: number): boolean;
10
+ export function restore_asset_record(name: string, bytes: Uint8Array): boolean;
11
+ export function read_saved_asset(key: string): Promise<Uint8Array>;
12
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ // Public API of @nayan-ui/engine.
4
+
5
+ // World: entities, physics, collisions, animation, particles, picking.
6
+ export { World, isEngineAvailable } from "./world/World.js";
7
+ export { quat } from "./world/quat.js";
8
+ export { effects } from "./world/effects.js";
9
+ export { Scoreboard } from "./world/Scoreboard.js";
10
+ // Rendering and touch.
11
+ export { GameView } from "./render/GameView.js";
12
+ export { warmUpEngine, getMeshIdLimit } from "./render/meshes.js";
13
+
14
+ // Models, custom shapes, textures and fonts.
15
+
16
+ export { loadModel } from "./assets/model.js";
17
+ export { clearAssetCache } from "./assets/AssetCache.js";
18
+ export { ProceduralCache } from "./assets/ProceduralCache.js";
19
+ export { loadTexture } from "./assets/texture.js";
20
+ export { Solid, solidGeometry, createSolid, optimizeGeometry, createGeometryLods, getGeometryStats, deformGeometry } from "./assets/modelling.js";
21
+ export { loft, block, ellipsoid, smoothCurve } from "./assets/loft.js";
22
+ export { parametricSurface } from "./assets/surface.js";
23
+ export { createShape, extrude, lathe, merge, Path, recolor, shapeGeometry, smoothNormals, tube } from "./assets/shape.js";
24
+ export { loadFont, measureText } from "./assets/font.js";
25
+ // Sound and haptics.
26
+ export { audio } from "./media/audio.js";
27
+ export { haptics } from "./media/haptics.js";
28
+ // Input.
29
+ export { Joystick, useJoystick } from './input/Joystick';
30
+ export { createKeyboardInput } from "./input/keyboard.js";
31
+ // Shared types.
32
+ export { MAX_MESHES } from "./types.js";
33
+ // Native 2D drawing, SVG compatibility and transparent PNG snapshots; documents also texture game entities.
34
+ export { DrawingDocument } from "./drawing/DrawingDocument.js";
35
+ export { DrawingCanvas } from './drawing/DrawingCanvas';
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
4
+ import { Animated, PanResponder, StyleSheet, View } from 'react-native';
5
+
6
+ /** Written by `<Joystick>` on touch; read it from your game loop. Each axis is in -1..1, y is up. */
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ /** A joystick state that lives as long as the component: `const stick = useJoystick()`. */
9
+ export function useJoystick() {
10
+ return useRef({
11
+ x: 0,
12
+ y: 0
13
+ }).current;
14
+ }
15
+ /**
16
+ * A relative touch stick: wherever the finger lands inside the pad is the center.
17
+ * Updates `state` synchronously on the JS thread, so there is no React re-render per touch.
18
+ */
19
+ export function Joystick({
20
+ state,
21
+ size = 150,
22
+ style
23
+ }) {
24
+ const knob = useRef(new Animated.ValueXY()).current;
25
+ const knobSize = size * 0.4;
26
+ const reach = (size - knobSize) / 2;
27
+ const reset = useCallback(() => {
28
+ state.x = 0;
29
+ state.y = 0;
30
+ knob.setValue({
31
+ x: 0,
32
+ y: 0
33
+ });
34
+ }, [state, knob]);
35
+ useEffect(() => reset, [reset]);
36
+ const pan = useMemo(() => PanResponder.create({
37
+ onStartShouldSetPanResponder: () => true,
38
+ onMoveShouldSetPanResponder: () => true,
39
+ onPanResponderMove: (_, g) => {
40
+ const d = Math.hypot(g.dx, g.dy);
41
+ const k = d > reach ? reach / d : 1;
42
+ const dx = g.dx * k;
43
+ const dy = g.dy * k;
44
+ state.x = dx / reach;
45
+ state.y = -dy / reach;
46
+ knob.setValue({
47
+ x: dx,
48
+ y: dy
49
+ });
50
+ },
51
+ onPanResponderRelease: reset,
52
+ onPanResponderTerminate: reset
53
+ }), [state, knob, reach, reset]);
54
+ return /*#__PURE__*/_jsx(View, {
55
+ ...pan.panHandlers,
56
+ style: [styles.base, {
57
+ width: size,
58
+ height: size,
59
+ borderRadius: size / 2
60
+ }, style],
61
+ children: /*#__PURE__*/_jsx(Animated.View, {
62
+ style: [styles.knob, {
63
+ width: knobSize,
64
+ height: knobSize,
65
+ borderRadius: knobSize / 2
66
+ }, {
67
+ transform: knob.getTranslateTransform()
68
+ }]
69
+ })
70
+ });
71
+ }
72
+ const styles = StyleSheet.create({
73
+ base: {
74
+ alignItems: 'center',
75
+ justifyContent: 'center',
76
+ backgroundColor: 'rgba(255,255,255,0.08)',
77
+ borderWidth: 2,
78
+ borderColor: 'rgba(255,255,255,0.25)'
79
+ },
80
+ knob: {
81
+ backgroundColor: 'rgba(255,255,255,0.55)'
82
+ }
83
+ });
84
+ //# sourceMappingURL=Joystick.js.map
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useRef } from 'react';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export function useJoystick() {
6
+ return useRef({
7
+ x: 0,
8
+ y: 0
9
+ }).current;
10
+ }
11
+ export function Joystick({
12
+ state,
13
+ size = 150,
14
+ style
15
+ }) {
16
+ const knob = useRef(null);
17
+ const pointer = useRef(null);
18
+ const origin = useRef({
19
+ x: 0,
20
+ y: 0
21
+ });
22
+ const reach = size * 0.3;
23
+ const reset = useCallback(() => {
24
+ pointer.current = null;
25
+ state.x = state.y = 0;
26
+ if (knob.current) knob.current.style.transform = 'translate(0px, 0px)';
27
+ }, [state]);
28
+ useEffect(() => {
29
+ const visibility = () => {
30
+ if (document.hidden) reset();
31
+ };
32
+ window.addEventListener('blur', reset);
33
+ document.addEventListener('visibilitychange', visibility);
34
+ return () => {
35
+ reset();
36
+ window.removeEventListener('blur', reset);
37
+ document.removeEventListener('visibilitychange', visibility);
38
+ };
39
+ }, [reset]);
40
+ const move = e => {
41
+ if (e.pointerId !== pointer.current) return;
42
+ const x = e.clientX - origin.current.x,
43
+ y = e.clientY - origin.current.y;
44
+ const scale = Math.min(1, reach / Math.max(1, Math.hypot(x, y)));
45
+ state.x = x * scale / reach;
46
+ state.y = -y * scale / reach;
47
+ knob.current.style.transform = `translate(${x * scale}px, ${y * scale}px)`;
48
+ };
49
+ return /*#__PURE__*/_jsx("div", {
50
+ style: {
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ width: size,
55
+ height: size,
56
+ borderRadius: size / 2,
57
+ boxSizing: 'border-box',
58
+ background: 'rgba(255,255,255,0.08)',
59
+ border: '2px solid rgba(255,255,255,0.25)',
60
+ touchAction: 'none',
61
+ ...style
62
+ },
63
+ onPointerDown: e => {
64
+ if (pointer.current !== null || e.button !== 0) return;
65
+ e.preventDefault();
66
+ pointer.current = e.pointerId;
67
+ origin.current = {
68
+ x: e.clientX,
69
+ y: e.clientY
70
+ };
71
+ e.currentTarget.setPointerCapture(e.pointerId);
72
+ },
73
+ onPointerMove: move,
74
+ onPointerUp: e => {
75
+ if (e.pointerId === pointer.current) reset();
76
+ },
77
+ onPointerCancel: reset,
78
+ onLostPointerCapture: e => {
79
+ if (e.pointerId === pointer.current) reset();
80
+ },
81
+ children: /*#__PURE__*/_jsx("div", {
82
+ ref: knob,
83
+ style: {
84
+ pointerEvents: 'none',
85
+ width: size * 0.4,
86
+ height: size * 0.4,
87
+ borderRadius: '50%',
88
+ background: 'rgba(255,255,255,0.55)'
89
+ }
90
+ })
91
+ });
92
+ }
93
+ //# sourceMappingURL=Joystick.web.js.map