add aabb search and benchmark
This commit is contained in:
parent
0ecee89421
commit
097e294e7f
2 changed files with 133 additions and 27 deletions
|
@ -94,11 +94,32 @@ fn nearest_short_horizon(c: &mut Criterion) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn aabb_search(c: &mut Criterion) {
|
||||||
|
c.bench_function(
|
||||||
|
"search an axis-aligned bounding box 10 units a side",
|
||||||
|
move |b| {
|
||||||
|
let points: Vec<_> = create_random_points(DEFAULT_NUM_POINTS, SEED_1)
|
||||||
|
.into_iter()
|
||||||
|
.map(|[x, y]| Point2d { x, y })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let index = Spindex2d::new(&points);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
b.iter(|| {
|
||||||
|
i += 1;
|
||||||
|
!index.aabb(&points[i - 1], 10.0).is_empty()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
benches,
|
benches,
|
||||||
new_index,
|
new_index,
|
||||||
update_positions,
|
update_positions,
|
||||||
nearest_default_horizon,
|
nearest_default_horizon,
|
||||||
nearest_short_horizon
|
nearest_short_horizon,
|
||||||
|
aabb_search
|
||||||
);
|
);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
137
src/lib.rs
137
src/lib.rs
|
@ -132,72 +132,157 @@ impl Spindex2d {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find up to `k` closest points to `point`.
|
/// Find up to `k` closest points to `point`.
|
||||||
pub fn knn(&self, point: &Point2d, k: usize) -> Option<Vec<Point2d>> {
|
pub fn knn(&self, point: &Point2d, k: usize) -> Vec<Point2d> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search within a given `radius` from `point`.
|
/// Search within a given `radius` from `point`.
|
||||||
pub fn radius(&self, point: &Point2d, radius: f64) -> Option<Vec<Point2d>> {
|
pub fn radius(&self, point: &Point2d, radius: f64) -> Vec<Point2d> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search an axis-aligned bounding box whose sides are `span` away from the given point.
|
/// Search an axis-aligned bounding box whose sides are `span` away from the given point.
|
||||||
pub fn aabb(&self, point: &Point2d, span: f64) -> Option<Vec<Point2d>> {
|
pub fn aabb(&self, point: &Point2d, span: f64) -> Vec<Point2d> {
|
||||||
todo!()
|
let span = span / 2.0;
|
||||||
|
|
||||||
|
let mut nxs = HashMap::new();
|
||||||
|
let mut nys = HashMap::new();
|
||||||
|
|
||||||
|
thread::scope(|s| {
|
||||||
|
// x axis
|
||||||
|
s.spawn(|| {
|
||||||
|
let xs = self.xs.read().unwrap();
|
||||||
|
let p = PVal::new(0, point.x);
|
||||||
|
let nx = match xs.binary_search(&p) {
|
||||||
|
Ok(i) | Err(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut i = nx;
|
||||||
|
// first walk backwards
|
||||||
|
loop {
|
||||||
|
let p = xs[i];
|
||||||
|
// `p.val` is less than `point.x` because the xs are sorted
|
||||||
|
let dist = point.x - p.val();
|
||||||
|
if dist <= span {
|
||||||
|
nxs.insert(p.point_index(), p.val());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now forwards
|
||||||
|
i = nx;
|
||||||
|
while i < xs.len() {
|
||||||
|
let p = xs[i];
|
||||||
|
let dist = p.val() - point.x;
|
||||||
|
if dist <= span {
|
||||||
|
nxs.insert(p.point_index(), p.val());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// y axis
|
||||||
|
s.spawn(|| {
|
||||||
|
let ys = self.ys.read().unwrap();
|
||||||
|
let p = PVal::new(0, point.y);
|
||||||
|
let ny = match ys.binary_search(&p) {
|
||||||
|
Ok(i) | Err(i) => i,
|
||||||
|
};
|
||||||
|
let mut i = ny;
|
||||||
|
// backwards
|
||||||
|
loop {
|
||||||
|
let p = ys[i];
|
||||||
|
// `p.val` is less than `point.y` because the ys are sorted
|
||||||
|
let dist = point.y - p.val();
|
||||||
|
if dist <= span {
|
||||||
|
nys.insert(p.point_index(), p.val());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// forwards
|
||||||
|
i = ny;
|
||||||
|
while i < ys.len() {
|
||||||
|
let p = ys[i];
|
||||||
|
let dist = p.val() - point.y;
|
||||||
|
if dist <= span {
|
||||||
|
nys.insert(p.point_index(), p.val());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut neighbors = Vec::with_capacity(nxs.len());
|
||||||
|
for (id, x) in nxs.into_iter() {
|
||||||
|
if let Some(&y) = nys.get(&id) {
|
||||||
|
neighbors.push(Point2d::new(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
neighbors
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nearest(&self, point: &Point2d) -> Option<Point2d> {
|
pub fn nearest(&self, point: &Point2d) -> Option<Point2d> {
|
||||||
let x = point.x;
|
|
||||||
let y = point.y;
|
|
||||||
|
|
||||||
let xs = self.xs.read().unwrap();
|
|
||||||
let ys = self.ys.read().unwrap();
|
|
||||||
|
|
||||||
let len = xs.len();
|
|
||||||
let horizon = self.horizon;
|
let horizon = self.horizon;
|
||||||
let mut nxs = HashMap::with_capacity(horizon * 2);
|
let mut nxs = HashMap::with_capacity(horizon * 2);
|
||||||
let mut nys = HashMap::with_capacity(horizon * 2);
|
let mut nys = HashMap::with_capacity(horizon * 2);
|
||||||
thread::scope(|s| {
|
thread::scope(|s| {
|
||||||
s.spawn(|| {
|
s.spawn(|| {
|
||||||
let p = PVal::new(0, x);
|
let xs = self.xs.read().unwrap();
|
||||||
|
let p = PVal::new(0, point.x);
|
||||||
let nx = match xs.binary_search(&p) {
|
let nx = match xs.binary_search(&p) {
|
||||||
Ok(i) | Err(i) => i,
|
Ok(i) | Err(i) => i,
|
||||||
};
|
};
|
||||||
let start = nx.saturating_sub(horizon);
|
let start = nx.saturating_sub(horizon);
|
||||||
let end = (nx + horizon).min(len);
|
let end = (nx + horizon).min(xs.len());
|
||||||
for p in xs[start..end].iter() {
|
for p in xs[start..end].iter() {
|
||||||
nxs.insert(p.point_index(), p.val());
|
nxs.insert(p.point_index(), p.val());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
s.spawn(|| {
|
s.spawn(|| {
|
||||||
let p = PVal::new(0, y);
|
let ys = self.ys.read().unwrap();
|
||||||
|
let p = PVal::new(0, point.y);
|
||||||
let ny = match ys.binary_search(&p) {
|
let ny = match ys.binary_search(&p) {
|
||||||
Ok(i) | Err(i) => i,
|
Ok(i) | Err(i) => i,
|
||||||
};
|
};
|
||||||
let start = ny.saturating_sub(horizon);
|
let start = ny.saturating_sub(horizon);
|
||||||
let end = (ny + horizon).min(len);
|
let end = (ny + horizon).min(ys.len());
|
||||||
for p in ys[start..end].iter() {
|
for p in ys[start..end].iter() {
|
||||||
nys.insert(p.point_index(), p.val());
|
nys.insert(p.point_index(), p.val());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let mut points = Vec::with_capacity(horizon * 2);
|
|
||||||
|
let mut points = Vec::with_capacity(nxs.len());
|
||||||
|
|
||||||
for (id, xval) in nxs.into_iter() {
|
for (id, xval) in nxs.into_iter() {
|
||||||
if let Some(&yval) = nys.get(&id) {
|
if let Some(&yval) = nys.get(&id) {
|
||||||
points.push(Point2d::new(xval, yval));
|
points.push(Point2d::new(xval, yval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if points.is_empty() {
|
|
||||||
None
|
points.sort_unstable_by(|a, b| {
|
||||||
} else {
|
let adist = a.distance_squared(point);
|
||||||
points.sort_unstable_by(|a, b| {
|
let bdist = b.distance_squared(point);
|
||||||
let adist = a.distance_squared(point);
|
adist.total_cmp(&bdist)
|
||||||
let bdist = b.distance_squared(point);
|
});
|
||||||
adist.total_cmp(&bdist)
|
|
||||||
});
|
points.first().cloned()
|
||||||
points.first().cloned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn points(&self) -> Vec<Point2d> {
|
pub fn points(&self) -> Vec<Point2d> {
|
||||||
|
|
Loading…
Reference in a new issue