@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,245 @@
1
+ //! Spatial indexing structures for fast queries
2
+
3
+ use std::collections::HashMap;
4
+
5
+ /// KD-Tree for 2D points
6
+ pub struct KDTree {
7
+ nodes: Vec<KDNode>,
8
+ dimension: usize,
9
+ }
10
+
11
+ struct KDNode {
12
+ point: [f32; 2],
13
+ data_index: usize,
14
+ left: Option<usize>,
15
+ right: Option<usize>,
16
+ }
17
+
18
+ impl KDTree {
19
+ pub fn new() -> Self {
20
+ Self {
21
+ nodes: Vec::new(),
22
+ dimension: 2,
23
+ }
24
+ }
25
+
26
+ pub fn build(points: &[(f32, f32, usize)]) -> Self {
27
+ let mut tree = Self::new();
28
+ if points.is_empty() {
29
+ return tree;
30
+ }
31
+
32
+ let mut indices: Vec<usize> = (0..points.len()).collect();
33
+ tree.build_recursive(&mut indices, points, 0);
34
+ tree
35
+ }
36
+
37
+ fn build_recursive(
38
+ &mut self,
39
+ indices: &mut [usize],
40
+ points: &[(f32, f32, usize)],
41
+ depth: usize,
42
+ ) -> Option<usize> {
43
+ if indices.is_empty() {
44
+ return None;
45
+ }
46
+
47
+ let axis = depth % self.dimension;
48
+
49
+ // Sort by axis
50
+ indices.sort_by(|&a, &b| {
51
+ let va = if axis == 0 { points[a].0 } else { points[a].1 };
52
+ let vb = if axis == 0 { points[b].0 } else { points[b].1 };
53
+ va.partial_cmp(&vb).unwrap_or(std::cmp::Ordering::Equal)
54
+ });
55
+
56
+ let mid = indices.len() / 2;
57
+ let point_idx = indices[mid];
58
+ let point = points[point_idx];
59
+
60
+ let node_idx = self.nodes.len();
61
+ self.nodes.push(KDNode {
62
+ point: [point.0, point.1],
63
+ data_index: point.2,
64
+ left: None,
65
+ right: None,
66
+ });
67
+
68
+ let (left, right) = indices.split_at_mut(mid);
69
+ let right = &mut right[1..]; // Skip median
70
+
71
+ let left_child = self.build_recursive(left, points, depth + 1);
72
+ let right_child = self.build_recursive(right, points, depth + 1);
73
+
74
+ self.nodes[node_idx].left = left_child;
75
+ self.nodes[node_idx].right = right_child;
76
+
77
+ Some(node_idx)
78
+ }
79
+
80
+ /// Find k nearest neighbors
81
+ pub fn k_nearest(&self, query: (f32, f32), k: usize) -> Vec<(usize, f32)> {
82
+ if self.nodes.is_empty() {
83
+ return Vec::new();
84
+ }
85
+
86
+ let mut best = Vec::with_capacity(k);
87
+ self.k_nearest_recursive(0, [query.0, query.1], k, 0, &mut best);
88
+ best.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
89
+ best
90
+ }
91
+
92
+ fn k_nearest_recursive(
93
+ &self,
94
+ node_idx: usize,
95
+ query: [f32; 2],
96
+ k: usize,
97
+ depth: usize,
98
+ best: &mut Vec<(usize, f32)>,
99
+ ) {
100
+ let node = &self.nodes[node_idx];
101
+ let axis = depth % self.dimension;
102
+
103
+ let dx = query[0] - node.point[0];
104
+ let dy = query[1] - node.point[1];
105
+ let dist_sq = dx * dx + dy * dy;
106
+ let dist = dist_sq.sqrt();
107
+
108
+ // Check if this node is a candidate
109
+ if best.len() < k {
110
+ best.push((node.data_index, dist));
111
+ } else if dist < best.last().unwrap().1 {
112
+ best.pop();
113
+ best.push((node.data_index, dist));
114
+ best.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
115
+ }
116
+
117
+ // Determine which side to search first
118
+ let diff = query[axis] - node.point[axis];
119
+ let (first, second) = if diff < 0.0 {
120
+ (node.left, node.right)
121
+ } else {
122
+ (node.right, node.left)
123
+ };
124
+
125
+ // Search first side
126
+ if let Some(child) = first {
127
+ self.k_nearest_recursive(child, query, k, depth + 1, best);
128
+ }
129
+
130
+ // Check if we need to search the other side
131
+ let worst_dist = if best.len() < k {
132
+ f32::MAX
133
+ } else {
134
+ best.last().unwrap().1
135
+ };
136
+
137
+ if diff.abs() < worst_dist {
138
+ if let Some(child) = second {
139
+ self.k_nearest_recursive(child, query, k, depth + 1, best);
140
+ }
141
+ }
142
+ }
143
+
144
+ /// Range query - find all points within radius
145
+ pub fn range_query(&self, center: (f32, f32), radius: f32) -> Vec<usize> {
146
+ if self.nodes.is_empty() {
147
+ return Vec::new();
148
+ }
149
+
150
+ let mut results = Vec::new();
151
+ self.range_query_recursive(0, [center.0, center.1], radius, 0, &mut results);
152
+ results
153
+ }
154
+
155
+ fn range_query_recursive(
156
+ &self,
157
+ node_idx: usize,
158
+ center: [f32; 2],
159
+ radius: f32,
160
+ depth: usize,
161
+ results: &mut Vec<usize>,
162
+ ) {
163
+ let node = &self.nodes[node_idx];
164
+ let axis = depth % self.dimension;
165
+
166
+ // Check if this point is within radius
167
+ let dx = center[0] - node.point[0];
168
+ let dy = center[1] - node.point[1];
169
+ let dist_sq = dx * dx + dy * dy;
170
+
171
+ if dist_sq <= radius * radius {
172
+ results.push(node.data_index);
173
+ }
174
+
175
+ // Check which children to search
176
+ let diff = center[axis] - node.point[axis];
177
+
178
+ // Search side containing query point
179
+ if diff < radius {
180
+ if let Some(child) = node.left {
181
+ self.range_query_recursive(child, center, radius, depth + 1, results);
182
+ }
183
+ }
184
+ if diff > -radius {
185
+ if let Some(child) = node.right {
186
+ self.range_query_recursive(child, center, radius, depth + 1, results);
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ /// Spatial hash grid for O(1) average case queries
193
+ pub struct SpatialHashGrid {
194
+ cell_size: f32,
195
+ inv_cell_size: f32,
196
+ cells: HashMap<(i32, i32), Vec<usize>>,
197
+ }
198
+
199
+ impl SpatialHashGrid {
200
+ pub fn new(cell_size: f32) -> Self {
201
+ Self {
202
+ cell_size,
203
+ inv_cell_size: 1.0 / cell_size,
204
+ cells: HashMap::new(),
205
+ }
206
+ }
207
+
208
+ fn cell_key(&self, x: f32, y: f32) -> (i32, i32) {
209
+ (
210
+ (x * self.inv_cell_size).floor() as i32,
211
+ (y * self.inv_cell_size).floor() as i32,
212
+ )
213
+ }
214
+
215
+ pub fn insert(&mut self, x: f32, y: f32, index: usize) {
216
+ let key = self.cell_key(x, y);
217
+ self.cells.entry(key).or_insert_with(Vec::new).push(index);
218
+ }
219
+
220
+ pub fn query_radius(&self, x: f32, y: f32, radius: f32) -> Vec<usize> {
221
+ let mut results = Vec::new();
222
+ let cells_to_check = (radius * self.inv_cell_size).ceil() as i32 + 1;
223
+ let center_key = self.cell_key(x, y);
224
+
225
+ for dy in -cells_to_check..=cells_to_check {
226
+ for dx in -cells_to_check..=cells_to_check {
227
+ let key = (center_key.0 + dx, center_key.1 + dy);
228
+ if let Some(indices) = self.cells.get(&key) {
229
+ results.extend(indices.iter().copied());
230
+ }
231
+ }
232
+ }
233
+
234
+ results
235
+ }
236
+
237
+ pub fn query_cell(&self, x: f32, y: f32) -> Option<&Vec<usize>> {
238
+ let key = self.cell_key(x, y);
239
+ self.cells.get(&key)
240
+ }
241
+
242
+ pub fn clear(&mut self) {
243
+ self.cells.clear();
244
+ }
245
+ }