1use num_traits::AsPrimitive;
4
5#[allow(clippy::identity_op)]
15pub fn construct_kdtree<Real>(
16 tree: &mut Vec<usize>,
17 idx_node: usize,
18 points: &mut Vec<([Real; 2], usize)>,
19 idx_point_begin: usize,
20 idx_point_end: usize,
21 i_depth: i32,
22) where
23 Real: num_traits::Float + Copy,
24{
25 if points.is_empty() {
26 tree.clear();
27 return;
28 }
29 if idx_node == 0 {
30 tree.resize(3, usize::MAX);
31 }
32
33 if idx_point_end - idx_point_begin == 1 {
34 tree[idx_node * 3 + 0] = points[idx_point_begin].1;
36 return;
37 }
38
39 #[allow(clippy::manual_is_multiple_of)]
40 if i_depth % 2 == 0 {
41 points[idx_point_begin..idx_point_end].sort_by(|a, b| a.0[0].partial_cmp(&b.0[0]).unwrap());
43 } else {
44 points[idx_point_begin..idx_point_end].sort_by(|a, b| a.0[1].partial_cmp(&b.0[1]).unwrap());
46 }
47
48 let idx_point_mid = (idx_point_end - idx_point_begin) / 2 + idx_point_begin; tree[idx_node * 3 + 0] = points[idx_point_mid].1;
50
51 if idx_point_begin != idx_point_mid {
52 let idx_node_left = tree.len() / 3;
54 tree.resize(tree.len() + 3, usize::MAX);
55 tree[idx_node * 3 + 1] = idx_node_left;
56 construct_kdtree(
57 tree,
58 idx_node_left,
59 points,
60 idx_point_begin,
61 idx_point_mid,
62 i_depth + 1,
63 );
64 }
65 if idx_point_mid + 1 != idx_point_end {
66 let idx_node_right = tree.len() / 3;
68 tree.resize(tree.len() + 3, usize::MAX);
69 tree[idx_node * 3 + 2] = idx_node_right;
70 construct_kdtree(
71 tree,
72 idx_node_right,
73 points,
74 idx_point_mid + 1,
75 idx_point_end,
76 i_depth + 1,
77 );
78 }
79}
80
81#[allow(clippy::identity_op)]
82pub fn find_edges<Real>(
83 edge2xy: &mut Vec<Real>,
84 vtx2xy: &[Real],
85 nodes: &[usize],
86 idx_node: usize,
87 min: [Real; 2],
88 max: [Real; 2],
89 i_depth: i32,
90) where
91 Real: Copy,
92{
93 if idx_node >= nodes.len() {
94 return;
95 }
96 let ivtx = nodes[idx_node * 3 + 0];
97 let pos = &vtx2xy[ivtx * 2..(ivtx + 1) * 2];
98 #[allow(clippy::manual_is_multiple_of)]
99 if i_depth % 2 == 0 {
100 edge2xy.push(pos[0]);
101 edge2xy.push(min[1]);
102 edge2xy.push(pos[0]);
103 edge2xy.push(max[1]);
104 find_edges(
105 edge2xy,
106 vtx2xy,
107 nodes,
108 nodes[idx_node * 3 + 1],
109 min,
110 [pos[0], max[1]],
111 i_depth + 1,
112 );
113 find_edges(
114 edge2xy,
115 vtx2xy,
116 nodes,
117 nodes[idx_node * 3 + 2],
118 [pos[0], min[1]],
119 max,
120 i_depth + 1,
121 );
122 } else {
123 edge2xy.push(min[0]);
124 edge2xy.push(pos[1]);
125 edge2xy.push(max[0]);
126 edge2xy.push(pos[1]);
127 find_edges(
128 edge2xy,
129 vtx2xy,
130 nodes,
131 nodes[idx_node * 3 + 1],
132 min,
133 [max[0], pos[1]],
134 i_depth + 1,
135 );
136 find_edges(
137 edge2xy,
138 vtx2xy,
139 nodes,
140 nodes[idx_node * 3 + 2],
141 [min[0], pos[1]],
142 max,
143 i_depth + 1,
144 );
145 }
146}
147
148pub struct TreeBranch<'a, Real> {
149 pub vtx2xy: &'a [Real],
150 pub nodes: &'a Vec<usize>,
151 pub idx_node: usize,
152 pub min: [Real; 2],
153 pub max: [Real; 2],
154 pub i_depth: usize,
155}
156
157#[allow(clippy::identity_op)]
158pub fn nearest<Real>(pos_near: &mut ([Real; 2], usize), pos_in: [Real; 2], branch: TreeBranch<Real>)
159where
160 Real: num_traits::Float + Copy + 'static,
161 f64: AsPrimitive<Real>,
162{
163 use del_geo_core::vec2::Vec2;
164 if branch.idx_node >= branch.nodes.len() {
165 return;
166 } let cur_dist = pos_near.0.sub(&pos_in).norm();
169 if cur_dist
170 < del_geo_core::aabb2::sdf(
171 &[branch.min[0], branch.min[1], branch.max[0], branch.max[1]],
172 &pos_in,
173 )
174 {
175 return;
176 }
177
178 let ivtx = branch.nodes[branch.idx_node * 3 + 0];
179 let pos = [branch.vtx2xy[ivtx * 2], branch.vtx2xy[ivtx * 2 + 1]];
180 if pos.sub(&pos_in).norm() < cur_dist {
181 *pos_near = (pos, ivtx); }
183
184 #[allow(clippy::manual_is_multiple_of)]
185 if branch.i_depth % 2 == 0 {
186 if pos_in[0] < pos[0] {
188 nearest(
189 pos_near,
190 pos_in,
191 TreeBranch {
192 vtx2xy: branch.vtx2xy,
193 nodes: branch.nodes,
194 idx_node: branch.nodes[branch.idx_node * 3 + 1],
195 min: branch.min,
196 max: [pos[0], branch.max[1]],
197 i_depth: branch.i_depth + 1,
198 },
199 );
200 nearest(
201 pos_near,
202 pos_in,
203 TreeBranch {
204 vtx2xy: branch.vtx2xy,
205 nodes: branch.nodes,
206 idx_node: branch.nodes[branch.idx_node * 3 + 2],
207 min: [pos[0], branch.min[1]],
208 max: branch.max,
209 i_depth: branch.i_depth + 1,
210 },
211 );
212 } else {
213 nearest(
214 pos_near,
215 pos_in,
216 TreeBranch {
217 vtx2xy: branch.vtx2xy,
218 nodes: branch.nodes,
219 idx_node: branch.nodes[branch.idx_node * 3 + 2],
220 min: [pos[0], branch.min[1]],
221 max: branch.max,
222 i_depth: branch.i_depth + 1,
223 },
224 );
225 nearest(
226 pos_near,
227 pos_in,
228 TreeBranch {
229 vtx2xy: branch.vtx2xy,
230 nodes: branch.nodes,
231 idx_node: branch.nodes[branch.idx_node * 3 + 1],
232 min: branch.min,
233 max: [pos[0], branch.max[1]],
234 i_depth: branch.i_depth + 1,
235 },
236 );
237 }
238 } else {
239 if pos_in[1] < pos[1] {
241 nearest(
242 pos_near,
243 pos_in,
244 TreeBranch {
245 vtx2xy: branch.vtx2xy,
246 nodes: branch.nodes,
247 idx_node: branch.nodes[branch.idx_node * 3 + 1],
248 min: branch.min,
249 max: [branch.max[0], pos[1]],
250 i_depth: branch.i_depth + 1,
251 },
252 );
253 nearest(
254 pos_near,
255 pos_in,
256 TreeBranch {
257 vtx2xy: branch.vtx2xy,
258 nodes: branch.nodes,
259 idx_node: branch.nodes[branch.idx_node * 3 + 2],
260 min: [branch.min[0], pos[1]],
261 max: branch.max,
262 i_depth: branch.i_depth + 1,
263 },
264 );
265 } else {
266 nearest(
267 pos_near,
268 pos_in,
269 TreeBranch {
270 vtx2xy: branch.vtx2xy,
271 nodes: branch.nodes,
272 idx_node: branch.nodes[branch.idx_node * 3 + 2],
273 min: [branch.min[0], pos[1]],
274 max: branch.max,
275 i_depth: branch.i_depth + 1,
276 },
277 );
278 nearest(
279 pos_near,
280 pos_in,
281 TreeBranch {
282 vtx2xy: branch.vtx2xy,
283 nodes: branch.nodes,
284 idx_node: branch.nodes[branch.idx_node * 3 + 1],
285 min: branch.min,
286 max: [branch.max[0], pos[1]],
287 i_depth: branch.i_depth + 1,
288 },
289 );
290 }
291 }
292}
293
294#[allow(clippy::identity_op)]
295pub fn inside_square<Real>(
296 pos_near: &mut Vec<usize>,
297 pos_in: [Real; 2],
298 rad: Real,
299 branch: TreeBranch<Real>,
300) where
301 Real: num_traits::Float + Copy + 'static,
302 f64: AsPrimitive<Real>,
303{
304 if branch.idx_node >= branch.nodes.len() {
305 return;
306 } if !del_geo_core::aabb2::is_intersect_square(
309 &[branch.min[0], branch.min[1], branch.max[0], branch.max[1]],
310 &pos_in,
311 rad,
312 ) {
313 return;
314 }
315
316 let ivtx = branch.nodes[branch.idx_node * 3 + 0];
317 let pos = [branch.vtx2xy[ivtx * 2 + 0], branch.vtx2xy[ivtx * 2 + 1]];
318 if (pos[0] - pos_in[0]).abs() < rad && (pos[1] - pos_in[1]).abs() < rad {
319 pos_near.push(ivtx); }
321 #[allow(clippy::manual_is_multiple_of)]
322 if branch.i_depth % 2 == 0 {
323 inside_square(
325 pos_near,
326 pos_in,
327 rad,
328 TreeBranch {
329 vtx2xy: branch.vtx2xy,
330 nodes: branch.nodes,
331 idx_node: branch.nodes[branch.idx_node * 3 + 2],
332 min: [pos[0], branch.min[1]],
333 max: branch.max,
334 i_depth: branch.i_depth + 1,
335 },
336 );
337 inside_square(
338 pos_near,
339 pos_in,
340 rad,
341 TreeBranch {
342 vtx2xy: branch.vtx2xy,
343 nodes: branch.nodes,
344 idx_node: branch.nodes[branch.idx_node * 3 + 1],
345 min: branch.min,
346 max: [pos[0], branch.max[1]],
347 i_depth: branch.i_depth + 1,
348 },
349 );
350 } else {
351 inside_square(
353 pos_near,
354 pos_in,
355 rad,
356 TreeBranch {
357 vtx2xy: branch.vtx2xy,
358 nodes: branch.nodes,
359 idx_node: branch.nodes[branch.idx_node * 3 + 1],
360 min: branch.min,
361 max: [branch.max[0], pos[1]],
362 i_depth: branch.i_depth + 1,
363 },
364 );
365 inside_square(
366 pos_near,
367 pos_in,
368 rad,
369 TreeBranch {
370 vtx2xy: branch.vtx2xy,
371 nodes: branch.nodes,
372 idx_node: branch.nodes[branch.idx_node * 3 + 2],
373 min: [branch.min[0], pos[1]],
374 max: branch.max,
375 i_depth: branch.i_depth + 1,
376 },
377 );
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use crate::kdtree2::TreeBranch;
384 use num_traits::AsPrimitive;
385
386 fn test_data<Real>(num_xy: usize) -> (Vec<Real>, Vec<usize>)
387 where
388 Real: num_traits::Float + 'static + Copy,
389 f64: AsPrimitive<Real>,
390 rand::distr::StandardUniform: rand::distr::Distribution<Real>,
391 {
392 let xys = {
393 let mut rng: rand::rngs::StdRng = rand::SeedableRng::from_seed([13_u8; 32]);
394 let rad: Real = 0.4_f64.as_();
395 let half: Real = 0.4_f64.as_();
396 let mut ps = Vec::<Real>::with_capacity(num_xy * 2);
397 for _i in 0..num_xy {
398 use rand::RngExt;
399 let x: Real = (rng.random::<Real>() * 2_f64.as_() - Real::one()) * rad + half;
400 let y: Real = (rng.random::<Real>() * 2_f64.as_() - Real::one()) * rad + half;
401 ps.push(x);
402 ps.push(y);
403 }
404 ps
405 };
406 let tree = {
407 let mut pairs_xy_idx = xys
408 .chunks(2)
409 .enumerate()
410 .map(|(ivtx, xy)| ([xy[0], xy[1]], ivtx))
411 .collect();
412 let mut tree = Vec::<usize>::new();
413 crate::kdtree2::construct_kdtree(&mut tree, 0, &mut pairs_xy_idx, 0, xys.len() / 2, 0);
414 tree
415 };
416 (xys, tree)
417 }
418
419 #[test]
420 fn check_nearest_raw() {
421 use crate::kdtree2::nearest;
422 use del_geo_core::vec2::Vec2;
423 type Real = f64;
425 let (vtx2xy, nodes) = test_data::<Real>(10000);
426 let mut rng: rand::rngs::StdRng = rand::SeedableRng::from_seed([13_u8; 32]);
427 for _ in 0..10000 {
429 use rand::RngExt;
430 let p0 = [rng.random::<Real>(), rng.random::<Real>()];
431 let mut pos_near = ([Real::MAX, Real::MAX], usize::MAX);
432 nearest(
433 &mut pos_near,
434 p0,
435 TreeBranch {
436 vtx2xy: &vtx2xy,
437 nodes: &nodes,
438 idx_node: 0,
439 min: [0., 0.],
440 max: [1., 1.],
441 i_depth: 0,
442 },
443 );
444 }
445 for _ in 0..10000 {
447 use rand::RngExt;
448 let p0 = [rng.random::<Real>(), rng.random::<Real>()];
449 let mut pos_near = ([Real::MAX, Real::MAX], usize::MAX);
450 nearest(
451 &mut pos_near,
452 p0,
453 TreeBranch {
454 vtx2xy: &vtx2xy,
455 nodes: &nodes,
456 idx_node: 0,
457 min: [0., 0.],
458 max: [1., 1.],
459 i_depth: 0,
460 },
461 );
462 let dist_min = pos_near.0.sub(&p0).norm();
463 for xy in vtx2xy.chunks(2) {
464 let xy = arrayref::array_ref![xy, 0, 2];
465 assert!(xy.sub(&p0).norm() >= dist_min);
466 }
467 }
468 }
469
470 #[test]
471 fn check_inside_square_raw() {
472 type Real = f64;
474 let (vtx2xy, nodes) = test_data::<Real>(10000);
475 let mut rng: rand::rngs::StdRng = rand::SeedableRng::from_seed([13_u8; 32]);
476 let rad: Real = 0.03;
477 for _ in 0..10000 {
479 use rand::RngExt;
480 let p0 = [rng.random::<Real>(), rng.random::<Real>()];
481 let mut pos_near = Vec::<usize>::new();
482 crate::kdtree2::inside_square(
483 &mut pos_near,
484 p0,
485 rad,
486 TreeBranch {
487 vtx2xy: &vtx2xy,
488 nodes: &nodes,
489 idx_node: 0,
490 min: [0., 0.],
491 max: [1., 1.],
492 i_depth: 0,
493 },
494 );
495 }
496 for _ in 0..10000 {
499 use rand::RngExt;
500 let p0 = [rng.random::<Real>(), rng.random::<Real>()];
501 let mut idxs0 = Vec::<usize>::new();
502 crate::kdtree2::inside_square(
503 &mut idxs0,
504 p0,
505 rad,
506 TreeBranch {
507 vtx2xy: &vtx2xy,
508 nodes: &nodes,
509 idx_node: 0,
510 min: [0., 0.],
511 max: [1., 1.],
512 i_depth: 0,
513 },
514 );
515 let idxs1: Vec<usize> = vtx2xy
516 .chunks(2)
517 .enumerate()
518 .filter(|(_, xy)| (xy[0] - p0[0]).abs() < rad && (xy[1] - p0[1]).abs() < rad)
519 .map(|v| v.0)
520 .collect();
521 let idxs1 = std::collections::BTreeSet::from_iter(idxs1.iter());
522 let idxs0 = std::collections::BTreeSet::from_iter(idxs0.iter());
523 assert_eq!(idxs1, idxs0);
524 }
525 }
526}