@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.
- package/.eslintrc.json +28 -0
- package/LICENSE +39 -0
- package/README.md +291 -0
- package/examples/basic-usage.tsx +52 -0
- package/package.json +65 -0
- package/public/hexgrid-worker.js +1763 -0
- package/rust/Cargo.toml +41 -0
- package/rust/src/lib.rs +740 -0
- package/rust/src/math.rs +574 -0
- package/rust/src/spatial.rs +245 -0
- package/rust/src/statistics.rs +496 -0
- package/src/HexGridEnhanced.ts +16 -0
- package/src/Snapshot.ts +1402 -0
- package/src/adapters.ts +65 -0
- package/src/algorithms/AdvancedStatistics.ts +328 -0
- package/src/algorithms/BayesianStatistics.ts +317 -0
- package/src/algorithms/FlowField.ts +126 -0
- package/src/algorithms/FluidSimulation.ts +99 -0
- package/src/algorithms/GraphAlgorithms.ts +184 -0
- package/src/algorithms/OutlierDetection.ts +391 -0
- package/src/algorithms/ParticleSystem.ts +85 -0
- package/src/algorithms/index.ts +13 -0
- package/src/compat.ts +96 -0
- package/src/components/HexGrid.tsx +31 -0
- package/src/components/NarrationOverlay.tsx +221 -0
- package/src/components/index.ts +2 -0
- package/src/features.ts +125 -0
- package/src/index.ts +30 -0
- package/src/math/HexCoordinates.ts +15 -0
- package/src/math/Matrix4.ts +35 -0
- package/src/math/Quaternion.ts +37 -0
- package/src/math/SpatialIndex.ts +114 -0
- package/src/math/Vector3.ts +69 -0
- package/src/math/index.ts +11 -0
- package/src/note-adapter.ts +124 -0
- package/src/ontology-adapter.ts +77 -0
- package/src/stores/index.ts +1 -0
- package/src/stores/uiStore.ts +85 -0
- package/src/types/index.ts +3 -0
- package/src/types.ts +152 -0
- package/src/utils/image-utils.ts +25 -0
- package/src/wasm/HexGridWasmWrapper.ts +753 -0
- package/src/wasm/index.ts +7 -0
- package/src/workers/hexgrid-math.ts +177 -0
- package/src/workers/hexgrid-worker.worker.ts +1807 -0
- 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
|
+
}
|