@buley/hexgrid-3d 1.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 (46) hide show
  1. package/.eslintrc.json +28 -0
  2. package/LICENSE +39 -0
  3. package/README.md +291 -0
  4. package/examples/basic-usage.tsx +52 -0
  5. package/package.json +65 -0
  6. package/public/hexgrid-worker.js +1763 -0
  7. package/rust/Cargo.toml +41 -0
  8. package/rust/src/lib.rs +740 -0
  9. package/rust/src/math.rs +574 -0
  10. package/rust/src/spatial.rs +245 -0
  11. package/rust/src/statistics.rs +496 -0
  12. package/src/HexGridEnhanced.ts +16 -0
  13. package/src/Snapshot.ts +1402 -0
  14. package/src/adapters.ts +65 -0
  15. package/src/algorithms/AdvancedStatistics.ts +328 -0
  16. package/src/algorithms/BayesianStatistics.ts +317 -0
  17. package/src/algorithms/FlowField.ts +126 -0
  18. package/src/algorithms/FluidSimulation.ts +99 -0
  19. package/src/algorithms/GraphAlgorithms.ts +184 -0
  20. package/src/algorithms/OutlierDetection.ts +391 -0
  21. package/src/algorithms/ParticleSystem.ts +85 -0
  22. package/src/algorithms/index.ts +13 -0
  23. package/src/compat.ts +96 -0
  24. package/src/components/HexGrid.tsx +31 -0
  25. package/src/components/NarrationOverlay.tsx +221 -0
  26. package/src/components/index.ts +2 -0
  27. package/src/features.ts +125 -0
  28. package/src/index.ts +30 -0
  29. package/src/math/HexCoordinates.ts +15 -0
  30. package/src/math/Matrix4.ts +35 -0
  31. package/src/math/Quaternion.ts +37 -0
  32. package/src/math/SpatialIndex.ts +114 -0
  33. package/src/math/Vector3.ts +69 -0
  34. package/src/math/index.ts +11 -0
  35. package/src/note-adapter.ts +124 -0
  36. package/src/ontology-adapter.ts +77 -0
  37. package/src/stores/index.ts +1 -0
  38. package/src/stores/uiStore.ts +85 -0
  39. package/src/types/index.ts +3 -0
  40. package/src/types.ts +152 -0
  41. package/src/utils/image-utils.ts +25 -0
  42. package/src/wasm/HexGridWasmWrapper.ts +753 -0
  43. package/src/wasm/index.ts +7 -0
  44. package/src/workers/hexgrid-math.ts +177 -0
  45. package/src/workers/hexgrid-worker.worker.ts +1807 -0
  46. package/tsconfig.json +18 -0
@@ -0,0 +1,574 @@
1
+ //! SIMD-optimized vector math operations
2
+
3
+ /// 2D Vector
4
+ #[derive(Clone, Copy, Debug, Default)]
5
+ pub struct Vec2 {
6
+ pub x: f32,
7
+ pub y: f32,
8
+ }
9
+
10
+ impl Vec2 {
11
+ #[inline]
12
+ pub const fn new(x: f32, y: f32) -> Self {
13
+ Self { x, y }
14
+ }
15
+
16
+ #[inline]
17
+ pub const fn zero() -> Self {
18
+ Self { x: 0.0, y: 0.0 }
19
+ }
20
+
21
+ #[inline]
22
+ pub fn magnitude(&self) -> f32 {
23
+ (self.x * self.x + self.y * self.y).sqrt()
24
+ }
25
+
26
+ #[inline]
27
+ pub fn magnitude_squared(&self) -> f32 {
28
+ self.x * self.x + self.y * self.y
29
+ }
30
+
31
+ #[inline]
32
+ pub fn normalize(&self) -> Self {
33
+ let mag = self.magnitude();
34
+ if mag > 0.0001 {
35
+ Self {
36
+ x: self.x / mag,
37
+ y: self.y / mag,
38
+ }
39
+ } else {
40
+ Self::zero()
41
+ }
42
+ }
43
+
44
+ #[inline]
45
+ pub fn dot(&self, other: &Self) -> f32 {
46
+ self.x * other.x + self.y * other.y
47
+ }
48
+
49
+ #[inline]
50
+ pub fn cross(&self, other: &Self) -> f32 {
51
+ self.x * other.y - self.y * other.x
52
+ }
53
+
54
+ #[inline]
55
+ pub fn add(&self, other: &Self) -> Self {
56
+ Self {
57
+ x: self.x + other.x,
58
+ y: self.y + other.y,
59
+ }
60
+ }
61
+
62
+ #[inline]
63
+ pub fn sub(&self, other: &Self) -> Self {
64
+ Self {
65
+ x: self.x - other.x,
66
+ y: self.y - other.y,
67
+ }
68
+ }
69
+
70
+ #[inline]
71
+ pub fn scale(&self, s: f32) -> Self {
72
+ Self {
73
+ x: self.x * s,
74
+ y: self.y * s,
75
+ }
76
+ }
77
+
78
+ #[inline]
79
+ pub fn lerp(&self, other: &Self, t: f32) -> Self {
80
+ Self {
81
+ x: self.x + (other.x - self.x) * t,
82
+ y: self.y + (other.y - self.y) * t,
83
+ }
84
+ }
85
+
86
+ #[inline]
87
+ pub fn distance(&self, other: &Self) -> f32 {
88
+ self.sub(other).magnitude()
89
+ }
90
+
91
+ #[inline]
92
+ pub fn rotate(&self, angle: f32) -> Self {
93
+ let cos = angle.cos();
94
+ let sin = angle.sin();
95
+ Self {
96
+ x: self.x * cos - self.y * sin,
97
+ y: self.x * sin + self.y * cos,
98
+ }
99
+ }
100
+
101
+ #[inline]
102
+ pub fn perpendicular(&self) -> Self {
103
+ Self { x: -self.y, y: self.x }
104
+ }
105
+
106
+ #[inline]
107
+ pub fn reflect(&self, normal: &Self) -> Self {
108
+ let d = self.dot(normal) * 2.0;
109
+ Self {
110
+ x: self.x - normal.x * d,
111
+ y: self.y - normal.y * d,
112
+ }
113
+ }
114
+
115
+ #[inline]
116
+ pub fn angle(&self) -> f32 {
117
+ self.y.atan2(self.x)
118
+ }
119
+
120
+ #[inline]
121
+ pub fn from_angle(angle: f32) -> Self {
122
+ Self {
123
+ x: angle.cos(),
124
+ y: angle.sin(),
125
+ }
126
+ }
127
+ }
128
+
129
+ /// 3D Vector
130
+ #[derive(Clone, Copy, Debug, Default)]
131
+ pub struct Vec3 {
132
+ pub x: f32,
133
+ pub y: f32,
134
+ pub z: f32,
135
+ }
136
+
137
+ impl Vec3 {
138
+ #[inline]
139
+ pub const fn new(x: f32, y: f32, z: f32) -> Self {
140
+ Self { x, y, z }
141
+ }
142
+
143
+ #[inline]
144
+ pub const fn zero() -> Self {
145
+ Self { x: 0.0, y: 0.0, z: 0.0 }
146
+ }
147
+
148
+ #[inline]
149
+ pub fn magnitude(&self) -> f32 {
150
+ (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
151
+ }
152
+
153
+ #[inline]
154
+ pub fn magnitude_squared(&self) -> f32 {
155
+ self.x * self.x + self.y * self.y + self.z * self.z
156
+ }
157
+
158
+ #[inline]
159
+ pub fn normalize(&self) -> Self {
160
+ let mag = self.magnitude();
161
+ if mag > 0.0001 {
162
+ Self {
163
+ x: self.x / mag,
164
+ y: self.y / mag,
165
+ z: self.z / mag,
166
+ }
167
+ } else {
168
+ Self::zero()
169
+ }
170
+ }
171
+
172
+ #[inline]
173
+ pub fn dot(&self, other: &Self) -> f32 {
174
+ self.x * other.x + self.y * other.y + self.z * other.z
175
+ }
176
+
177
+ #[inline]
178
+ pub fn cross(&self, other: &Self) -> Self {
179
+ Self {
180
+ x: self.y * other.z - self.z * other.y,
181
+ y: self.z * other.x - self.x * other.z,
182
+ z: self.x * other.y - self.y * other.x,
183
+ }
184
+ }
185
+
186
+ #[inline]
187
+ pub fn add(&self, other: &Self) -> Self {
188
+ Self {
189
+ x: self.x + other.x,
190
+ y: self.y + other.y,
191
+ z: self.z + other.z,
192
+ }
193
+ }
194
+
195
+ #[inline]
196
+ pub fn sub(&self, other: &Self) -> Self {
197
+ Self {
198
+ x: self.x - other.x,
199
+ y: self.y - other.y,
200
+ z: self.z - other.z,
201
+ }
202
+ }
203
+
204
+ #[inline]
205
+ pub fn scale(&self, s: f32) -> Self {
206
+ Self {
207
+ x: self.x * s,
208
+ y: self.y * s,
209
+ z: self.z * s,
210
+ }
211
+ }
212
+
213
+ #[inline]
214
+ pub fn lerp(&self, other: &Self, t: f32) -> Self {
215
+ Self {
216
+ x: self.x + (other.x - self.x) * t,
217
+ y: self.y + (other.y - self.y) * t,
218
+ z: self.z + (other.z - self.z) * t,
219
+ }
220
+ }
221
+
222
+ #[inline]
223
+ pub fn distance(&self, other: &Self) -> f32 {
224
+ self.sub(other).magnitude()
225
+ }
226
+
227
+ /// Convert to spherical coordinates (r, theta, phi)
228
+ #[inline]
229
+ pub fn to_spherical(&self) -> (f32, f32, f32) {
230
+ let r = self.magnitude();
231
+ if r < 0.0001 {
232
+ return (0.0, 0.0, 0.0);
233
+ }
234
+ let theta = (self.z / r).acos();
235
+ let phi = self.y.atan2(self.x);
236
+ (r, theta, phi)
237
+ }
238
+
239
+ /// Create from spherical coordinates
240
+ #[inline]
241
+ pub fn from_spherical(r: f32, theta: f32, phi: f32) -> Self {
242
+ Self {
243
+ x: r * theta.sin() * phi.cos(),
244
+ y: r * theta.sin() * phi.sin(),
245
+ z: r * theta.cos(),
246
+ }
247
+ }
248
+
249
+ /// Convert lat/lng to 3D point on unit sphere
250
+ #[inline]
251
+ pub fn from_lat_lng(lat: f32, lng: f32) -> Self {
252
+ let lat_rad = lat.to_radians();
253
+ let lng_rad = lng.to_radians();
254
+ Self {
255
+ x: lat_rad.cos() * lng_rad.cos(),
256
+ y: lat_rad.cos() * lng_rad.sin(),
257
+ z: lat_rad.sin(),
258
+ }
259
+ }
260
+
261
+ /// Convert point on unit sphere to lat/lng
262
+ #[inline]
263
+ pub fn to_lat_lng(&self) -> (f32, f32) {
264
+ let lat = self.z.asin().to_degrees();
265
+ let lng = self.y.atan2(self.x).to_degrees();
266
+ (lat, lng)
267
+ }
268
+ }
269
+
270
+ /// 4x4 Matrix (column-major)
271
+ #[derive(Clone, Copy, Debug)]
272
+ pub struct Mat4 {
273
+ pub data: [f32; 16],
274
+ }
275
+
276
+ impl Mat4 {
277
+ pub fn identity() -> Self {
278
+ Self {
279
+ data: [
280
+ 1.0, 0.0, 0.0, 0.0,
281
+ 0.0, 1.0, 0.0, 0.0,
282
+ 0.0, 0.0, 1.0, 0.0,
283
+ 0.0, 0.0, 0.0, 1.0,
284
+ ],
285
+ }
286
+ }
287
+
288
+ pub fn translation(x: f32, y: f32, z: f32) -> Self {
289
+ Self {
290
+ data: [
291
+ 1.0, 0.0, 0.0, 0.0,
292
+ 0.0, 1.0, 0.0, 0.0,
293
+ 0.0, 0.0, 1.0, 0.0,
294
+ x, y, z, 1.0,
295
+ ],
296
+ }
297
+ }
298
+
299
+ pub fn scale(x: f32, y: f32, z: f32) -> Self {
300
+ Self {
301
+ data: [
302
+ x, 0.0, 0.0, 0.0,
303
+ 0.0, y, 0.0, 0.0,
304
+ 0.0, 0.0, z, 0.0,
305
+ 0.0, 0.0, 0.0, 1.0,
306
+ ],
307
+ }
308
+ }
309
+
310
+ pub fn rotation_x(angle: f32) -> Self {
311
+ let c = angle.cos();
312
+ let s = angle.sin();
313
+ Self {
314
+ data: [
315
+ 1.0, 0.0, 0.0, 0.0,
316
+ 0.0, c, s, 0.0,
317
+ 0.0, -s, c, 0.0,
318
+ 0.0, 0.0, 0.0, 1.0,
319
+ ],
320
+ }
321
+ }
322
+
323
+ pub fn rotation_y(angle: f32) -> Self {
324
+ let c = angle.cos();
325
+ let s = angle.sin();
326
+ Self {
327
+ data: [
328
+ c, 0.0, -s, 0.0,
329
+ 0.0, 1.0, 0.0, 0.0,
330
+ s, 0.0, c, 0.0,
331
+ 0.0, 0.0, 0.0, 1.0,
332
+ ],
333
+ }
334
+ }
335
+
336
+ pub fn rotation_z(angle: f32) -> Self {
337
+ let c = angle.cos();
338
+ let s = angle.sin();
339
+ Self {
340
+ data: [
341
+ c, s, 0.0, 0.0,
342
+ -s, c, 0.0, 0.0,
343
+ 0.0, 0.0, 1.0, 0.0,
344
+ 0.0, 0.0, 0.0, 1.0,
345
+ ],
346
+ }
347
+ }
348
+
349
+ pub fn perspective(fov: f32, aspect: f32, near: f32, far: f32) -> Self {
350
+ let f = 1.0 / (fov / 2.0).tan();
351
+ let nf = 1.0 / (near - far);
352
+
353
+ Self {
354
+ data: [
355
+ f / aspect, 0.0, 0.0, 0.0,
356
+ 0.0, f, 0.0, 0.0,
357
+ 0.0, 0.0, (far + near) * nf, -1.0,
358
+ 0.0, 0.0, 2.0 * far * near * nf, 0.0,
359
+ ],
360
+ }
361
+ }
362
+
363
+ pub fn multiply(&self, other: &Self) -> Self {
364
+ let mut result = [0.0f32; 16];
365
+
366
+ for i in 0..4 {
367
+ for j in 0..4 {
368
+ result[i * 4 + j] =
369
+ self.data[0 * 4 + j] * other.data[i * 4 + 0] +
370
+ self.data[1 * 4 + j] * other.data[i * 4 + 1] +
371
+ self.data[2 * 4 + j] * other.data[i * 4 + 2] +
372
+ self.data[3 * 4 + j] * other.data[i * 4 + 3];
373
+ }
374
+ }
375
+
376
+ Self { data: result }
377
+ }
378
+
379
+ pub fn transform_point(&self, v: &Vec3) -> Vec3 {
380
+ let w = self.data[3] * v.x + self.data[7] * v.y + self.data[11] * v.z + self.data[15];
381
+ Vec3 {
382
+ x: (self.data[0] * v.x + self.data[4] * v.y + self.data[8] * v.z + self.data[12]) / w,
383
+ y: (self.data[1] * v.x + self.data[5] * v.y + self.data[9] * v.z + self.data[13]) / w,
384
+ z: (self.data[2] * v.x + self.data[6] * v.y + self.data[10] * v.z + self.data[14]) / w,
385
+ }
386
+ }
387
+
388
+ pub fn transform_direction(&self, v: &Vec3) -> Vec3 {
389
+ Vec3 {
390
+ x: self.data[0] * v.x + self.data[4] * v.y + self.data[8] * v.z,
391
+ y: self.data[1] * v.x + self.data[5] * v.y + self.data[9] * v.z,
392
+ z: self.data[2] * v.x + self.data[6] * v.y + self.data[10] * v.z,
393
+ }
394
+ }
395
+ }
396
+
397
+ /// Quaternion for rotations
398
+ #[derive(Clone, Copy, Debug)]
399
+ pub struct Quat {
400
+ pub x: f32,
401
+ pub y: f32,
402
+ pub z: f32,
403
+ pub w: f32,
404
+ }
405
+
406
+ impl Quat {
407
+ pub const fn identity() -> Self {
408
+ Self { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }
409
+ }
410
+
411
+ pub fn from_axis_angle(axis: &Vec3, angle: f32) -> Self {
412
+ let half = angle / 2.0;
413
+ let s = half.sin();
414
+ let normalized = axis.normalize();
415
+ Self {
416
+ x: normalized.x * s,
417
+ y: normalized.y * s,
418
+ z: normalized.z * s,
419
+ w: half.cos(),
420
+ }
421
+ }
422
+
423
+ pub fn from_euler(x: f32, y: f32, z: f32) -> Self {
424
+ let cx = (x / 2.0).cos();
425
+ let sx = (x / 2.0).sin();
426
+ let cy = (y / 2.0).cos();
427
+ let sy = (y / 2.0).sin();
428
+ let cz = (z / 2.0).cos();
429
+ let sz = (z / 2.0).sin();
430
+
431
+ Self {
432
+ x: sx * cy * cz - cx * sy * sz,
433
+ y: cx * sy * cz + sx * cy * sz,
434
+ z: cx * cy * sz - sx * sy * cz,
435
+ w: cx * cy * cz + sx * sy * sz,
436
+ }
437
+ }
438
+
439
+ pub fn magnitude(&self) -> f32 {
440
+ (self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt()
441
+ }
442
+
443
+ pub fn normalize(&self) -> Self {
444
+ let mag = self.magnitude();
445
+ if mag > 0.0001 {
446
+ Self {
447
+ x: self.x / mag,
448
+ y: self.y / mag,
449
+ z: self.z / mag,
450
+ w: self.w / mag,
451
+ }
452
+ } else {
453
+ Self::identity()
454
+ }
455
+ }
456
+
457
+ pub fn conjugate(&self) -> Self {
458
+ Self {
459
+ x: -self.x,
460
+ y: -self.y,
461
+ z: -self.z,
462
+ w: self.w,
463
+ }
464
+ }
465
+
466
+ pub fn multiply(&self, other: &Self) -> Self {
467
+ Self {
468
+ x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
469
+ y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
470
+ z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w,
471
+ w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
472
+ }
473
+ }
474
+
475
+ pub fn rotate_vector(&self, v: &Vec3) -> Vec3 {
476
+ let qv = Self { x: v.x, y: v.y, z: v.z, w: 0.0 };
477
+ let result = self.multiply(&qv).multiply(&self.conjugate());
478
+ Vec3 { x: result.x, y: result.y, z: result.z }
479
+ }
480
+
481
+ pub fn slerp(&self, other: &Self, t: f32) -> Self {
482
+ let mut dot = self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w;
483
+
484
+ let (other, dot) = if dot < 0.0 {
485
+ (Self { x: -other.x, y: -other.y, z: -other.z, w: -other.w }, -dot)
486
+ } else {
487
+ (*other, dot)
488
+ };
489
+
490
+ if dot > 0.9995 {
491
+ // Linear interpolation for very close quaternions
492
+ Self {
493
+ x: self.x + t * (other.x - self.x),
494
+ y: self.y + t * (other.y - self.y),
495
+ z: self.z + t * (other.z - self.z),
496
+ w: self.w + t * (other.w - self.w),
497
+ }.normalize()
498
+ } else {
499
+ let theta = dot.acos();
500
+ let sin_theta = theta.sin();
501
+ let s0 = ((1.0 - t) * theta).sin() / sin_theta;
502
+ let s1 = (t * theta).sin() / sin_theta;
503
+
504
+ Self {
505
+ x: s0 * self.x + s1 * other.x,
506
+ y: s0 * self.y + s1 * other.y,
507
+ z: s0 * self.z + s1 * other.z,
508
+ w: s0 * self.w + s1 * other.w,
509
+ }
510
+ }
511
+ }
512
+
513
+ pub fn to_matrix4(&self) -> Mat4 {
514
+ let x2 = self.x + self.x;
515
+ let y2 = self.y + self.y;
516
+ let z2 = self.z + self.z;
517
+
518
+ let xx = self.x * x2;
519
+ let xy = self.x * y2;
520
+ let xz = self.x * z2;
521
+ let yy = self.y * y2;
522
+ let yz = self.y * z2;
523
+ let zz = self.z * z2;
524
+ let wx = self.w * x2;
525
+ let wy = self.w * y2;
526
+ let wz = self.w * z2;
527
+
528
+ Mat4 {
529
+ data: [
530
+ 1.0 - (yy + zz), xy + wz, xz - wy, 0.0,
531
+ xy - wz, 1.0 - (xx + zz), yz + wx, 0.0,
532
+ xz + wy, yz - wx, 1.0 - (xx + yy), 0.0,
533
+ 0.0, 0.0, 0.0, 1.0,
534
+ ],
535
+ }
536
+ }
537
+ }
538
+
539
+ // ═══════════════════════════════════════════════════════════════════════════
540
+ // BATCH OPERATIONS (for SIMD-style processing)
541
+ // ═══════════════════════════════════════════════════════════════════════════
542
+
543
+ /// Batch transform an array of points
544
+ pub fn batch_transform_points(points: &mut [(f32, f32, f32)], matrix: &Mat4) {
545
+ for (x, y, z) in points.iter_mut() {
546
+ let v = Vec3::new(*x, *y, *z);
547
+ let result = matrix.transform_point(&v);
548
+ *x = result.x;
549
+ *y = result.y;
550
+ *z = result.z;
551
+ }
552
+ }
553
+
554
+ /// Batch compute distances
555
+ pub fn batch_distances(points: &[(f32, f32)], target: (f32, f32)) -> Vec<f32> {
556
+ points.iter()
557
+ .map(|(x, y)| {
558
+ let dx = x - target.0;
559
+ let dy = y - target.1;
560
+ (dx * dx + dy * dy).sqrt()
561
+ })
562
+ .collect()
563
+ }
564
+
565
+ /// Batch normalize vectors
566
+ pub fn batch_normalize(vectors: &mut [(f32, f32)]) {
567
+ for (x, y) in vectors.iter_mut() {
568
+ let mag = (*x * *x + *y * *y).sqrt();
569
+ if mag > 0.0001 {
570
+ *x /= mag;
571
+ *y /= mag;
572
+ }
573
+ }
574
+ }