@navaramap/core 0.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 (67) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/dist/index.d.ts +605 -0
  4. package/dist/index.js +3042 -0
  5. package/dist/index.umd.cjs +1 -0
  6. package/package.json +38 -0
  7. package/src/abstracts/index.ts +1 -0
  8. package/src/abstracts/vec3.ts +15 -0
  9. package/src/assertions.ts +15 -0
  10. package/src/batch/attribute.test.ts +20 -0
  11. package/src/batch/attribute.ts +14 -0
  12. package/src/batch/index.ts +1 -0
  13. package/src/color/Color.ts +10 -0
  14. package/src/color/ColorMap.ts +86 -0
  15. package/src/color/index.ts +3 -0
  16. package/src/color/types.ts +4 -0
  17. package/src/constants/image.ts +1 -0
  18. package/src/constants/index.ts +1 -0
  19. package/src/error.ts +11 -0
  20. package/src/event/EventManager.test.ts +539 -0
  21. package/src/event/EventManager.ts +387 -0
  22. package/src/event/TransactionManager.test.ts +126 -0
  23. package/src/event/TransactionManager.ts +78 -0
  24. package/src/event/index.ts +2 -0
  25. package/src/event/mock.ts +36 -0
  26. package/src/eventHandler.ts +52 -0
  27. package/src/globe/Globe.ts +156 -0
  28. package/src/globe/index.ts +1 -0
  29. package/src/id.ts +9 -0
  30. package/src/index.ts +15 -0
  31. package/src/namedIndexMap.test.ts +268 -0
  32. package/src/namedIndexMap.ts +50 -0
  33. package/src/observed.ts +30 -0
  34. package/src/plugin.ts +15 -0
  35. package/src/time.ts +3 -0
  36. package/src/types/buffer.ts +4 -0
  37. package/src/types/camera.ts +16 -0
  38. package/src/types/cameraFrustum.ts +5 -0
  39. package/src/types/feature.ts +1 -0
  40. package/src/types/index.ts +13 -0
  41. package/src/types/layer.ts +1 -0
  42. package/src/types/plane.ts +5 -0
  43. package/src/types/ray.ts +5 -0
  44. package/src/types/tile.ts +1 -0
  45. package/src/types/transform.ts +5 -0
  46. package/src/types/unit.ts +7 -0
  47. package/src/types/utils.ts +20 -0
  48. package/src/types/vec.ts +9 -0
  49. package/src/types/window.ts +5 -0
  50. package/src/utils/ExtentRadianF32Like.ts +17 -0
  51. package/src/utils/FloatAttributeLike.ts +17 -0
  52. package/src/utils/ReturnedConstructedTerrainMeshLike.ts +92 -0
  53. package/src/utils/TransferableMartiniLike.ts +21 -0
  54. package/src/utils/TransferableRasterDEMDataLike.ts +36 -0
  55. package/src/utils/TransferableTileLike.ts +53 -0
  56. package/src/utils/UpsamplableTerrainGeometryLike.ts +26 -0
  57. package/src/utils/Vec3Like.ts +15 -0
  58. package/src/utils/index.ts +9 -0
  59. package/src/utils/polygon/ConstructedPolygonGeometryLike.ts +70 -0
  60. package/src/utils/polygon/PolygonMaterialLike.ts +37 -0
  61. package/src/utils/polygon/TransferablePolygonBatchedFeatureLike.ts +73 -0
  62. package/src/utils/polygon/index.ts +3 -0
  63. package/src/utils/polyline/ConstructedPolylineGeometryLike.ts +77 -0
  64. package/src/utils/polyline/PolylineMaterialLike.ts +31 -0
  65. package/src/utils/polyline/TransferablePolylineBatchedFeatureLike.ts +43 -0
  66. package/src/utils/polyline/index.ts +3 -0
  67. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,539 @@
1
+ import type { RenderableFeatureAddedEvent } from "@navaramap/engine";
2
+ import { expect, it, vi } from "vitest";
3
+
4
+ import { wait } from "../time";
5
+
6
+ import { EventManager } from "./EventManager";
7
+ import { makeEvent, makeRenderableFeatures } from "./mock";
8
+
9
+ it("should handle renderable feature event", async () => {
10
+ const eventManager = new EventManager();
11
+
12
+ const mockFnAdded = vi.fn();
13
+ const mockFnRemoved = vi.fn();
14
+ const mockFnChanged = vi.fn();
15
+
16
+ // First frame
17
+ eventManager.pushEvents(
18
+ makeEvent({
19
+ renderable_feature_added: [
20
+ makeRenderableFeatures(1, 1),
21
+ makeRenderableFeatures(1, 2),
22
+ makeRenderableFeatures(1, 3),
23
+ makeRenderableFeatures(1, 4),
24
+ ],
25
+ renderable_feature_removed: [],
26
+ renderable_feature_changed: [],
27
+ }),
28
+ );
29
+
30
+ eventManager.processTransactionEvents(
31
+ "renderableFeature",
32
+ {
33
+ add: {
34
+ key: "renderable_feature_added",
35
+ max: 3,
36
+ },
37
+ remove: {
38
+ key: "renderable_feature_removed",
39
+ },
40
+ change: {
41
+ key: "renderable_feature_changed",
42
+ },
43
+ },
44
+ async ({ type }) => {
45
+ switch (type) {
46
+ case "add":
47
+ await wait(100);
48
+ mockFnAdded();
49
+ break;
50
+ case "remove":
51
+ await wait(100);
52
+ mockFnRemoved();
53
+ break;
54
+ case "change":
55
+ await wait(100);
56
+ mockFnChanged();
57
+ break;
58
+ }
59
+ },
60
+ );
61
+
62
+ await vi.waitFor(() => expect(mockFnAdded).toBeCalledTimes(3));
63
+ expect(mockFnRemoved).not.toBeCalled();
64
+ expect(mockFnChanged).not.toBeCalled();
65
+
66
+ mockFnAdded.mockReset();
67
+
68
+ // Second frame
69
+ eventManager.pushEvents(
70
+ makeEvent({
71
+ renderable_feature_added: [],
72
+ renderable_feature_removed: [
73
+ makeRenderableFeatures(1, 1),
74
+ makeRenderableFeatures(1, 4),
75
+ ],
76
+ renderable_feature_changed: [
77
+ makeRenderableFeatures(1, 1),
78
+ makeRenderableFeatures(1, 2),
79
+ makeRenderableFeatures(1, 3),
80
+ ],
81
+ }),
82
+ );
83
+
84
+ eventManager.processTransactionEvents(
85
+ "renderableFeature",
86
+ {
87
+ add: {
88
+ key: "renderable_feature_added",
89
+ max: 3,
90
+ },
91
+ remove: {
92
+ key: "renderable_feature_removed",
93
+ },
94
+ change: {
95
+ key: "renderable_feature_changed",
96
+ },
97
+ },
98
+ async ({ type }) => {
99
+ switch (type) {
100
+ case "add":
101
+ await wait(100);
102
+ mockFnAdded();
103
+ break;
104
+ case "remove":
105
+ await wait(100);
106
+ mockFnRemoved();
107
+ break;
108
+ case "change":
109
+ await wait(100);
110
+ mockFnChanged();
111
+ break;
112
+ }
113
+ },
114
+ );
115
+
116
+ expect(mockFnAdded).not.toBeCalled();
117
+ await vi.waitFor(() => expect(mockFnRemoved).toBeCalledTimes(1));
118
+ expect(mockFnChanged).not.toBeCalled();
119
+
120
+ mockFnRemoved.mockReset();
121
+
122
+ // Third frame
123
+ eventManager.processTransactionEvents(
124
+ "renderableFeature",
125
+ {
126
+ add: {
127
+ key: "renderable_feature_added",
128
+ max: 3,
129
+ },
130
+ remove: {
131
+ key: "renderable_feature_removed",
132
+ },
133
+ change: {
134
+ key: "renderable_feature_changed",
135
+ },
136
+ },
137
+ async ({ type }) => {
138
+ switch (type) {
139
+ case "add":
140
+ await wait(100);
141
+ mockFnAdded();
142
+ break;
143
+ case "remove":
144
+ await wait(100);
145
+ mockFnRemoved();
146
+ break;
147
+ case "change":
148
+ await wait(100);
149
+ mockFnChanged();
150
+ break;
151
+ }
152
+ },
153
+ );
154
+
155
+ expect(mockFnAdded).not.toBeCalled();
156
+ expect(mockFnRemoved).not.toBeCalled();
157
+ await vi.waitFor(() => expect(mockFnChanged).toBeCalledTimes(2));
158
+ });
159
+
160
+ it("should remove duplicated renderable feature event", async () => {
161
+ const eventManager = new EventManager();
162
+
163
+ eventManager.pushEvents(
164
+ makeEvent({
165
+ renderable_feature_added: [
166
+ makeRenderableFeatures(1, 1),
167
+ makeRenderableFeatures(1, 2),
168
+ makeRenderableFeatures(1, 3),
169
+ makeRenderableFeatures(1, 4),
170
+ ],
171
+ renderable_feature_removed: [
172
+ makeRenderableFeatures(1, 1),
173
+ makeRenderableFeatures(1, 3),
174
+ ],
175
+ renderable_feature_changed: [
176
+ makeRenderableFeatures(1, 1),
177
+ makeRenderableFeatures(1, 4),
178
+ ],
179
+ }),
180
+ );
181
+
182
+ eventManager.removeDuplicatedTransactionEvents({
183
+ add: {
184
+ key: "renderable_feature_added",
185
+ },
186
+ remove: {
187
+ key: "renderable_feature_removed",
188
+ },
189
+ change: {
190
+ key: "renderable_feature_changed",
191
+ },
192
+ });
193
+
194
+ expect(eventManager.stacks.renderable_feature_added).toEqual([
195
+ makeRenderableFeatures(1, 2),
196
+ makeRenderableFeatures(1, 4),
197
+ ]);
198
+ expect(eventManager.stacks.renderable_feature_removed).toEqual([]);
199
+ expect(eventManager.stacks.renderable_feature_changed).toEqual([
200
+ makeRenderableFeatures(1, 4),
201
+ ]);
202
+ });
203
+
204
+ it("should handle the increment in async boundary", async () => {
205
+ const eventManager = new EventManager();
206
+
207
+ // Simulate a concurrency limiter that allows max 2 concurrent operations
208
+ const maxConcurrent = 2;
209
+ let currentConcurrent = 0;
210
+ let processedCount = 0;
211
+
212
+ const canIncrement = () => currentConcurrent < maxConcurrent;
213
+ const increment = () => {
214
+ currentConcurrent++;
215
+ };
216
+ const decrement = () => {
217
+ currentConcurrent--;
218
+ };
219
+
220
+ const doAsync = async (t: number) => {
221
+ increment();
222
+ await wait(t);
223
+ decrement();
224
+ processedCount++;
225
+ };
226
+
227
+ const addedFeatures: RenderableFeatureAddedEvent[] = [
228
+ makeRenderableFeatures(1, 1),
229
+ makeRenderableFeatures(1, 2),
230
+ makeRenderableFeatures(1, 3),
231
+ makeRenderableFeatures(1, 4),
232
+ makeRenderableFeatures(1, 5),
233
+ ];
234
+
235
+ eventManager.pushEvents(
236
+ makeEvent({
237
+ renderable_feature_added: addedFeatures,
238
+ renderable_feature_removed: [],
239
+ renderable_feature_changed: [],
240
+ }),
241
+ );
242
+
243
+ const waitingTime = 10;
244
+
245
+ eventManager.processTransactionEvents(
246
+ "renderableFeature",
247
+ {
248
+ add: {
249
+ key: "renderable_feature_added",
250
+ max: 10,
251
+ },
252
+ remove: {
253
+ key: "renderable_feature_removed",
254
+ },
255
+ change: {
256
+ key: "renderable_feature_changed",
257
+ },
258
+ },
259
+ async ({ type }) => {
260
+ if (type === "add") {
261
+ await doAsync(waitingTime);
262
+ }
263
+ },
264
+ {
265
+ shouldProcess: ({ type }) => {
266
+ if (type === "add") {
267
+ return canIncrement();
268
+ }
269
+ return true;
270
+ },
271
+ },
272
+ );
273
+
274
+ // Wait until all features is processed.
275
+ await wait(waitingTime * (addedFeatures.length + 1));
276
+
277
+ await vi.waitFor(() => expect(processedCount).toBe(2));
278
+ });
279
+
280
+ it("should keep skipped events in stack when shouldProcess returns false", async () => {
281
+ const eventManager = new EventManager();
282
+
283
+ const processedIds: number[] = [];
284
+
285
+ // Add 5 events to the stack
286
+ eventManager.pushEvents(
287
+ makeEvent({
288
+ renderable_feature_added: [
289
+ makeRenderableFeatures(1, 1),
290
+ makeRenderableFeatures(1, 2),
291
+ makeRenderableFeatures(1, 3),
292
+ makeRenderableFeatures(1, 4),
293
+ makeRenderableFeatures(1, 5),
294
+ ],
295
+ renderable_feature_removed: [],
296
+ renderable_feature_changed: [],
297
+ }),
298
+ );
299
+
300
+ // First pass: only process events with odd gen (1, 3, 5)
301
+ await eventManager.forEachStackAsync(
302
+ "renderable_feature_added",
303
+ async (event) => {
304
+ processedIds.push(event.gen);
305
+ },
306
+ 10,
307
+ (event) => event.gen % 2 === 1, // Only process odd gen
308
+ );
309
+
310
+ // Should have processed 1, 3, 5
311
+ expect(processedIds).toEqual([1, 3, 5]);
312
+
313
+ // Events with gen 2, 4 should remain in the stack
314
+ expect(eventManager.stacks.renderable_feature_added).toHaveLength(2);
315
+ expect(eventManager.stacks.renderable_feature_added[0]).toMatchObject({
316
+ gen: 2,
317
+ });
318
+ expect(eventManager.stacks.renderable_feature_added[1]).toMatchObject({
319
+ gen: 4,
320
+ });
321
+
322
+ // Second pass: process remaining events
323
+ processedIds.length = 0;
324
+ await eventManager.forEachStackAsync(
325
+ "renderable_feature_added",
326
+ async (event) => {
327
+ processedIds.push(event.gen);
328
+ },
329
+ 10,
330
+ );
331
+
332
+ // Should have processed 2, 4
333
+ expect(processedIds).toEqual([2, 4]);
334
+
335
+ // Stack should now be empty
336
+ expect(eventManager.stacks.renderable_feature_added).toHaveLength(0);
337
+ });
338
+
339
+ it("should correctly count max limit including skipped events", async () => {
340
+ const eventManager = new EventManager();
341
+
342
+ const processedIds: number[] = [];
343
+
344
+ // Add 10 events to the stack
345
+ eventManager.pushEvents(
346
+ makeEvent({
347
+ renderable_feature_added: [
348
+ makeRenderableFeatures(1, 1),
349
+ makeRenderableFeatures(1, 2),
350
+ makeRenderableFeatures(1, 3),
351
+ makeRenderableFeatures(1, 4),
352
+ makeRenderableFeatures(1, 5),
353
+ makeRenderableFeatures(1, 6),
354
+ makeRenderableFeatures(1, 7),
355
+ makeRenderableFeatures(1, 8),
356
+ makeRenderableFeatures(1, 9),
357
+ makeRenderableFeatures(1, 10),
358
+ ],
359
+ renderable_feature_removed: [],
360
+ renderable_feature_changed: [],
361
+ }),
362
+ );
363
+
364
+ // Process with max=5, but only process odd gen (1, 3, 5)
365
+ // This tests that idx is incremented for skipped events too,
366
+ // so max limit works correctly (should check first 5 events: 1,2,3,4,5)
367
+ await eventManager.forEachStackAsync(
368
+ "renderable_feature_added",
369
+ async (event) => {
370
+ processedIds.push(event.gen);
371
+ },
372
+ 5, // max limit
373
+ (event) => event.gen % 2 === 1, // Only process odd gen
374
+ );
375
+
376
+ // Should have processed odd events within first 5: 1, 3, 5
377
+ expect(processedIds).toEqual([1, 3, 5]);
378
+
379
+ // Stack should have: 2, 4 (skipped from first 5) + 6, 7, 8, 9, 10 (not checked)
380
+ expect(eventManager.stacks.renderable_feature_added).toHaveLength(7);
381
+ expect(
382
+ eventManager.stacks.renderable_feature_added.map((e) => e.gen),
383
+ ).toEqual([2, 4, 6, 7, 8, 9, 10]);
384
+ });
385
+
386
+ it("should process the remaining events of a batch when one handler rejects", async () => {
387
+ const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
388
+ const eventManager = new EventManager();
389
+
390
+ eventManager.pushEvents(
391
+ makeEvent({
392
+ renderable_feature_added: [
393
+ makeRenderableFeatures(1, 1),
394
+ makeRenderableFeatures(1, 2),
395
+ makeRenderableFeatures(1, 3),
396
+ ],
397
+ }),
398
+ );
399
+
400
+ const processedIds: number[] = [];
401
+ await eventManager.forEachStackAsync(
402
+ "renderable_feature_added",
403
+ async (event) => {
404
+ if (event.gen === 2) throw new Error("boom");
405
+ processedIds.push(event.gen);
406
+ },
407
+ 10,
408
+ );
409
+
410
+ expect(processedIds).toEqual([1, 3]);
411
+ // The failed event still counts as processed: it must leave the stack (and
412
+ // be freed) instead of being retried forever.
413
+ expect(eventManager.stacks.renderable_feature_added).toHaveLength(0);
414
+ expect(consoleError).toHaveBeenCalledTimes(1);
415
+ consoleError.mockRestore();
416
+ });
417
+
418
+ it("should abort the remaining in-flight adds even when an onAbort handler throws", async () => {
419
+ const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
420
+ const eventManager = new EventManager();
421
+
422
+ const aborted: number[] = [];
423
+ let releaseAdds!: () => void;
424
+ const gate = new Promise<void>((resolve) => (releaseAdds = resolve));
425
+
426
+ const options = {
427
+ add: { key: "renderable_feature_added" },
428
+ remove: { key: "renderable_feature_removed" },
429
+ } as const;
430
+ const cb = async ({ type }: { type: string }) => {
431
+ if (type === "add") await gate;
432
+ };
433
+ const handlers = {
434
+ onAbort: (ev: { gen: number }) => {
435
+ if (ev.gen === 1) throw new Error("abort boom");
436
+ aborted.push(ev.gen);
437
+ },
438
+ };
439
+
440
+ // First frame: two adds start and stay pending on the gate.
441
+ eventManager.pushEvents(
442
+ makeEvent({
443
+ renderable_feature_added: [
444
+ makeRenderableFeatures(1, 1),
445
+ makeRenderableFeatures(1, 2),
446
+ ],
447
+ }),
448
+ );
449
+ eventManager.processTransactionEvents("workerTask", options, cb, handlers);
450
+
451
+ // Second frame: removals for both pending adds arrive. The first abort
452
+ // handler throws; the second must still run.
453
+ eventManager.pushEvents(
454
+ makeEvent({
455
+ renderable_feature_removed: [
456
+ makeRenderableFeatures(1, 1),
457
+ makeRenderableFeatures(1, 2),
458
+ ],
459
+ }),
460
+ );
461
+ eventManager.processTransactionEvents("workerTask", options, cb, handlers);
462
+
463
+ expect(aborted).toEqual([2]);
464
+ expect(consoleError).toHaveBeenCalledTimes(1);
465
+
466
+ // Later frames replay the transaction; the pending adds settle and the
467
+ // remove phase drains the stack.
468
+ releaseAdds();
469
+ await vi.waitFor(() => {
470
+ eventManager.processTransactionEvents("workerTask", options, cb, handlers);
471
+ expect(eventManager.stacks.renderable_feature_removed).toHaveLength(0);
472
+ });
473
+ consoleError.mockRestore();
474
+ });
475
+
476
+ it("should keep in-flight add tracking isolated between transaction keys", async () => {
477
+ const eventManager = new EventManager();
478
+
479
+ const aborted: number[] = [];
480
+ let releaseAdds!: () => void;
481
+ const gate = new Promise<void>((resolve) => (releaseAdds = resolve));
482
+
483
+ const optionsA = {
484
+ add: { key: "renderable_feature_added" },
485
+ remove: { key: "renderable_feature_removed" },
486
+ } as const;
487
+ const cbA = async ({ type }: { type: string }) => {
488
+ if (type === "add") await gate;
489
+ };
490
+ const handlersA = {
491
+ onAbort: (ev: { gen: number }) => {
492
+ aborted.push(ev.gen);
493
+ },
494
+ };
495
+
496
+ // Key A: one add starts and stays pending on the gate.
497
+ eventManager.pushEvents(
498
+ makeEvent({
499
+ renderable_feature_added: [makeRenderableFeatures(1, 1)],
500
+ }),
501
+ );
502
+ eventManager.processTransactionEvents("keyA", optionsA, cbA, handlersA);
503
+
504
+ // Key B: runs its add and remove phases to completion, clearing ITS OWN
505
+ // in-flight tracking. Before the per-key split this wiped key A's ids too
506
+ // and A's abort below never fired.
507
+ let processedB = false;
508
+ eventManager.pushEvents(
509
+ makeEvent({
510
+ texture_fragment_removed: [makeRenderableFeatures(9, 9)],
511
+ }),
512
+ );
513
+ const optionsB = {
514
+ add: { key: "texture_fragment_requested" },
515
+ remove: { key: "texture_fragment_removed" },
516
+ } as const;
517
+ const cbB = async ({ type }: { type: string }) => {
518
+ if (type === "remove") processedB = true;
519
+ };
520
+ // Replay across "frames" until key B's remove phase (which clears its
521
+ // tracking) has run.
522
+ await vi.waitFor(() => {
523
+ eventManager.processTransactionEvents("keyB", optionsB, cbB, {
524
+ onAbort: () => {},
525
+ });
526
+ expect(processedB).toBe(true);
527
+ });
528
+
529
+ // A removal for key A's still-pending add arrives; its abort must fire.
530
+ eventManager.pushEvents(
531
+ makeEvent({
532
+ renderable_feature_removed: [makeRenderableFeatures(1, 1)],
533
+ }),
534
+ );
535
+ eventManager.processTransactionEvents("keyA", optionsA, cbA, handlersA);
536
+
537
+ expect(aborted).toEqual([1]);
538
+ releaseAdds();
539
+ });